commit 3ca522ca1d95553a10156c97dad62c5ebfa460bf Author: jndaniels Date: Mon Oct 28 19:16:06 2024 -0500 INIT diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6a3cec3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.wappler +**/.git +**/.svn +node_modules diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..277b791 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +.svn +.env +**/.DS_Store \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..a21347f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +scripts-prepend-node-path=true \ No newline at end of file diff --git a/.wappler/project.json b/.wappler/project.json new file mode 100644 index 0000000..e8729f5 --- /dev/null +++ b/.wappler/project.json @@ -0,0 +1,52 @@ +{ + "projectName": "ERTFastFiller", + "styleFile": "/css/style.css", + "assetsFolder": "/assets", + "designFramework": "bootstrap5", + "frameworks": [ + { + "name": "fontawesome_5", + "type": "cdn" + }, + { + "name": "bootstrap5", + "type": "local" + }, + { + "name": "appConnect", + "type": "local" + } + ], + "hostingType": "docker", + "projectServerModel": "node", + "runtime": "capacitor", + "webRootFolder": "/public", + "useRouting": true, + "addBase": true, + "routingHandler": "node", + "projectLinksType": "site", + "targets": [ + { + "name": "Development", + "remoteURL": "http://localhost:8100", + "webServerPort": 8100, + "webServerLang": "node", + "targetType": "docker", + "webServer": "node", + "NodeVersion": "lts", + "NodeOS": "alpine", + "NodeImageType": "slim", + "webLoggingMaxFiles": "5", + "webLoggingMaxFileSize": "10m", + "databaseLoggingMaxFiles": "5", + "databaseLoggingMaxFileSize": "10m" + } + ], + "activeTarget": "Development", + "projectType": "web", + "extensions": [ + { + "name": "pdf-lib" + } + ] +} \ No newline at end of file diff --git a/.wappler/targets/Development/docker-compose.yml b/.wappler/targets/Development/docker-compose.yml new file mode 100644 index 0000000..feac4bb --- /dev/null +++ b/.wappler/targets/Development/docker-compose.yml @@ -0,0 +1,22 @@ +services: + web: + volumes: + - '../../../app:/opt/node_app/app' + - '../../../lib:/opt/node_app/lib' + - '../../../views:/opt/node_app/views' + - '../../../public:/opt/node_app/public' + - '../../../extensions:/opt/node_app/extensions' + - '../../../db:/opt/node_app/db' + - '../../../certs:/opt/node_app/certs' + ports: + - '8100:3000' + restart: 'always' + stdin_open: true + tty: true + build: + context: '../../../' + dockerfile: '.wappler/targets/Development/web/Dockerfile' + logging: + options: + max-file: '5' + max-size: '10m' diff --git a/.wappler/targets/Development/web/Dockerfile b/.wappler/targets/Development/web/Dockerfile new file mode 100644 index 0000000..c80c936 --- /dev/null +++ b/.wappler/targets/Development/web/Dockerfile @@ -0,0 +1,16 @@ +FROM node:lts-alpine + +ARG NODE_ENV=production +ENV NODE_ENV $NODE_ENV + +ARG PORT=3000 +ENV PORT $PORT +EXPOSE $PORT + +ENV PATH /opt/node_app/node_modules/.bin:$PATH +ENV NODE_ENV development +WORKDIR /opt/node_app +COPY index.js . +COPY package.json . +RUN npm install --no-optional --no-package-lock +CMD [ "nodemon", "--polling-interval", "5000", "--legacy-watch", "./index.js" ] diff --git a/.wappler/thumb.png b/.wappler/thumb.png new file mode 100644 index 0000000..287b22d Binary files /dev/null and b/.wappler/thumb.png differ diff --git a/app/config/routes.json b/app/config/routes.json new file mode 100644 index 0000000..215bf5f --- /dev/null +++ b/app/config/routes.json @@ -0,0 +1,10 @@ +{ + "routes": [ + { + "path": "/", + "page": "index", + "routeType": "page", + "layout": "main" + } + ] +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..e3c0683 --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +const server = require('./lib/server'); + +server.start(); \ No newline at end of file diff --git a/lib/auth/database.js b/lib/auth/database.js new file mode 100644 index 0000000..5492455 --- /dev/null +++ b/lib/auth/database.js @@ -0,0 +1,72 @@ +const AuthProvider = require('./provider'); + +class DatabaseProvider extends AuthProvider { + + constructor(app, opts, name) { + super(app, opts, name); + this.users = opts.users; + this.perms = opts.permissions; + this.db = app.getDbConnection(opts.connection); + } + + async validate(username, password, passwordVerify) { + if (!username) return false; + if (!password) password = ''; + + let results = await this.db + .select(this.users.identity, this.users.username, this.users.password) + .from(this.users.table) + .where(this.users.username, username); + + for (let result of results) { + if (result[this.users.username] == username) { + if (passwordVerify && result[this.users.password].startsWith('$')) { + const argon2 = require('argon2'); + const valid = await argon2.verify(result[this.users.password], password); + return valid ? result[this.users.identity] : false; + } else if (result[this.users.password] == password) { + return result[this.users.identity]; + } + } + } + + return false; + } + + async permissions(identity, permissions) { + for (let permission of permissions) { + if (!this.perms[permission]) return false; + + let perm = this.perms[permission]; + let table = perm.table || this.users.table; + let ident = perm.identity || this.users.identity; + + let results = await this.db + .select(ident) + .from(table) + .where(ident, identity) + .where(function() { + for (let condition of perm.conditions) { + if (condition.operator == 'in') { + this.whereIn(condition.column, condition.value); + } else if (condition.operator == 'not in') { + this.whereNotIn(condition.column, condition.value); + } else if (condition.operator == 'is null') { + this.whereNull(condition.column); + } else if (condition.operator == 'is not null') { + this.whereNotNull(condition.column); + } else { + this.where(condition.column, condition.operator, condition.value); + } + } + }); + + if (!results.length) return false; + } + + return true; + } + +} + +module.exports = DatabaseProvider; \ No newline at end of file diff --git a/lib/auth/passport.js b/lib/auth/passport.js new file mode 100644 index 0000000..7c6ef95 --- /dev/null +++ b/lib/auth/passport.js @@ -0,0 +1,25 @@ +const PassportStrategy = require('passport-strategy'); + +class ServerConnectStrategy extends PassportStrategy { + constructor (options) { + const { provider } = options; + + if (!provider) { throw new TypeError('ServerConnectStrategy requires a provider'); } + + super(); + + this.name = 'server-connect'; + this._key = provider + 'Id'; + } + + authenticate (req, options = {}) { + if (req.session && req.session[this._key]) { + const property = req._userProperty || 'user'; + req[property] = { id: req.session[this._key] }; + } + + this.pass(); + } +} + +module.exports = ServerConnectStrategy; \ No newline at end of file diff --git a/lib/auth/provider.js b/lib/auth/provider.js new file mode 100644 index 0000000..da5b97d --- /dev/null +++ b/lib/auth/provider.js @@ -0,0 +1,159 @@ +const config = require('../setup/config'); +const crypto = require('crypto'); +const debug = require('debug')('server-connect:auth'); + +class AuthProvider { + + constructor(app, opts, name) { + this.app = app; + this.name = name; + this.identity = this.app.getSession(this.name + 'Id') || false; + this.secret = opts.secret || config.secret; + this.basicAuth = opts.basicAuth; + this.basicRealm = opts.basicRealm; + this.passwordVerify = opts.passwordVerify || false; + + this.cookieOpts = { + domain: opts.domain || undefined, + httpOnly: true, + maxAge: (opts.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: opts.path || '/', + secure: !!opts.secure, + sameSite: opts.sameSite || 'Strict', + signed: true + }; + } + + async autoLogin() { + if (this.basicAuth) { + const auth = require('../core/basicauth')(this.app.req); + debug(`Basic auth credentials received: %o`, auth); + if (auth) await this.login(auth.username, auth.password, false, true); + } + + const cookie = this.app.getCookie(this.name + '.auth', true); + if (cookie) { + const auth = this.decrypt(cookie); + debug(`Login with cookie: %o`, auth); + if (auth) await this.login(auth.username, auth.password, true, true); + } else { + debug(`No login cookie found`); + } + } + + async impersonate(identity) { + this.app.setSession(this.name + 'Id', identity); + this.app.set('identity', identity); + this.identity = identity; + await this.app.regenerateSessionId(); + } + + async login(username, password, remember, autoLogin) { + const identity = await this.validate(username, password, this.passwordVerify); + + if (!identity) { + await this.logout(); + + if (!autoLogin) { + this.unauthorized(); + return false; + } + } else { + this.app.setSession(this.name + 'Id', identity); + this.app.set('identity', identity); + + if (remember) { + debug('setCookie', identity, username, password); + this.app.setCookie(this.name + '.auth', this.encrypt({ username, password }), this.cookieOpts); + } + + this.identity = identity; + } + + await this.app.regenerateSessionId(); + + return identity; + } + + async logout() { + this.app.removeSession(this.name + 'Id'); + this.app.removeCookie(this.name + '.auth', this.cookieOpts); + this.app.remove('identity'); + this.identity = false; + await this.app.regenerateSessionId(); + } + + async restrict(opts) { + if (this.identity === false) { + if (opts.loginUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.loginUrl); + } else { + this.app.res.redirect(opts.loginUrl); + } + } else { + this.unauthorized(); + } + + return; + } + + if (opts.permissions) { + const allowed = await this.permissions(this.identity, opts.permissions); + if (!allowed) { + if (opts.forbiddenUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.forbiddenUrl); + } else { + this.app.res.redirect(opts.forbiddenUrl); + } + } else { + this.forbidden(); + } + } + } + } + + encrypt(data) { + const iv = crypto.randomBytes(16); + const key = crypto.scryptSync(this.secret, iv, 32); + const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64'); + return iv.toString('base64') + '.' + encrypted + cipher.final('base64'); + } + + decrypt(data) { + // try/catch to prevent errors on currupt cookies + try { + const iv = Buffer.from(data.split('.')[0], 'base64'); + const key = crypto.scryptSync(this.secret, iv, 32); + const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + const decrypted = decipher.update(data.split('.')[1], 'base64', 'utf8'); + return JSON.parse(decrypted + decipher.final('utf8')); + } catch (err) { + return undefined; + } + } + + unauthorized() { + if (this.basicAuth) { + this.app.res.set('WWW-Authenticate', `Basic Realm="${this.basicRealm}"`); + } + this.app.res.sendStatus(401); + } + + forbidden() { + this.app.res.sendStatus(403); + } + + async validate(username, password) { + throw new Error('auth.validate needs to be extended.'); + } + + async permissions(identity, permissions) { + throw new Error('auth.permissions needs to be extended.'); + } + +} + +module.exports = AuthProvider; \ No newline at end of file diff --git a/lib/auth/provider_experimental.js b/lib/auth/provider_experimental.js new file mode 100644 index 0000000..a852ce6 --- /dev/null +++ b/lib/auth/provider_experimental.js @@ -0,0 +1,181 @@ +const config = require('../setup/config'); +const crypto = require('crypto'); +const debug = require('debug')('server-connect:auth'); + +class AuthProvider { + + constructor(app, opts, name) { + this.app = app; + this.name = name; + this.identity = options.jwt ? false : this.app.getSession(this.name + 'Id') || false; + this.jwt = options.jwt || false; + this.jwtExpires = options.jwtExpires || '1h'; + this.jwtIssuer = options.jwtIssuer || 'serverconnect'; + this.secret = opts.secret || config.secret; + this.basicAuth = opts.basicAuth; + this.basicRealm = opts.basicRealm; + this.passwordVerify = opts.passwordVerify || false; + + this.cookieOpts = { + domain: opts.domain || undefined, + httpOnly: true, + maxAge: (opts.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: opts.path || '/', + secure: !!opts.secure, + sameSite: opts.sameSite || 'Strict', + signed: true + }; + } + + async autoLogin() { + if (this.basicAuth) { + const auth = require('../core/basicauth')(this.app.req); + debug(`Basic auth credentials received: %o`, auth); + if (auth) await this.login(auth.username, auth.password, false, true); + } + + if (this.jwt && this.app.req.headers['authorization']) { + const matches = /(\S+)\s+(\S+)/; + + if (matches && matches[1].toLowerCase == 'bearer') { + const jwt = require('jsonwebtoken'); + const { identity } = jwt.verify(matches[2], this.secret, { + algorithm: 'HS256', + issuer: this.jwtIssuer, + }); + + this.app.set('identity', identity); + this.identity = identity; + } + } + + const cookie = this.app.getCookie(this.name + '.auth', true); + if (cookie) { + const auth = this.decrypt(cookie); + debug(`Login with cookie: %o`, auth); + if (auth) await this.login(auth.username, auth.password, true, true); + } else { + debug(`No login cookie found`); + } + } + + async login(username, password, remember, autoLogin) { + const identity = await this.validate(username, password, this.passwordVerify); + + if (!identity) { + await this.logout(); + + if (!autoLogin) { + this.unauthorized(); + return false; + } + } else { + this.app.set('identity', identity); + + if (!this.jwt) { + this.app.setSession(this.name + 'Id', identity); + + if (remember) { + debug('setCookie', identity, username, password); + this.app.setCookie(this.name + '.auth', this.encrypt({ username, password }), this.cookieOpts); + } + } + + this.identity = identity; + } + + if (this.jwt) { + const jwt = require('jsonwebtoken'); + return jwt.sign({ identity }, this.secret, { + algorithm: 'HS256', + expiresIn: this.jwtExpires, + issuer: this.jwtIssuer, + }); + } + + return identity; + } + + async logout() { + if (!this.jwt) { + this.app.removeSession(this.name + 'Id'); + this.app.removeCookie(this.name + '.auth', this.cookieOpts); + } + this.app.remove('identity'); + this.identity = false; + } + + async restrict(opts) { + if (this.identity === false) { + if (opts.loginUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.loginUrl); + } else { + this.app.res.redirect(opts.loginUrl); + } + } else { + this.unauthorized(); + } + + return; + } + + if (opts.permissions) { + const allowed = await this.permissions(this.identity, opts.permissions); + if (!allowed) { + if (opts.forbiddenUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.forbiddenUrl); + } else { + this.app.res.redirect(opts.forbiddenUrl); + } + } else { + this.forbidden(); + } + } + } + } + + encrypt(data) { + const iv = crypto.randomBytes(16); + const key = crypto.scryptSync(this.secret, iv, 32); + const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64'); + return iv.toString('base64') + '.' + encrypted + cipher.final('base64'); + } + + decrypt(data) { + // try/catch to prevent errors on currupt cookies + try { + const iv = Buffer.from(data.split('.')[0], 'base64'); + const key = crypto.scryptSync(this.secret, iv, 32); + const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + const decrypted = decipher.update(data.split('.')[1], 'base64', 'utf8'); + return JSON.parse(decrypted + decipher.final('utf8')); + } catch (err) { + return undefined; + } + } + + unauthorized() { + if (this.basicAuth) { + this.app.res.set('WWW-Authenticate', `Basic Realm="${this.basicRealm}"`); + } + this.app.res.sendStatus(401); + } + + forbidden() { + this.app.res.sendStatus(403); + } + + async validate(username, password) { + throw new Error('auth.validate needs to be extended.'); + } + + async permissions(identity, permissions) { + throw new Error('auth.permissions needs to be extended.'); + } + +} + +module.exports = AuthProvider; \ No newline at end of file diff --git a/lib/auth/single.js b/lib/auth/single.js new file mode 100644 index 0000000..5b38e64 --- /dev/null +++ b/lib/auth/single.js @@ -0,0 +1,26 @@ +const AuthProvider = require('./provider'); +const debug = require('debug')('server-connect:auth'); + +class SingleProvider extends AuthProvider { + + constructor(app, opts, name) { + super(app, opts, name); + this.username = opts.username; + this.password = opts.password; + } + + validate(username, password) { + if (username == this.username && password == this.password) { + return username; + } + + return false; + } + + permissions() { + return true; + } + +} + +module.exports = SingleProvider; \ No newline at end of file diff --git a/lib/auth/static.js b/lib/auth/static.js new file mode 100644 index 0000000..ce1113f --- /dev/null +++ b/lib/auth/static.js @@ -0,0 +1,31 @@ +const AuthProvider = require('./provider'); + +class StaticProvider extends AuthProvider { + + constructor(app, opts, name) { + super(app, opts, name); + this.users = opts.users; + this.perms = opts.perms; + } + + validate(username, password) { + if (this.users[username] == password) { + return username; + } + + return false; + } + + permissions(username, permissions) { + for (let permission of permissions) { + if (!this.perms[permission] || !this.perms[permission].includes(username)) { + return false; + } + } + + return true; + } + +} + +module.exports = StaticProvider; \ No newline at end of file diff --git a/lib/core/app.js b/lib/core/app.js new file mode 100644 index 0000000..27d08f1 --- /dev/null +++ b/lib/core/app.js @@ -0,0 +1,840 @@ +const fs = require('fs-extra'); +const Scope = require('./scope'); +const Parser = require('./parser'); +const db = require('./db'); +const validator = require('../validator'); +const config = require('../setup/config'); +const debug = require('debug')('server-connect:app'); +const debug2 = require('debug')('server-connect:output'); +const { clone, formatDate, parseDate } = require('../core/util'); +const { toSystemPath } = require('../core/path'); +const os = require('os'); + +if (!global.fileCache) { + global.fileCache = new Map(); +} + +if (!global.db) { + global.db = {}; +} + +if (!global.rateLimiter) { + global.rateLimiter = {}; +} + +function App(req = {}, res = {}) { + this.error = false; + this.data = {}; + this.meta = {}; + this.settings = {}; + this.modules = {}; + + this.io = global.io; + this.req = req; + this.res = res; + this.global = new Scope(); + this.scope = this.global; + + this.rateLimiter = {}; + this.mail = {}; + this.auth = {}; + this.oauth = {}; + this.db = {}; + this.s3 = {}; + this.jwt = {}; + this.trx = {}; + + if (config.globals.data) { + this.set(config.globals.data); + } + + this.set({ + $_ERROR: null, + //$_SERVER: process.env, + $_ENV: process.env, + $_GET: req.query, + $_POST: req.body, + $_PARAM: req.params, + $_HEADER: req.headers, + $_COOKIE: req.cookies, + $_SESSION: req.session, + }); + + let urlParts = req.originalUrl ? req.originalUrl.split('?') : ['', '']; + let $server = { + CONTENT_TYPE: req.headers && req.headers['content-type'], + HTTPS: req.protocol == 'https', + PATH_INFO: req.path, + QUERY_STRING: urlParts[1], + REMOTE_ADDR: req.ip, + REQUEST_PROTOCOL: req.protocol, + REQUEST_METHOD: req.method, + SERVER_NAME: req.hostname, + BASE_URL: req.headers && req.protocol + '://' + req.headers['host'], + URL: urlParts[0], + HOSTNAME: os.hostname(), + }; + + if (req.headers) { + for (let header in req.headers) { + $server['HTTP_' + header.toUpperCase().replace(/-/, '_')] = req.headers[header]; + } + } + + // Try to keep same as ASP/PHP + this.set('$_SERVER', $server); +} + +App.prototype = { + set: function (key, value) { + this.global.set(key, value); + }, + + get: function (key, def) { + let value = this.global.get(key); + return value !== undefined ? value : def; + }, + + remove: function (key) { + this.global.remove(key); + }, + + setSession: function (key, value) { + this.req.session[key] = value; + }, + + getSession: function (key) { + return this.req.session[key]; + }, + + removeSession: function (key) { + delete this.req.session[key]; + }, + + regenerateSessionId: function () { + return new Promise((resolve, reject) => { + const oldSession = this.req.session; + this.req.session.regenerate((err) => { + if (err) return reject(err); + + for (let key in oldSession) { + this.req.session[key] = oldSession[key]; + } + + resolve(); + }); + }); + }, + + setCookie: function (name, value, opts) { + if (!this.res.headersSent) { + if (this.res.cookie) { + this.res.cookie(name, value, opts); + } + } else { + debug(`Trying to set ${name} cookie while headers were already sent.`); + } + }, + + getCookie: function (name, signed) { + return signed ? this.req.signedCookies[name] : this.req.cookies[name]; + }, + + removeCookie: function (name, opts) { + if (this.res.clearCookie) { + // copy all options except expires and maxAge + const clearOpts = Object.assign({}, opts); + delete clearOpts.expires; + delete clearOpts.maxAge; + this.res.clearCookie(name, clearOpts); + } + }, + + setRateLimiter: function (name, options) { + const { RateLimiterMemory, RateLimiterRedis } = require('rate-limiter-flexible'); + + if (global.redisClient) { + global.rateLimiter[name] = new RateLimiterRedis({ + ...options, + storeClient: global.redisClient, + keyPrefix: 'ac:' + name + ':', + }); + } else { + global.rateLimiter[name] = new RateLimiterMemory({ + ...options, + keyPrefix: 'ac:' + name + ':', + }); + } + + return global.rateLimiter[name]; + }, + + getRateLimiter: function (name) { + if (global.rateLimiter[name]) { + return global.rateLimiter[name]; + } + + if (config.rateLimiter[name]) { + return this.setRateLimiter(name, config.rateLimiter[name]); + } + + if (fs.existsSync(`app/modules/RateLimiter/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/RateLimiter/${name}.json`); + return this.setRateLimiter(name, action.options); + } + + throw new Error(`Couldn't find rate limiter "${name}".`); + }, + + setMailer: function (name, options) { + let setup = {}; + + options = this.parse(options); + + switch (options.server) { + case 'mail': + setup.sendmail = true; + break; + + case 'ses': + const aws = require('@aws-sdk/client-ses'); + const ses = new AWS.SES({ + endpoint: options.endpoint, + credentials: { + accessKeyId: options.accessKeyId, + secretAccessKey: options.secretAccessKey, + }, + }); + setup.SES = { ses, aws }; + break; + + default: + // https://nodemailer.com/smtp/ + setup.host = options.host || 'localhost'; + setup.port = options.port || 25; + setup.secure = options.useSSL || false; + setup.auth = { + user: options.username, + pass: options.password, + }; + setup.tls = { + rejectUnauthorized: false, + }; + } + + return (this.mail[name] = setup); + }, + + getMailer: function (name) { + if (this.mail[name]) { + return this.mail[name]; + } + + if (config.mail[name]) { + return this.setMailer(name, config.mail[name]); + } + + if (fs.existsSync(`app/modules/Mailer/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/Mailer/${name}.json`); + return this.setMailer(name, action.options); + } + + throw new Error(`Couldn't find mailer "${name}".`); + }, + + setAuthProvider: async function (name, options) { + const Provider = require('../auth/' + options.provider.toLowerCase()); + const provider = new Provider(this, options, name); + if (!provider.identity) await provider.autoLogin(); + this.set('identity', provider.identity); + return (this.auth[name] = provider); + }, + + getAuthProvider: async function (name) { + if (this.auth[name]) { + return this.auth[name]; + } + + if (config.auth[name]) { + return await this.setAuthProvider(name, config.auth[name]); + } + + if (fs.existsSync(`app/modules/SecurityProviders/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/SecurityProviders/${name}.json`); + return await this.setAuthProvider(name, action.options); + } + + throw new Error(`Couldn't find security provider "${name}".`); + }, + + setOAuthProvider: async function (name, options) { + const OAuth2 = require('../oauth'); + const services = require('../oauth/services'); + + options = this.parse(options); + + let service = this.parseOptional(options.service, 'string', null); + let opts = service ? services[service] : {}; + opts.client_id = this.parseOptional(options.client_id, 'string', null); + opts.client_secret = this.parseOptional(options.client_secret, 'string', null); + opts.token_endpoint = + opts.token_endpoint || + this.parseRequired(options.token_endpoint, 'string', 'oauth.provider: token_endpoint is required.'); + opts.auth_endpoint = opts.auth_endpoint || this.parseOptional(options.auth_endpoint, 'string', ''); + opts.scope_separator = opts.scope_separator || this.parseOptional(options.scope_separator, 'string', ' '); + opts.access_token = this.parseOptional(options.access_token, 'string', null); + opts.refresh_token = this.parseOptional(options.refresh_token, 'string', null); + opts.jwt_bearer = this.parseOptional(options.jwt_bearer, 'string', false); + opts.client_credentials = this.parseOptional(options.client_credentials, 'boolean', false); + opts.params = Object.assign({}, opts.params, this.parseOptional(options.params, 'object', {})); + + this.oauth[name] = new OAuth2(this, this.parse(options), name); + await this.oauth[name].init(); + + return this.oauth[name]; + }, + + getOAuthProvider: async function (name) { + if (this.oauth[name]) { + return this.oauth[name]; + } + + if (config.oauth[name]) { + return await this.setOAuthProvider(name, config.oauth[name]); + } + + if (fs.existsSync(`app/modules/oauth/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/oauth/${name}.json`); + return await this.setOAuthProvider(name, action.options); + } + + throw new Error(`Couldn't find oauth provider "${name}".`); + }, + + setDbConnection: function (name, options) { + if (global.db[name]) { + return global.db[name]; + } + + options = this.parse(options); + + switch (options.client) { + case 'couchdb': + const { host, port, user, password, database } = options.connection; + const nano = require('nano'); + global.db[name] = nano( + `http://${user ? user + (password ? ':' + password : '') + '@' : ''}${host}${ + port ? ':' + port : '' + }/${database}` + ); + global.db[name].client = options.client; + return global.db[name]; + + case 'sqlite3': + if (options.connection.filename) { + options.connection.filename = toSystemPath(options.connection.filename); + } + break; + + case 'mysql': + case 'mysql2': + options.connection = { + supportBigNumbers: true, + dateStrings: !!options.tz, + decimalNumbers: true, + ...options.connection, + }; + /* + options.connection.typeCast = function(field, next) { + if (field.type == 'NEWDECIMAL') { + return field.string() + 'm'; + } + + return next(); + }; + */ + break; + + case 'mssql': + if (options.tz) { + // use local timezone to have same behavior as the other drivers + // to prevent problems node should have same timezone as database + options.connection.options = { useUTC: false, ...options.connection.options }; + } + break; + + case 'postgres': + if (options.tz) { + const types = require('pg').types; + const parseFn = (val) => val; + types.setTypeParser(types.builtins.TIME, parseFn); + types.setTypeParser(types.builtins.TIMETZ, parseFn); + types.setTypeParser(types.builtins.TIMESTAMP, parseFn); + types.setTypeParser(types.builtins.TIMESTAMPTZ, parseFn); + } + break; + } + + if (options.connection && options.connection.ssl) { + if (options.connection.ssl.key) { + options.connection.ssl.key = fs.readFileSync(toSystemPath(options.connection.ssl.key)); + } + + if (options.connection.ssl.ca) { + options.connection.ssl.ca = fs.readFileSync(toSystemPath(options.connection.ssl.ca)); + } + + if (options.connection.ssl.cert) { + options.connection.ssl.cert = fs.readFileSync(toSystemPath(options.connection.ssl.cert)); + } + } + + options.useNullAsDefault = true; + + const formatRecord = (record, meta) => { + for (column in record) { + if (record[column] != null) { + if (meta.has(column)) { + const info = meta.get(column); + + if (['json', 'object', 'array'].includes(info.type)) { + if (typeof record[column] == 'string') { + try { + // column of type json returned as string, need parse + record[column] = JSON.parse(record[column]); + } catch (err) { + console.warn(err); + } + } + } + + if (info.type == 'date') { + if (typeof record[column] == 'string' && record[column].startsWith('0000-00-00')) { + record[column] = undefined; + } else { + record[column] = formatDate(record[column], 'yyyy-MM-dd'); + } + } + + if (info.type == 'time') { + if (options.tz == 'local') { + record[column] = formatDate(record[column], 'HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate( + parseDate(formatDate('now', 'yyyy-MM-dd ') + formatDate(record[column], 'HH:mm:ss.v')) + ) + .toISOString() + .slice(11); + } + } + + if ( + ['datetime', 'timestamp'].includes(info.type) || + Object.prototype.toString.call(record[column]) == '[object Date]' + ) { + if (typeof record[column] == 'string' && record[column].startsWith('0000-00-00')) { + record[column] = undefined; + } else if (options.tz == 'local') { + record[column] = formatDate(record[column], 'yyyy-MM-dd HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate(record[column]).toISOString(); + } + } + } else { + // try to detect datetime + if (Object.prototype.toString.call(record[column]) == '[object Date]') { + if (options.tz == 'local') { + record[column] = formatDate(record[column], 'yyyy-MM-dd HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate(record[column]).toISOString(); + } + } else if ( + typeof record[column] == 'string' && + /^\d{4}-\d{2}-\d{2}([T\s]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}(:\d{2})?)?)?$/.test(record[column]) + ) { + if (record[column].startsWith('0000-00-00')) { + record[column] = undefined; + } else if (options.tz == 'local') { + record[column] = formatDate(record[column], 'yyyy-MM-dd HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate(record[column]).toISOString(); + } + } + } + + if (record[column] == undefined) { + record[column] = null; + } else if (record[column].toJSON) { + record[column] = record[column].toJSON(); + } + } + } + + return record; + }; + + options.postProcessResponse = function (result, queryContext) { + const meta = new Map(); + + if (Array.isArray(queryContext)) { + for (let item of queryContext) { + if (item.name && item.type) { + meta.set(item.name, item); + } + } + } + + if (Array.isArray(result)) { + return result.map((record) => formatRecord(record, meta)); + } else { + return formatRecord(result, meta); + } + }; + + global.db[name] = db(options); + + return global.db[name]; + }, + + getDbConnection: function (name) { + if (config.db[name]) { + return this.setDbConnection(name, config.db[name]); + } + + const path = `app/modules/connections/${name}.json`; + const action = global.fileCache.get(path) || fs.readJSONSync(`app/modules/connections/${name}.json`); + const isDynamic = this.isDynamic(action); + const options = clone(action.options); // need to clone because this.parse will update object by ref + + if (!global.fileCache.has(path)) { + // if action was not already cached do it now + global.fileCache.set(path, action); + } + + if (isDynamic) { + // since it is only used internal json stringify is good enough for creating a unique name + name = JSON.stringify(this.parse(options)); + } + + if (this.trx[name]) return this.trx[name]; + + return this.setDbConnection(name, options); + }, + + setS3Provider: function (name, options) { + options = this.parse(options); + + const { S3 } = require('@aws-sdk/client-s3'); + const endpoint = this.parseRequired(options.endpoint, 'string', 's3.provider: endpoint is required.'); + const accessKeyId = this.parseRequired(options.accessKeyId, 'string', 's3.provider: accessKeyId is required.'); + const secretAccessKey = this.parseRequired( + options.secretAccessKey, + 'string', + 's3.provider: secretAccessKey is required.' + ); + + let region = options.region || 'us-east-1'; + let pos = endpoint.indexOf('.amazonaws'); + if (pos > 3) region = endpoint.substr(3, pos - 3); + let forcePathStyle = options.forcePathStyle || false; + + this.s3[name] = new S3({ + endpoint: 'https://' + endpoint, + credentials: { accessKeyId, secretAccessKey }, + region, + signatureVersion: 'v4', + forcePathStyle, + }); + + return this.s3[name]; + }, + + getS3Provider: function (name) { + if (this.s3[name]) { + return this.s3[name]; + } + + if (config.s3[name]) { + return this.setS3Provider(name, config.s3[name]); + } + + if (fs.existsSync(`app/modules/s3/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/s3/${name}.json`); + return this.setS3Provider(name, action.options); + } + + throw new Error(`Couldn't find S3 provider "${name}".`); + }, + + setJSONWebToken: function (name, options) { + const jwt = require('jsonwebtoken'); + + options = this.parse(options); + + let opts = {}; + + if (options.alg) opts.algorithm = options.alg; + if (options.iss) opts.issuer = options.iss; + if (options.sub) opts.subject = options.sub; + if (options.aud) opts.audience = options.aud; + if (options.jti) opts.jwtid = options.jti; + if (options.expiresIn) opts.expiresIn = options.expiresIn; + + return (this.jwt[name] = jwt.sign( + { + ...options.claims, + }, + options.key, + { + expiresIn: 3600, // use as default (required by google) + ...opts, + } + )); + }, + + getJSONWebToken: function (name) { + if (config.jwt[name]) { + return this.setJSONWebToken(name, config.jwt[name]); + } + + if (fs.existsSync(`app/modules/jwt/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/jwt/${name}.json`); + return this.setJSONWebToken(name, action.options); + } + + throw new Error(`Couldn't find JSON Web Token "${name}".`); + }, + + define: async function (cfg, internal) { + if (cfg.settings) { + this.settings = clone(cfg.settings); + } + + if (cfg.vars) { + this.set(clone(cfg.vars)); + } + + if (cfg.meta) { + this.meta = clone(cfg.meta); + await validator.init(this, this.meta); + } + + if (fs.existsSync('app/modules/global.json')) { + await this.exec(await fs.readJSON('app/modules/global.json'), true); + } + + /* + debug('body: %o', this.req.body); + debug('query: %o', this.req.query); + debug('params: %o', this.req.params); + debug('headers: %o', this.req.headers); + debug('cookies: %o', this.req.cookies); + debug('session: %o', this.req.session); + */ + + await this.exec(cfg.exec || cfg, internal); + }, + + sub: async function (actions, scope) { + const subApp = new App(this.req, this.res); + subApp.global = this.global; + subApp.scope = this.scope.create(scope); + await subApp.exec(actions, true); + return subApp.data; + }, + + exec: async function (actions, internal) { + if (actions.exec) { + return this.exec(actions.exec, internal); + } + + actions = clone(actions); + + await this._exec(actions.steps || actions); + + if (this.error !== false) { + if (actions.catch) { + this.scope.set('$_ERROR', this.error.message); + this.error = false; + await this._exec(actions.catch, true); + } else { + throw this.error; + } + } + + if (!internal && !this.res.headersSent && !this.noOutput) { + this.res.json(this.data); + } + }, + + _exec: async function (steps, ignoreAbort) { + if (this.res.headersSent) return; + + if (typeof steps == 'string') { + return this.exec(await fs.readJSON(`app/modules/${steps}.json`), true); + } + + if (this.res.headersSent) { + // do not execute other steps after headers has been sent + return; + } + + if (Array.isArray(steps)) { + for (let step of steps) { + await this._exec(step); + if (this.error) return; + } + return; + } + + if (steps.disabled) { + return; + } + + if (steps.action) { + try { + let module; + + if (fs.existsSync(`extensions/server_connect/modules/${steps.module}.js`)) { + module = require(`../../extensions/server_connect/modules/${steps.module}`); + } else if (fs.existsSync(`lib/modules/${steps.module}.js`)) { + module = require(`../modules/${steps.module}`); + } else { + throw new Error(`Module ${steps.module} doesn't exist`); + } + + if (typeof module[steps.action] != 'function') { + throw new Error(`Action ${steps.action} doesn't exist in ${steps.module || 'core'}`); + } + + debug(`Executing action step ${steps.action}`); + debug(`options: %O`, steps.options); + + if (!ignoreAbort && config.abortOnDisconnect && this.req.isDisconnected) { + throw new Error('Aborted'); + } + + const data = await module[steps.action].call(this, clone(steps.options), steps.name, steps.meta); + + if (data instanceof Error) { + throw data; + } + + debug2(`${steps.name || steps.action}: %O`, data); + + if (steps.name) { + this.scope.set(steps.name, data); + + if (steps.output) { + this.data[steps.name] = data; + } + } + } catch (e) { + debug2(`Error at ${steps.name || steps.action}: %O`, e); + + this.error = e; + return; + } + } + }, + + parse: function (value, scope) { + return Parser.parseValue(value, scope || this.scope); + }, + + parseRequired: function (value, type, err) { + if (value === undefined) { + throw new Error(err); + } + + let val = Parser.parseValue(value, this.scope); + + if (type == '*') { + if (val === undefined) { + throw new Error(err); + } + } else if (type == 'boolean') { + val = !!val; + } else if (typeof val != type) { + throw new Error(err); + } + + return val; + }, + + parseOptional: function (value, type, def) { + if (value === undefined) return def; + + let val = Parser.parseValue(value, this.scope); + + if (type == '*') { + if (val === undefined) val = def; + } else if (type == 'boolean') { + if (val === undefined) { + val = def; + } else { + val = !!val; + } + } else if (typeof val != type) { + val = def; + } + + return val; + }, + + parseSQL: function (sql) { + if (!sql) return null; + + ['values', 'orders'].forEach((prop) => { + if (Array.isArray(sql[prop])) { + sql[prop] = sql[prop].filter((value) => { + if (!value.condition) return true; + return !!Parser.parseValue(value.condition, this.scope); + }); + } + }); + + if (sql.wheres && sql.wheres.rules) { + if (sql.wheres.conditional && !Parser.parseValue(sql.wheres.conditional, this.scope)) { + delete sql.wheres; + } else { + sql.wheres.rules = sql.wheres.rules.filter(function filterConditional(rule) { + if (!rule.rules) return true; + if (rule.conditional && !Parser.parseValue(rule.conditional, this.scope)) return false; + rule.rules = rule.rules.filter(filterConditional, this); + return rule.rules.length; + }, this); + + if (!sql.wheres.rules.length) { + delete sql.wheres; + } + } + } + + if (sql.sub) { + for (const name in sql.sub) { + sql.sub[name] = this.parseSQL(sql.sub[name]); + } + } + + return Parser.parseValue(sql, this.scope); + }, + + isDynamic: function (value) { + if (typeof value == 'string') { + return value.includes('{{'); + } + + if (typeof value == 'object') { + for (const prop in value) { + if (this.isDynamic(value[prop])) { + return true; + } + } + } + + return false; + }, +}; + +module.exports = App; diff --git a/lib/core/async.js b/lib/core/async.js new file mode 100644 index 0000000..d25e20d --- /dev/null +++ b/lib/core/async.js @@ -0,0 +1,97 @@ +function defered() { + let resolve, reject; + let promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + + return { + promise, + resolve, + reject + }; +} + +function limit(concurrency) { + let running = 0; + let queue = []; + + function init() { + if (running < concurrency) { + running++; + return Promise.resolve(); + } + + let defer = defered(); + queue.push(defer); + return defer.promise; + } + + function complete(a) { + let next = queue.shift(); + + if (next) { + next.resolve(); + } else { + running--; + } + + return a; + } + + return function(fn) { + return function() { + let args = Array.prototype.slice.apply(arguments); + + return init().then(() => { + return fn.apply(null, args); + }).finally(complete); + } + } +} + +module.exports = { + + map: function(arr, fn, concurrency) { + arr = Array.isArray(arr) ? arr : [arr]; + + if (concurrency) { + const limiter = limit(concurrency) + return Promise.all(arr.map(limiter(fn))); + } + + return Promise.all(arr.map(fn)); + }, + + mapSeries: function(arr, fn) { + arr = Array.isArray(arr) ? arr : [arr]; + + return arr.reduce((promise, curr, index, arr) => { + return promise.then(prev => { + return fn(curr, index, arr).then(val => { + prev.push(val); + return prev; + }); + }) + }, Promise.resolve([])); + }, + + reduce: function(arr, fn, start) { + arr = Array.isArray(arr) ? arr : [arr]; + + if (!arr.length) { + return Promise.resolve(start); + } + + return arr.reduce((promise, curr, index, arr) => { + return promise.then(prev => { + if (prev === undefined && arr.length === 1) { + return curr; + } + + return fn(prev, curr, index, arr); + }); + }, Promise.resolve(start)); + }, + +}; \ No newline at end of file diff --git a/lib/core/base32.js b/lib/core/base32.js new file mode 100644 index 0000000..81bfde0 --- /dev/null +++ b/lib/core/base32.js @@ -0,0 +1,84 @@ +const Variants = { + 'RFC4648': { + alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', + padding: true, + }, + 'RFC4648-HEX': { + alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUV', + padding: true, + }, + 'CROCKFORD': { + alphabet: '0123456789ABCDEFGHJKMNPQRSTVWXYZ', + padding: false, + }, +}; + +exports.encode = (input, opts = {}) => { + const Variant = Variants[opts.variant] || Variants['RFC4648']; + const alphabet = Variant.alphabet; + const padding = opts.padding != null ? opts.padding : Variant.padding; + const data = Buffer.from(input); + + let bits = 0; + let value = 0; + let output = ''; + + for (let i = 0; i < data.length; i++) { + value = (value << 8) | data.readUInt8(i); + bits += 8; + + while (bits >= 5) { + output += alphabet[(value >>> (bits - 5)) & 31]; + bits -= 5; + } + } + + if (bits > 0) { + output += alphabet[(value << (5 - bits)) & 31]; + } + + if (padding) { + while ((output.length % 8) !== 0) { + output += '='; + } + } + + return output; +}; + +exports.decode = (input, opts = {}) => { + const Variant = Variants[opts.variant] || Variants['RFC4648']; + const alphabet = Variant.alphabet; + + let bits = 0; + let value = 0; + + if (opts.variant === 'CROCKFORD') { + input = input.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'); + } else { + input = input.replace(/=/g, ''); + } + + let index = 0; + let length = input.length; + let output = Buffer.alloc(Math.ceil(length * 5 / 8)); + + for (let i = 0; i < length; i++) { + let char = input[i]; + let char_index = alphabet.indexOf(char); + + if (char_index === -1) { + throw new Error('Invalid character: ' + char); + } + + value = (value << 5) | char_index; + bits += 5; + + if (bits >= 8) { + output.writeUInt8((value >>> (bits - 8)) & 255, index++); + bits -= 8; + } + } + + return output; +}; \ No newline at end of file diff --git a/lib/core/basicauth.js b/lib/core/basicauth.js new file mode 100644 index 0000000..e712232 --- /dev/null +++ b/lib/core/basicauth.js @@ -0,0 +1,43 @@ +// Code taken from https://github.com/jshttp/basic-auth/blob/master/index.js +// Copyright(c) 2013 TJ Holowaychuk +// Copyright(c) 2014 Jonathan Ong +// Copyright(c) 2015-2016 Douglas Christopher Wilson + +module.exports = auth; +module.exports.parse = parse; + +const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/; +const USER_PASS_REGEXP = /^([^:]*):(.*)$/; + +function auth(req) { + if (!req) throw new Error('argument req is required.'); + if (typeof req != 'object') throw new Error('argument req needs to be an object.'); + + return parse(getAuthorization(req)); +} + +function decodeBase64(str) { + return Buffer.from(str, 'base64').toString(); +} + +function getAuthorization(req) { + if (!req.headers || typeof req.headers != 'object') { + throw new Error('argument req needs to have headers.'); + } + + return req.headers.authorization; +} + +function parse(str) { + if (typeof str != 'string') { + return undefined; + } + + const match = CREDENTIALS_REGEXP.exec(str); + if (!match) return undefined; + + const userPass = USER_PASS_REGEXP.exec(decodeBase64(match[1])); + if (!userPass) return undefined; + + return { username: userPass[1], password: userPass[2] }; +} \ No newline at end of file diff --git a/lib/core/db.js b/lib/core/db.js new file mode 100644 index 0000000..a16eb0d --- /dev/null +++ b/lib/core/db.js @@ -0,0 +1,217 @@ +const knex = require('knex'); + +knex.QueryBuilder.extend('whereGroup', function(condition, rules) { + condition = condition.toLowerCase(); + + for (let rule of rules) { + if (!rule.condition) { + let where = condition == 'or' ? 'orWhere' : 'where'; + let column = rule.data ? rule.data.column : rule.column; + + if (rule.data && rule.data.table) { + if (rule.data.schema) { + column = rule.data.schema + '.' + rule.data.table + '.' + column; + } else { + column = rule.data.table + '.' + column; + } + } else if (rule.table) { + if (rule.schema) { + column = rule.schema + '.' + rule.table + '.' + column; + } else { + column = rule.table + '.' + column; + } + } + + if (typeof rule.value == 'undefined') { + rule.value = null; + } + + if (rule.operator == 'between') { + this[where + 'Between'](column, rule.value); + } else if (rule.operator == 'not_between') { + this[where + 'NotBetween'](column, rule.value); + } else if (rule.operator == 'is_null') { + this[where + 'Null'](column); + } else if (rule.operator == 'is_not_null') { + this[where + 'NotNull'](column); + } else if (rule.operator == 'in') { + this[where + 'In'](column, rule.value); + } else if (rule.operator == 'not_in') { + this[where + 'NotIn'](column, rule.value); + } else if (rule.operator == 'begins_with') { + // simple escaping for likes, knex doesn't do this (\ is default in most databases) + // perhaps improve by using raw query like + // this[where](knex.raw("?? like ? escape '\'"), [column, escapedValue]); + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where](column, 'like', escapedValue + '%'); + } else if (rule.operator == 'not_begins_with') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where + 'Not'](column, 'like', escapedValue + '%'); + } else if (rule.operator == 'ends_with') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where](column, 'like', '%' + escapedValue); + } else if (rule.operator == 'not_ends_with') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where + 'Not'](column, 'like', '%' + escapedValue); + } else if (rule.operator == 'contains') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where](column, 'like', '%' + escapedValue + '%'); + } else if (rule.operator == 'not_contains') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where + 'Not'](column, 'like', '%' + escapedValue + '%'); + } else { + this[where](column, rule.operation, rule.value); + } + } else { + this[condition + 'Where'](function() { + this.whereGroup(rule.condition, rule.rules); + }); + } + } + + return this; +}); + +knex.QueryBuilder.extend('fromJSON', function(ast, meta) { + if (ast.type == 'count') { + return this.count('* as Total').from(function() { + this.fromJSON(Object.assign({}, ast, { + type: 'select', + offset: null, + limit: null, + orders: null + })).as('t1'); + }).first(); + } else if (ast.type == 'insert' || ast.type == 'update') { + let values = {}; + + for (let val of ast.values) { + if (val.type == 'json') { + // json support + values[val.column] = JSON.stringify(val.value); + } else { + values[val.column] = val.value; + } + } + + this[ast.type](values); + } else { + this[ast.type](); + } + + if (ast.returning && !(this.client?.config?.client?.includes('mysql'))) { + // Adding the option includeTriggerModifications allows you + // to run statements on tables that contain triggers. Only affects MSSQL. + this.returning(ast.returning, { includeTriggerModifications: true }); + } + + if (ast.table) { + let table = ast.table.name || ast.table; + + if (ast.table.schema) { + table = ast.table.schema + '.' + table; + } + + if (ast.table.alias) { + table += ' as ' + ast.table.alias; + } + + this.from(table); + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.joins && ast.joins.length) { + for (let join of ast.joins) { + let table = join.table; + + if (join.schema) { + table = join.schema + '.' + table; + } + + if (join.alias) { + table += ' as ' + join.alias; + } + + this[(join.type || 'inner').toLowerCase() + 'Join'](table, function() { + for (let clause of join.clauses.rules) { + this.on( + (clause.schema ? clause.schema + '.' : '') + clause.table + '.' + clause.column, + clause.operation, + (clause.value.schema ? clause.value.schema + '.' : '') + clause.value.table + '.' + clause.value.column + ); + } + }); + } + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.columns) { + for (let col of ast.columns) { + let column = col.column || col; + + if (ast.joins && ast.joins.length && col.table) { + column = col.table + '.' + column; + + if (col.schema) { + column = col.schema + '.' + column; + } + } + + if (col.alias) { + column += ' as ' + col.alias; + } + + this[col.aggregate ? col.aggregate.toLowerCase() : 'column'](column); + } + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.groupBy && ast.groupBy.length) { + for (let col of ast.groupBy) { + if (ast.joins && ast.joins.length && col.table) { + if (col.schema) { + this.groupBy(col.schema + '.' + col.table + '.' + col.column); + } else { + this.groupBy(col.table + '.' + col.column); + } + } else { + this.groupBy(col.column || col); + } + } + } + + if (ast.wheres && ast.wheres.condition) { + this.whereGroup(ast.wheres.condition, ast.wheres.rules); + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.orders && ast.orders.length) { + for (let order of ast.orders) { + if (ast.joins && ast.joins.length && order.table) { + if (order.schema) { + this.orderBy(order.schema + '.' + order.table + '.' + order.column, order.direction); + } else { + this.orderBy(order.table + '.' + order.column, order.direction); + } + } else { + this.orderBy(order.column, order.direction); + } + } + } + + if (ast.distinct) { + this.distinct(); + } + + if (ast.type == 'select' && ast.limit) { + this.limit(ast.limit); + } + + if (ast.type == 'select' && ast.offset) { + this.offset(ast.offset); + } + + if (meta) { + this.queryContext(meta); + } + + return this; +}); + +module.exports = knex; \ No newline at end of file diff --git a/lib/core/diacritics.js b/lib/core/diacritics.js new file mode 100644 index 0000000..8f4caa4 --- /dev/null +++ b/lib/core/diacritics.js @@ -0,0 +1,96 @@ +const diacriticsMap = [ + {'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g}, + {'base':'AA','letters':/[\uA732]/g}, + {'base':'AE','letters':/[\u00C6\u01FC\u01E2]/g}, + {'base':'AO','letters':/[\uA734]/g}, + {'base':'AU','letters':/[\uA736]/g}, + {'base':'AV','letters':/[\uA738\uA73A]/g}, + {'base':'AY','letters':/[\uA73C]/g}, + {'base':'B', 'letters':/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g}, + {'base':'C', 'letters':/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g}, + {'base':'D', 'letters':/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g}, + {'base':'DZ','letters':/[\u01F1\u01C4]/g}, + {'base':'Dz','letters':/[\u01F2\u01C5]/g}, + {'base':'E', 'letters':/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g}, + {'base':'F', 'letters':/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g}, + {'base':'G', 'letters':/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g}, + {'base':'H', 'letters':/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g}, + {'base':'I', 'letters':/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g}, + {'base':'J', 'letters':/[\u004A\u24BF\uFF2A\u0134\u0248]/g}, + {'base':'K', 'letters':/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g}, + {'base':'L', 'letters':/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g}, + {'base':'LJ','letters':/[\u01C7]/g}, + {'base':'Lj','letters':/[\u01C8]/g}, + {'base':'M', 'letters':/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g}, + {'base':'N', 'letters':/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g}, + {'base':'NJ','letters':/[\u01CA]/g}, + {'base':'Nj','letters':/[\u01CB]/g}, + {'base':'O', 'letters':/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g}, + {'base':'OI','letters':/[\u01A2]/g}, + {'base':'OO','letters':/[\uA74E]/g}, + {'base':'OU','letters':/[\u0222]/g}, + {'base':'P', 'letters':/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g}, + {'base':'Q', 'letters':/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g}, + {'base':'R', 'letters':/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g}, + {'base':'S', 'letters':/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g}, + {'base':'T', 'letters':/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g}, + {'base':'TZ','letters':/[\uA728]/g}, + {'base':'U', 'letters':/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g}, + {'base':'V', 'letters':/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g}, + {'base':'VY','letters':/[\uA760]/g}, + {'base':'W', 'letters':/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g}, + {'base':'X', 'letters':/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g}, + {'base':'Y', 'letters':/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g}, + {'base':'Z', 'letters':/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g}, + {'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g}, + {'base':'aa','letters':/[\uA733]/g}, + {'base':'ae','letters':/[\u00E6\u01FD\u01E3]/g}, + {'base':'ao','letters':/[\uA735]/g}, + {'base':'au','letters':/[\uA737]/g}, + {'base':'av','letters':/[\uA739\uA73B]/g}, + {'base':'ay','letters':/[\uA73D]/g}, + {'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g}, + {'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g}, + {'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g}, + {'base':'dz','letters':/[\u01F3\u01C6]/g}, + {'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g}, + {'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g}, + {'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g}, + {'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g}, + {'base':'hv','letters':/[\u0195]/g}, + {'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g}, + {'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g}, + {'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g}, + {'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g}, + {'base':'lj','letters':/[\u01C9]/g}, + {'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g}, + {'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g}, + {'base':'nj','letters':/[\u01CC]/g}, + {'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g}, + {'base':'oi','letters':/[\u01A3]/g}, + {'base':'ou','letters':/[\u0223]/g}, + {'base':'oo','letters':/[\uA74F]/g}, + {'base':'p','letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g}, + {'base':'q','letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g}, + {'base':'r','letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g}, + {'base':'s','letters':/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g}, + {'base':'t','letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g}, + {'base':'tz','letters':/[\uA729]/g}, + {'base':'u','letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g}, + {'base':'v','letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g}, + {'base':'vy','letters':/[\uA761]/g}, + {'base':'w','letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g}, + {'base':'x','letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g}, + {'base':'y','letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g}, + {'base':'z','letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g} +]; + +module.exports = { + replace: function(str) { + for (let change of diacriticsMap) { + str = str.replace(change.letters, change.base); + } + + return str; + } +}; \ No newline at end of file diff --git a/lib/core/memoryStore.js b/lib/core/memoryStore.js new file mode 100644 index 0000000..767fc4e --- /dev/null +++ b/lib/core/memoryStore.js @@ -0,0 +1,92 @@ +module.exports = function(session) { + const Store = session.Store + + const defer = (cb, ...args) => { + if (typeof cb != 'function') return + setImmediate(cb, ...args) + } + + class ExtendedMap extends Map { + set(key, value, maxAge) { + const cached = super.get(key) + if (cached && cached.timer) clearTimeout(cached.timer) + const timer = maxAge ? setTimeout(super.delete.bind(this, key), maxAge) : null + return super.set(key, {timer, value}) + } + + get(key) { + const cached = super.get(key) + return cached && cached.value + } + + delete(key) { + const cached = super.get(key) + if (cached && cached.timer) clearTimeout(cached.timer) + return super.delete(key) + } + } + + class MemoryStore extends Store { + constructor(options = {}) { + super(options) + this.sessions = new ExtendedMap() + this.ttl = options.ttl + } + + get(sid, callback) { + let session = this.sessions.get(sid) + defer(callback, null, session) + } + + set(sid, session, callback) { + let ttl = this._getTTL(session) + if (ttl > 0) { + this.sessions.set(sid, session, ttl) + } else { + this.sessions.delete(sid) + } + defer(callback, null) + } + + touch(sid, session, callback) { + let ttl = this._getTTL(session) + let stored = this.sessions.get(sid) + stored.cookie = session.cookie + this.sessions.set(sid, stored, ttl) + defer(callback, null) + } + + destroy(sid, callback) { + this.sessions.delete(sid) + defer(callback, null) + } + + clear(callback) { + this.sessions.clear() + defer(callback, null) + } + + length(callback) { + let len = this.sessions.size + defer(callback, null, len) + } + + ids(callback) { + let keys = Array.from(this.sessions.keys()) + defer(callback, null, keys) + } + + all(callback) { + let sessions = Array.from(this.sessions.values()) + defer(callback, null, sessions) + } + + _getTTL(session) { + if (typeof this.ttl == 'number') return this.ttl + let maxAge = (session && session.cookie) ? session.cookie.maxAge : null + return (typeof maxAge == 'number') ? maxAge : 86400000 + } + } + + return MemoryStore +} \ No newline at end of file diff --git a/lib/core/middleware.js b/lib/core/middleware.js new file mode 100644 index 0000000..3c662c2 --- /dev/null +++ b/lib/core/middleware.js @@ -0,0 +1,163 @@ +const config = require('../setup/config'); +const debug = require('debug')('server-connect:router'); +const { clone } = require('./util'); +const App = require('./app'); + +module.exports = { + cache: function(options) { + return async function(req, res, next) { + if (!options.ttl || !global.redisClient) { + // no caching + return next(); + } + + let key = 'erc:' + (req.originalUrl || req.url); + let nocache = false; + let nostore = false; + + if (req.fragment) { + key += '-fragment'; + } + + if (req.query.nocache) { + nocache = true; + } + + try { + const cacheControl = req.get('Cache-Control'); + if (cacheControl) { + if (cacheControl.includes('no-cache')) nocache = true; + if (cacheControl.includes('no-store')) nostore = true; + } + } catch (err) { + // Ignore but log errors + console.error(err); + } + + if (!nocache) { + try { + const cache = await global.redisClient.hgetall(key); + if (cache.body) { + res.type(cache.type || 'text/html'); + res.send(cache.body); + return; + } + } catch (err) { + // Ignore but log errors + console.error(err); + } + } + + if (!nostore) { + // wrap res.send + const send = res.send.bind(res); + res.send = function (body) { + const ret = send(body); + + if (this.statusCode !== 200 || typeof body !== 'string') { + // do not cache when not status 200 or body is not a string + return ret; + } + + global.redisClient.hset(key, { + body: body, + type: this.get('Content-Type') || 'text/html' + }).then(() => { + return global.redisClient.expire(key, +options.ttl); + }).catch(err => { + // Ignore but log errors + console.error(err); + }); + + return ret; + }; + } + + return next(); + }; + }, + + serverConnect: function(json) { + return async function(req, res, next) { + const app = new App(req, res); + + debug(`Serving serverConnect ${req.path}`); + + return Promise.resolve(app.define(json)).catch(next); + }; + }, + + templateView: function(layout, page, data, exec) { + return async function(req, res, next) { + const app = new App(req, res); + + debug(`Serving templateView ${req.path}`); + + const routeData = clone(data); + const methods = { + _: (expr, data = {}) => { + return app.parse(`{{${expr}}}`, app.scope.create(data)); + }, + + _exec: async (name, steps, data = {}) => { + let context = {}; + app.scope = app.scope.create(data, context); + await app.exec(steps, true); + app.scope = app.scope.parent; + app.set(name, context); + return context; + }, + + _repeat: (expr, cb) => { + let data = app.parse(`{{${expr}}}`); + if (Array.isArray(data)) return data.forEach(cb); + return ''; + }, + + _csrfToken: (overwrite) => { + return req.csrfToken(overwrite); + }, + + _csrfMeta: () => { + return ``; + }, + + _csrfInput: () => { + return ``; + }, + + _route: req.route.path + }; + + let template = page; + if (layout && !req.fragment) { + routeData.content = '/' + page; + template = 'layouts/' + layout; + } + + if (exec) { + return Promise.resolve(app.define(exec, true)).then(() => { + if (!res.headersSent) { + app.set(app.parse(routeData)); + debug(`Render template ${template}`); + debug(`Template data: %O`, Object.assign({}, app.global.data, app.data)); + res.render(template, Object.assign({}, app.global.data, app.data, methods), async (err, html) => { + // callback is needed when using ejs with aync, html is a promise + if (err) return next(err); + html.then(html => res.send(html)).catch(err => next(err)); + }); + } + }).catch(next) + } else { + app.set(app.parse(routeData)); + debug(`Render template ${template}`); + debug(`Template data: %O`, app.global.data); + res.render(template, Object.assign({}, app.global.data, methods), async (err, html) => { + // callback is needed when using ejs with aync, html is a promise + if (err) return next(err); + html.then(html => res.send(html)).catch(err => next(err)); + }); + } + }; + } +}; \ No newline at end of file diff --git a/lib/core/parser.js b/lib/core/parser.js new file mode 100644 index 0000000..73091dc --- /dev/null +++ b/lib/core/parser.js @@ -0,0 +1,752 @@ +const fs = require('fs-extra'); +const path = require('path'); +const { randomUUID } = require('crypto'); +const { v4: uuidv4 } = require('uuid'); + +const NOOP = function() {}; + +const OPERATORS = { + '{' : 'L_CURLY', + '}' : 'R_CURLY', + '(' : 'L_PAREN', + ')' : 'R_PAREN', + '[' : 'L_BRACKET', + ']' : 'R_BRACKET', + '.' : 'PERIOD', + ',' : 'COMMA', + ':' : 'COLON', + '?' : 'QUESTION', + // Arithmetic operators + '+' : 'ADDICTIVE', + '-' : 'ADDICTIVE', + '*' : 'MULTIPLICATIVE', + '/' : 'MULTIPLICATIVE', + '%' : 'MULTIPLICATIVE', + // Comparison operators + '===': 'EQUALITY', + '!==': 'EQUALITY', + '==' : 'EQUALITY', + '!=' : 'EQUALITY', + '<' : 'RELATIONAL', + '>' : 'RELATIONAL', + '<=' : 'RELATIONAL', + '>=' : 'RELATIONAL', + 'in' : 'RELATIONAL', + // Logical operators + '&&' : 'LOGICAL_AND', + '||' : 'LOGICAL_OR', + '!' : 'LOGICAL_NOT', + // Bitwise operators + '&' : 'BITWISE_AND', + '|' : 'BITWISE_OR', + '^' : 'BITWISE_XOR', + '~' : 'BITWISE_NOT', + '<<' : 'BITWISE_SHIFT', + '>>' : 'BITWISE_SHIFT', + '>>>': 'BITWISE_SHIFT' +}; + +const EXPRESSIONS = { + 'in' : function(a, b) { return a() in b(); }, + '?' : function(a, b, c) { return a() ? b() : c(); }, + '+' : function(a, b) { a = a(); b = b(); return a == null ? b : b == null ? a : a + b; }, + '-' : function(a, b) { return a() - b(); }, + '*' : function(a, b) { return a() * b(); }, + '/' : function(a, b) { return a() / b(); }, + '%' : function(a, b) { return a() % b(); }, + '===': function(a, b) { return a() === b(); }, + '!==': function(a, b) { return a() !== b(); }, + '==' : function(a, b) { return a() == b(); }, + '!=' : function(a, b) { return a() != b(); }, + '<' : function(a, b) { return a() < b(); }, + '>' : function(a, b) { return a() > b(); }, + '<=' : function(a, b) { return a() <= b(); }, + '>=' : function(a, b) { return a() >= b(); }, + '&&' : function(a, b) { return a() && b(); }, + '||' : function(a, b) { return a() || b(); }, + '&' : function(a, b) { return a() & b(); }, + '|' : function(a, b) { return a() | b(); }, + '^' : function(a, b) { return a() ^ b(); }, + '<<' : function(a, b) { return a() << b(); }, + '>>' : function(a, b) { return a() >> b(); }, + '>>>': function(a, b) { return a() >>> b(); }, + '~' : function(a) { return ~a(); }, + '!' : function(a) { return !a(); } +}; + +const ESCAPE = { + 'n': '\n', + 'f': '\f', + 'r': '\r', + 't': '\t', + 'v': '\v', + "'": "'", + '"': '"', + '`': '`' +}; + +const formatters = require('../formatters'); + +// User formatters +if (fs.existsSync('extensions/server_connect/formatters')) { + const files = fs.readdirSync('extensions/server_connect/formatters'); + for (let file of files) { + if (path.extname(file) == '.js') { + Object.assign(formatters, require(`../../extensions/server_connect/formatters/${file}`)); + } + } +} + +function lexer(expr) { + let tokens = [], + token, + name, + start, + index = 0, + op = true, + ch, + ch2, + ch3; + + while (index < expr.length) { + start = index; + + ch = read(); + + if (isQuote(ch) && op) { + name = 'STRING'; + token = readString(ch); + op = false; + } else if ((isDigid(ch) || (is('.') && peek() && isDigid(peek()))) && op) { + name = 'NUMBER'; + token = readNumber(); + op = false; + } else if (isAlpha(ch) && op) { + name = 'IDENT'; + token = readIdent(); + if (is('(')) { + name = 'METHOD'; + } + op = false; + } else if (is('/') && op && (token == '(' || token == ',' || token == '?' || token == ':') && testRegexp()) { + name = 'REGEXP'; + token = readRegexp(); + op = false; + } else if (isWhitespace(ch)) { + index++; + continue; + } else if ((ch3 = read(3)) && OPERATORS[ch3]) { + name = OPERATORS[ch3]; + token = ch3; + op = true; + index += 3; + } else if ((ch2 = read(2)) && OPERATORS[ch2]) { + name = OPERATORS[ch2]; + token = ch2; + op = true; + index += 2; + } else if (OPERATORS[ch]) { + name = OPERATORS[ch]; + token = ch; + op = true; + index++; + } else { + throw new Error('Lexer Error: Unexpected token "' + ch + '" at column ' + index + ' in expression {{' + expr + '}}'); + } + + tokens.push({ + name: name, + index: start, + value: token + }); + } + + return tokens; + + function read(n) { + if (!n) n = 1; + return expr.substr(index, n); + } + + function peek(n) { + n = n || 1; + return index + n < expr.length ? expr[index + n] : false; + } + + function is(chars) { + return chars.indexOf(ch) != -1; + } + + function isQuote(ch) { + return ch == '"' || ch == "'"; + } + + function isDigid(ch) { + return ch >= '0' && ch <= '9'; + } + + function isAlpha(ch) { + return (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + ch == '_' || ch == '$'; + } + + function isAlphaNum(ch) { + return isAlpha(ch) || isDigid(ch); + } + + function isWhitespace(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\u00A0'; + } + + function isExpOperator(ch) { + return ch == '-' || ch == '+' || isDigid(ch); + } + + function readString(quote) { + let str = '', esc = false; + + index++; + + while (index < expr.length) { + ch = read(); + + if (esc) { + if (ch == 'u') { + // unicode escape + index++; + let hex = read(4); + if (!hex.match(/[\da-f]{4}/i)) { + throw new Error('Lexer Error: Invalid unicode escape at column ' + index + ' in expression {{' + expr + '}}'); + } + str += String.fromCharCode(parseInt(hex, 16)); + index += 3; + } else { + str += ESCAPE[ch] ? ESCAPE[ch] : ch; + } + + esc = false; + } else if (ch == '\\') { + // escape character + esc = true; + } else if (ch == quote) { + // end of string + index ++; + return str; + } else { + str += ch; + } + + index++; + } + + throw new Error('Lexer Error: Unterminated string at column ' + index + ' in expression {{' + expr + '}}'); + } + + function readNumber() { + let num = '', exp = false; + + while (index < expr.length) { + ch = read(); + + if (isDigid(ch) || (is('.') && peek() && isDigid(peek()))) { + num += ch; + } else { + let next = peek(); + + if (is('eE') && isExpOperator(next)) { + num += 'e'; + exp = true; + } else if (isExpOperator(ch) && next && isDigid(next) && exp) { + num += ch; + exp = false; + } else if (isExpOperator(ch) && (!next || !isDigid(next)) && exp) { + throw new Error('Lexer Error: Invalid exponent at column ' + index + ' in expression {{' + expr + '}}'); + } else { + break; + } + } + + index++; + } + + return +num; + } + + function readIdent() { + let ident = ''; + + while (index < expr.length) { + ch = read(); + + if (isAlphaNum(ch)) { + ident += ch; + } else { + break; + } + + index++; + } + + return ident; + } + + function readRegexp() { + let re = '', mod = '', esc = false; + + index ++; + + while (index < expr.length) { + ch = read(); + + if (esc) { + esc = false; + } else if (ch == '\\') { + esc = true; + } else if (ch == '/') { + index++; + + while ('ign'.indexOf(ch = read()) != -1) { + mod += ch; + index++; + } + + return re + '%%%' + mod; + } + + re += ch; + index++; + } + + throw new Error('Lexer Error: Unterminated regexp at column ' + index + ' in expression {{' + expr + '}}'); + } + + function testRegexp() { + var idx = index, ok = true; + + try { + readRegexp(); + } catch (e) { + ok = false; + } + + // reset our index and ch + index = idx; + ch = '/'; + + return ok; + } +} + +function parser(expr, scope) { + let tokens = lexer(expr), + context = undefined, + RESERVED = { + 'PI' : function() { return Math.PI; }, + 'UUID' : function() { return randomUUID ? randomUUID() : uuidv4(); }, + 'NOW' : function() { return date(); }, + 'NOW_UTC' : function() { return utc_date(); }, + 'TIMESTAMP': function() { return timestamp(); }, + '$this' : function() { return scope.data; }, + '$global' : function() { return globalScope.data; }, + '$parent' : function() { return scope.parent && scope.parent.data; }, + 'null' : function() { return null; }, + 'true' : function() { return true; }, + 'false' : function() { return false; }, + '_' : function() { return { __dmxScope__: true } } + }; + + return start()(); + + function pad(s, n) { + return ('000' + s).substr(-n); + } + + function date(dt) { + dt = dt || new Date(); + return pad(dt.getFullYear(), 4) + '-' + pad(dt.getMonth() + 1, 2) + '-' + pad(dt.getDate(), 2) + ' ' + + pad(dt.getHours(), 2) + ':' + pad(dt.getMinutes(), 2) + ':' + pad(dt.getSeconds(), 2); + } + + function utc_date(dt) { + dt = dt || new Date(); + return pad(dt.getUTCFullYear(), 4) + '-' + pad(dt.getUTCMonth() + 1, 2) + '-' + pad(dt.getUTCDate(), 2) + 'T' + + pad(dt.getUTCHours(), 2) + ':' + pad(dt.getUTCMinutes(), 2) + ':' + pad(dt.getUTCSeconds(), 2) + 'Z'; + } + + function timestamp(dt) { + dt = dt || new Date(); + return ~~(dt / 1000); + } + + function read() { + if (tokens.length === 0) { + throw new Error('Parser Error: Unexpected end of expression {{' + expr + '}}'); + } + + return tokens[0]; + } + + function peek(e) { + if (tokens.length > 0) { + let token = tokens[0]; + + if (!e || token.name == e) { + return token; + } + } + + return false; + } + + function expect(e) { + let token = peek(e); + + if (token) { + tokens.shift(); + return token; + } + + return false; + } + + function consume(e) { + if (!expect(e)) { + throw new Error('Parser Error: Unexpected token, expecting ' + e + ' in expression {{' + expr + '}}'); + } + } + + function fn(expr) { + let args = [].slice.call(arguments, 1); + + return function() { + if (EXPRESSIONS[expr]) { + return EXPRESSIONS[expr].apply(context, args); + } else { + return expr; + } + } + } + + function start() { + return conditional(); + } + + function conditional() { + let left = logicalOr(), middle, token; + + if ((token = expect('QUESTION'))) { + middle = conditional(); + + if ((token = expect('COLON'))) { + return fn('?', left, middle, conditional()); + } else { + throw new Error('Parse Error: Expecting : in expression {{' + expr + '}}'); + } + } else { + return left; + } + } + + function logicalOr() { + let left = logicalAnd(), token; + + while (true) { + if ((token = expect('LOGICAL_OR'))) { + left = fn(token.value, left, logicalAnd()); + } else { + return left; + } + } + } + + function logicalAnd() { + let left = bitwiseOr(), token; + + if ((token = expect('LOGICAL_AND'))) { + left = fn(token.value, left, logicalAnd()); + } + + return left; + } + + function bitwiseOr() { + let left = bitwiseXor(), token; + + if ((token = expect('BITWISE_OR'))) { + left = fn(token.value, left, bitwiseXor()); + } + + return left; + } + + function bitwiseXor() { + let left = bitwiseAnd(), token; + + if ((token = expect('BITWISE_XOR'))) { + left = fn(token.value, left, bitwiseAnd()); + } + + return left; + } + + function bitwiseAnd() { + let left = equality(), token; + + if ((token = expect('BITWISE_AND'))) { + left = fn(token.value, left, bitwiseAnd()); + } + + return left; + } + + function equality() { + let left = relational(), token; + + if ((token = expect('EQUALITY'))) { + left = fn(token.value, left, equality()); + } + + return left; + } + + function relational() { + let left = bitwiseShift(), token; + + if ((token = expect('RELATIONAL'))) { + left = fn(token.value, left, relational()); + } + + return left; + } + + function bitwiseShift() { + let left = addictive(), token; + + if ((token = expect('BITWISE_SHIFT'))) { + left = fn(token.value, left, addictive()); + } + + return left; + } + + function addictive() { + let left = multiplicative(), token; + + while ((token = expect('ADDICTIVE'))) { + left = fn(token.value, left, multiplicative()); + } + + return left; + } + + function multiplicative() { + let left = unary(), token; + + while ((token = expect('MULTIPLICATIVE'))) { + left = fn(token.value, left, unary()); + } + + return left; + } + + function unary() { + let token; + + if ((token = expect('ADDICTIVE'))) { + if (token.value == '+') { + return primary(); + } else { + return fn(token.value, function() { return 0; }, unary()); + } + } else if ((token = expect('LOGICAL_NOT'))) { + return fn(token.value, unary()); + } + + return primary(); + } + + function primary() { + let value, next; + + if (expect('L_PAREN')) { + value = start(); + consume('R_PAREN'); + } else if (expect('L_CURLY')) { + let obj = {}; + + if (read().name != 'R_CURLY') { + do { + let key = expect().value; + consume('COLON'); + obj[key] = start()(); + } while (expect('COMMA')); + } + + value = fn(obj); + + consume('R_CURLY'); + } else if (expect('L_BRACKET')) { + let arr = []; + + if (read().name != 'R_BRACKET') { + do { + arr.push(start()()); + } while (expect('COMMA')); + } + + value = fn(arr); + + consume('R_BRACKET'); + } else if (expect('PERIOD')) { + value = peek() ? objectMember(fn(scope.data)) : fn(scope.data); + } else { + let token = expect(); + + if (token === false) { + throw new Error('Parser Error: Not a primary expression {{' + expr + '}}'); + } + + if (token.name == 'IDENT') { + value = RESERVED.hasOwnProperty(token.value) + ? RESERVED[token.value] + : function() { return scope.get(token.value) }; + } else if (token.name == 'METHOD') { + if (!formatters[token.value]) { + throw new Error('Parser Error: Formatter "' + token.value + '" does not exist, expression {{' + expression + '}}'); + } + + value = fn(formatters[token.value]); + } else if (token.name == 'REGEXP') { + value = function() { + let re = token.value.split('%%%'); + return new RegExp(re[0], re[1]); + }; + } else { + value = function() { return token.value }; + } + } + + while ((next = expect('L_PAREN') || expect('L_BRACKET') || expect('PERIOD'))) { + if (next.value == '(') { + value = functionCall(value, context); + } else if (next.value == '[') { + value = objectIndex(value); + } else if (next.value == '.') { + context = value; + value = objectMember(value); + } else { + throw new Error('Parser Error: Parse error in expression {{' + expr + '}}'); + } + } + + context = undefined; + + return value; + } + + function functionCall(func, ctx) { + let argsFn = []; + + if (read().name != 'R_PAREN') { + do { + argsFn.push(start()); + } while (expect('COMMA')); + } + + consume('R_PAREN'); + + return function() { + let args = []; + + if (ctx) args.push(ctx()); + + for (let argFn of argsFn) { + args.push(argFn()); + } + + let fnPtr = func() || NOOP; + + return fnPtr.apply(null, args); + } + } + + function objectIndex(obj) { + let indexFn = start(); + + consume('R_BRACKET'); + + return function() { + let o = obj(), + i = indexFn(); + + if (typeof o != 'object') return undefined; + + if (o.__dmxScope__) { + return scope.get(i); + } + + return o[i]; + } + } + + function objectMember(obj) { + let token = expect(); + + return function() { + let o = obj(); + + if (token.name == 'METHOD') { + if (!formatters[token.value]) { + throw new Error('Parser Error: Formatter "' + token.value + '" does not exist, expression {{' + expr + '}}'); + } + + return formatters[token.value]; + } + + if (o && o.__dmxScope) { + return scope.get(token.value); + } + + return o && o[token.value]; + } + } +} + +function parseValue(value, scope) { + if (value == null) return value; + + value = value.valueOf(); + + if (typeof value == 'object') { + for (let key in value) { + value[key] = parseValue(value[key], scope); + } + } + + if (typeof value == 'string') { + if (value.substr(0, 2) == '{{' && value.substr(-2) == '}}') { + let expr = value.replace(/^\{\{|\}\}$/g, ''); + + if (expr.indexOf('{{') == -1) { + return parser(expr, scope); + } + } + + return parseTemplate(value, scope); + } + + return value; +} + +function parseTemplate(template, scope) { + return template.replace(/\{\{(.*?)\}\}/g, function(a, m) { + var value = parser(m, scope); + return value != null ? String(value) : ''; + }); +} + +exports.lexer = lexer; +exports.parse = parser; +exports.parseValue = parseValue; +exports.parseTemplate = parseTemplate; \ No newline at end of file diff --git a/lib/core/path.js b/lib/core/path.js new file mode 100644 index 0000000..2166a34 --- /dev/null +++ b/lib/core/path.js @@ -0,0 +1,97 @@ +const { existsSync: exists } = require('fs'); +const { dirname, basename, extname, join, resolve, relative, posix } = require('path'); +const { v4: uuidv4 } = require('uuid'); +const debug = require('debug')('server-connect:path'); + +module.exports = { + + getFilesArray: function(paths) { + let files = []; + + if (!Array.isArray(paths)) { + paths = [paths]; + } + + for (let path of paths) { + if (Array.isArray(path)) { + files = files.concat(module.exports.getFilesArray(path)); + } else if (path && path.path) { + files.push(module.exports.toSystemPath(path.path)); + } else if (path) { + files.push(module.exports.toSystemPath(path)); + } + } + + return files; + }, + + toSystemPath: function(path) { + if (path[0] != '/' || path.includes('../')) { + throw new Error(`path.toSystemPath: Invalid path "${path}".`); + } + + return resolve('.' + path); + }, + + toAppPath: function(path) { + let root = resolve('.'); + let rel = relative(root, path).replace(/\\/g, '/'); + + debug('toAppPath: %O', { root, path, rel }); + + if (rel.includes('../')) { + throw new Error(`path.toAppPath: Invalid path "${rel}".`); + } + + return '/' + rel; + }, + + toSiteUrl: function(path) { + let root = resolve('public'); + let rel = relative(root, path).replace(/\\/g, '/'); + + debug('toSiteUrl: %O', { root, path, rel }); + + if (rel.includes('../')) { + return ''; + } + + return '/' + rel; + }, + + getUniqFile: function(path) { + let n = 1; + + while (exists(path)) { + path = path.replace(/(_(\d+))?(\.\w+)$/, (a, b, c, d) => '_' + (n++) + (d || a)); + if (n > 999) throw new Error(`path.getUniqFile: Couldn't create a unique filename for ${path}`); + } + + return path; + }, + + parseTemplate: function(path, template) { + let n = 1, dir = dirname(path), file = template.replace(/\{([^\}]+)\}/g, (a, b) => { + switch (b) { + case 'name': return basename(path, extname(path)); + case 'ext' : return extname(path); + case 'guid': return uuidv4(); + } + + return a; + }); + + if (file.includes('{_n}')) { + template = file; + file = template.replace('{_n}', ''); + + while (exists(join(dir, file))) { + file = template.replace('{_n}', n++); + if (n > 999) throw new Error(`path.parseTemplate: Couldn't create a unique filename for ${path}`); + } + } + + return join(dir, file); + } + +}; \ No newline at end of file diff --git a/lib/core/scope.js b/lib/core/scope.js new file mode 100644 index 0000000..dc8d92e --- /dev/null +++ b/lib/core/scope.js @@ -0,0 +1,63 @@ +function Scope(data, parent, context) { + if (typeof data != 'object') { + data = { $value: data }; + } + + this.data = data; + this.parent = parent; + this.context = context; +}; + +Scope.prototype = { + create: function(data, context) { + return new Scope(data, this, context); + }, + + get: function(name) { + if (this.data[name] !== undefined) { + return this.data[name]; + } + + if (this.parent) { + return this.parent.get(name); + } + + return undefined; + }, + + set: function(name, value) { + if (typeof name == 'object') { + for (let prop in name) { + this.set(prop, name[prop]); + } + } else { + this.data[name] = value; + + if (this.context) { + this.context[name] = value; + } + } + }, + + has: function(name) { + if (this.data[name] !== undefined) { + return true; + } + + if (this.parent) { + return this.parent.has(name); + } + + return false; + }, + + remove: function(name) { + delete this.data[name]; + + if (this.context) { + delete this.context[name]; + } + }, +} + +module.exports = Scope; \ No newline at end of file diff --git a/lib/core/util.js b/lib/core/util.js new file mode 100644 index 0000000..5c7ebd4 --- /dev/null +++ b/lib/core/util.js @@ -0,0 +1,186 @@ +const reGrouping = /(\d+)(\d{3})/; + +function clone(o) { + if (o == null) { + return o; + } + + if (Array.isArray(o)) { + return o.map(clone); + } + + if (o instanceof Date) { + // we do not clone date objects but convert them to an iso string + return o.toISOString(); + } + + if (typeof o == 'object') { + let oo = {}; + for (let key in o) { + oo[key] = clone(o[key]); + } + return oo; + } + + return o; +} + +function mergeDeep(target, source) { + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return target; +} + +function isObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item) && item !== null); + } + +module.exports = { + + clone, + + mergeDeep, + + escapeRegExp: function(val) { + // https://github.com/benjamingr/RegExp.escape + return val.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); + }, + + keysToLowerCase: function(o) { + return Object.keys(o).reduce((c, k) => (c[k.toLowerCase()] = o[k], c), {}); + }, + + padNumber: function(num, digids, trim) { + let sign = ''; + + if (num < 0) { + sign = '-'; + num = -num; + } + + num = String(num); + + while (num.length < digids) { + num = '0' + num; + } + + if (trim) { + num = num.substr(num.length - digids); + } + + return sign + num; + }, + + formatNumber: function(num, decimals, decimalSeparator, groupingSeparator) { + num = Number(num); + + if (isNaN(num) || !isFinite(num)) return ''; + + decimalSeparator = typeof decimalSeparator == 'string' ? decimalSeparator : '.'; + groupingSeparator = typeof groupingSeparator == 'string' ? groupingSeparator : ''; + precision = typeof precision == 'number' ? Math.abs(precision) : null; + + let minus = num < 0; + let parts = (decimals == null ? String(Math.abs(num)) : Math.abs(num).toFixed(decimals)).split('.'); + let wholePart = parts[0]; + let decimalPart = parts.length > 1 ? decimalSeparator + parts[1] : ''; + + if (groupingSeparator) { + while (reGrouping.test(wholePart)) { + wholePart = wholePart.replace(reGrouping, '$1' + groupingSeparator + '$2'); + } + } + + return (minus ? '-' : '') + wholePart + decimalPart; + }, + + parseDate: function(obj) { + if (Object.prototype.toString.call(obj) == '[object Date]') return new Date(obj); + + let date = new Date(obj); + + if (typeof obj == 'number') { + date = new Date(obj * 1000); + } + + if (typeof obj == 'string') { + if (obj == 'now') { + date = new Date(); + } else if (/^\d{4}-\d{2}-\d{2}$/.test(obj)) { + let parts = obj.split('-'); + date = new Date(parts[0], parts[1] - 1, parts[2]); + } else if (/^\d{2}:\d{2}:\d{2}$/.test(obj)) { + let parts = obj.split(':'); + date = new Date(); + date.setHours(parts[0]); + date.setMinutes(parts[1]); + date.setSeconds(parts[2]); + date.setMilliseconds(0); + } + } + + if (date.toString() == 'Invalid Date') { + return undefined; + } + + return date; + }, + + formatDate: function(date, format, utc, locale) { + date = module.exports.parseDate(date); + if (date == null) return date; + locale = typeof locale == 'string' ? locale : 'en-US'; + const lang = require('../locale/' + locale); + const pad = (v, n) => `0000${v}`.substr(-n); + + let y = utc ? date.getUTCFullYear() : date.getFullYear(), + n = utc ? date.getUTCMonth() : date.getMonth(), + d = utc ? date.getUTCDate() : date.getDate(), + w = utc ? date.getUTCDay() : date.getDay(), + h = utc ? date.getUTCHours() : date.getHours(), + m = utc ? date.getUTCMinutes() : date.getMinutes(), + s = utc ? date.getUTCSeconds() : date.getSeconds(), + v = utc ? date.getUTCMilliseconds() : date.getMilliseconds(); + + return format.replace(/([yMdHhmsaAvw])(\1+)?/g, part => { + switch (part) { + case 'yyyy': return pad(y, 4); + case 'yy': return pad(y, 2); + case 'y': return y; + case 'MMMM': return lang.months[n]; + case 'MMM': return lang.monthsShort[n]; + case 'MM': return pad(n + 1, 2); + case 'M': return n + 1; + case 'dddd': return lang.days[w]; + case 'ddd': return lang.daysShort[w]; + case 'dd': return pad(d, 2); + case 'd': return d; + case 'HH': return pad(h, 2); + case 'H': return h; + case 'hh': return pad((h % 12) || 12, 2); + case 'h': return (h % 12) || 12; + case 'mm': return pad(m, 2); + case 'm': return m; + case 'ss': return pad(s, 2); + case 's': return s; + case 'a': return h < 12 ? 'am' : 'pm'; + case 'A': return h < 12 ? 'AM' : 'PM'; + case 'v': return pad(v, 3); + case 'w': return w; + } + }); + } + +}; \ No newline at end of file diff --git a/lib/core/webhook.js b/lib/core/webhook.js new file mode 100644 index 0000000..f6ac8f1 --- /dev/null +++ b/lib/core/webhook.js @@ -0,0 +1,24 @@ +// Helper methods for webhooks + +const fs = require('fs-extra'); +const App = require('./app'); + +exports.createHandler = function(name, fn = () => 'handler') { + return async (req, res, next) => { + const action = await fn(req, res, next); + + if (typeof action == 'string') { + const path = `app/webhooks/${name}/${action}.json`; + + if (fs.existsSync(path)) { + const app = new App(req, res); + let json = await fs.readJSON(path); + return Promise.resolve(app.define(json)).catch(next); + } else { + res.json({error: `No action found for ${action}.`}); + // do not return 404 else stripe will retry + //next(); + } + } + } +}; \ No newline at end of file diff --git a/lib/formatters/collections.js b/lib/formatters/collections.js new file mode 100644 index 0000000..f7dd2a0 --- /dev/null +++ b/lib/formatters/collections.js @@ -0,0 +1,169 @@ +module.exports = { + + join: function(arr, separator, prop) { + if (!Array.isArray(arr)) return arr; + return arr.map(item => prop ? item[prop] : item).join(separator); + }, + + first: function(arr) { + if (!Array.isArray(arr)) return arr; + return arr.length ? arr[0] : null; + }, + + top: function(arr, count) { + if (!Array.isArray(arr)) return arr; + return arr.slice(0, count); + }, + + last: function(arr, count) { + if (!Array.isArray(arr)) return arr; + if (count) return arr.slice(-count); + return arr[arr.length - 1]; + }, + + where: function(arr, prop, operator, value) { + if (!Array.isArray(arr)) return arr; + return arr.filter(item => { + let val = item[prop]; + + switch (operator) { + case 'startsWith': return String(val).startsWith(value); + case 'endsWith': return String(val).endsWith(value); + case 'contains': return String(val).includes(value); + case '===': return val === value; + case '!==': return val !== value; + case '==': return val == value; + case '!=': return val != value; + case '<=': return val <= value; + case '>=': return val >= value; + case '<': return val < value; + case '>': return val > value; + }; + + return true; + }); + }, + + unique: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + if (prop) arr = arr.map(item => item[prop]); + + let lookup = []; + return arr.filter(item => { + if (lookup.includes(item)) return false; + lookup.push(item); + return true; + }); + }, + + groupBy: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((groups, item) => { + let group = String(item[prop]); + if (!groups[group]) groups[group] = []; + groups[group].push(item); + return groups; + }, {}); + }, + + sort: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.slice(0).sort((a, b) => { + a = prop ? a && a[prop] : a; + b = prop ? b && b[prop] : b; + + if (typeof a == 'string' && typeof b == 'string') { + return a.localeCompare(b); + } + + return a < b ? -1 : a > b ? 1 : 0; + }); + }, + + randomize: function(arr) { + if (!Array.isArray(arr)) return arr; + arr = arr.slice(0); + let len = arr.length; + if (len) { + while (--len) { + let rnd = Math.floor(Math.random() * (len + 1)); + let val = arr[len]; + arr[len] = arr[rnd]; + arr[rnd] = val; + } + } + return arr; + }, + + reverse: function(arr) { + if (!Array.isArray(arr)) return arr; + return arr.slice(0).reverse(); + }, + + count: function(arr) { + if (!Array.isArray(arr)) return arr; + return arr.length; + }, + + min: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((min, num) => { + num = prop ? num[prop] : num; + if (num == null) return min; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return min; + return min == null || num < min ? num : min; + }, null); + }, + + max: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((max, num) => { + num = prop ? num[prop] : num; + if (num == null) return max; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return max; + return max == null || num > max ? num : max; + }, null); + }, + + sum: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((sum, num) => { + num = prop ? num[prop] : num; + if (num == null) return sum; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return sum; + return sum + num; + }, 0); + }, + + avg: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + let cnt = 0; + return arr.reduce((avg, num) => { + num = prop ? num[prop] : num; + if (num == null) return avg; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return avg; + cnt++; + return avg + num; + }, 0) / cnt; + }, + + flatten: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.map(item => item[prop]); + }, + + keys: function(obj) { + if (typeof obj != 'object') return obj; + return Object.keys(obj); + }, + + values: function(obj) { + if (typeof obj != 'object') return obj; + return Object.values(obj); + }, + +}; \ No newline at end of file diff --git a/lib/formatters/conditional.js b/lib/formatters/conditional.js new file mode 100644 index 0000000..9ef50d0 --- /dev/null +++ b/lib/formatters/conditional.js @@ -0,0 +1,30 @@ +module.exports = { + + startsWith: function(val, str) { + if (val == null) return false; + return String(val).startsWith(str); + }, + + endsWith: function(val, str) { + if (val == null) return false; + return String(val).endsWith(str); + }, + + contains: function(val, str) { + if (val == null) return false; + return String(val).includes(str); + }, + + between: function(val, min, max) { + return val >= min && val <= max; + }, + + inRange: function(val, min, max) { + val = Number(val); + min = Number(min); + max = Number(max); + + return val >= min && val <= max; + }, + +}; \ No newline at end of file diff --git a/lib/formatters/core.js b/lib/formatters/core.js new file mode 100644 index 0000000..23e1755 --- /dev/null +++ b/lib/formatters/core.js @@ -0,0 +1,27 @@ +module.exports = { + + default: function(val, def) { + return val ? val : def; + }, + + then: function(val, trueVal, falseVal) { + return val ? trueVal : falseVal; + }, + + toNumber: function(val) { + return Number(val); + }, + + toString: function(val) { + return String(val); + }, + + toJSON: function(val) { + return JSON.stringify(val); + }, + + parseJSON: function(val) { + return JSON.parse(val); + }, + +}; \ No newline at end of file diff --git a/lib/formatters/crypto.js b/lib/formatters/crypto.js new file mode 100644 index 0000000..b6673da --- /dev/null +++ b/lib/formatters/crypto.js @@ -0,0 +1,99 @@ +const { createHash, createHmac, createCipheriv, createDecipheriv, randomBytes, scryptSync, randomUUID } = require('crypto'); +const { v4: uuidv4 } = require('uuid'); + +module.exports = { + + randomUUID: function() { + return randomUUID ? randomUUID() : uuidv4(); + }, + + md5: function(data, salt, enc) { + return hash('md5', data, salt, enc) + }, + + sha1: function(data, salt, enc) { + return hash('sha1', data, salt, enc) + }, + + sha256: function(data, salt, enc) { + return hash('sha256', data, salt, enc) + }, + + sha512: function(data, salt, enc) { + return hash('sha512', data, salt, enc) + }, + + hash: function(data, alg, enc) { + return hash(alg, data, '', enc); + }, + + hmac: function(data, alg, secret, enc) { + return hmac(alg, data, secret, enc); + }, + + transform: function(data, from, to) { + return transform(data, from, to); + }, + + encodeBase64: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data, enc, 'base64'); + }, + + decodeBase64: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data, 'base64', enc); + }, + + encodeBase64Url: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data, enc, 'base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); + }, + + decodeBase64Url: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data.replace(/-/g, '+').replace(/_/g, '/'), 'base64', enc); + }, + + encrypt: function(data, password, enc) { + const config = require('../setup/config'); + password = typeof password == 'string' ? password : config.secret; + enc = typeof enc == 'string' ? enc : 'base64'; + const iv = randomBytes(16); + const key = scryptSync(password, iv, 32); + const cipher = createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(data, 'utf8', enc); + return iv.toString(enc) + '.' + encrypted + cipher.final(enc); + }, + + decrypt: function(data, password, enc) { + const config = require('../setup/config'); + password = typeof password == 'string' ? password : config.secret; + enc = typeof enc == 'string' ? enc : 'base64'; + const iv = Buffer.from(data.split('.')[0], enc); + const key = scryptSync(password, iv, 32); + const decipher = createDecipheriv('aes-256-cbc', key, iv); + const decrypted = decipher.update(data.split('.')[1], enc, 'utf8'); + return decrypted + decipher.final('utf8'); + }, + +}; + +function hash(alg, data, salt, enc) { + if (data == null) return data; + salt = typeof salt == 'string' ? salt : ''; + enc = typeof enc == 'string' ? enc : 'hex'; + return createHash(alg).update(data + salt).digest(enc); +} + +function hmac(alg, data, secret, enc) { + if (data == null) return data; + secret = typeof secret == 'string' ? secret : ''; + enc = typeof enc == 'string' ? enc : 'hex'; + return createHmac(alg, secret).update(data).digest(enc); +} + +function transform(data, from, to) { + if (data == null) return data; + return Buffer.from(String(data), from).toString(to); +} \ No newline at end of file diff --git a/lib/formatters/date.js b/lib/formatters/date.js new file mode 100644 index 0000000..f7c8c97 --- /dev/null +++ b/lib/formatters/date.js @@ -0,0 +1,83 @@ +const { padNumber, parseDate, formatDate } = require('../core/util'); + +module.exports = { + + formatDate: function(date, format, utc) { + date = parseDate(date); + if (date == null) return date; + + return formatDate(date, format, utc); + }, + + dateAdd: function(date, interval, num) { + date = parseDate(date); + if (date == null) return date; + + switch (interval) { + case 'years': date.setFullYear(date.getFullYear() + num); break; + case 'months': date.setMonth(date.getMonth() + num); break; + case 'weeks': date.setDate(date.getDate() + (num * 7)); break; + case 'days': date.setDate(date.getDate() + num); break; + case 'hours': date.setHours(date.getHours() + num); break; + case 'minutes': date.setMinutes(date.getMinutes() + num); break; + case 'seconds': date.setSeconds(date.getSeconds() + num); break; + } + + return date.toISOString(); + }, + + dateDiff: function(date, interval, date2) { + date = parseDate(date); + date2 = parseDate(date2); + + let diff = Math.abs(date2 - date); + + if (isNaN(diff) || date == null || date2 == null) return undefined; + + let s = 1000, m = s * 60, h = m * 60, d = h * 24, w = d * 7; + + switch (interval) { + case 'years': return Math.abs(date2.getFullYear() - date.getFullYear()); + case 'months': return Math.abs((date2.getFullYear() * 12 + date2.getMonth()) - (date.getFullYear() * 12 + date.getMonth())); + case 'weeks': return Math.floor(diff / w); + case 'days': return Math.floor(diff / d); + case 'hours': return Math.floor(diff/ h); + case 'minutes': return Math.floor(diff / m); + case 'seconds': return Math.floor(diff / s); + case 'hours:minutes': + h = Math.floor(diff / h); + m = Math.floor(diff / m); + return padNumber(h, 2) + ':' + padNumber(m - (h * 60), 2); + case 'minutes:seconds': + m = Math.floor(diff / m); + s = Math.floor(diff / s); + return padNumber(m, 2) + ':' + padNumber(s - (m * 60), 2); + case 'hours:minutes:seconds': + h = Math.floor(diff / h); + m = Math.floor(diff / m); + s = Math.floor(diff / s); + return padNumber(h, 2) + ':' + padNumber(m - (h * 60), 2) + ':' + padNumber(s - m * 60, 2); + } + + return undefined; + }, + + toTimestamp: function(date) { + date = parseDate(date); + if (date == null) return date; + return Math.floor(date.getTime() / 1000); + }, + + toLocalTime: function(date) { + date = parseDate(date); + if (date == null) return date; + return formatDate(date, 'yyyy-MM-dd HH:mm:ss.v'); + }, + + toUTCTime: function(date) { + date = parseDate(date); + if (date == null) return date; + return date.toISOString(); + }, + +}; \ No newline at end of file diff --git a/lib/formatters/index.js b/lib/formatters/index.js new file mode 100644 index 0000000..316458c --- /dev/null +++ b/lib/formatters/index.js @@ -0,0 +1,17 @@ +const collections = require('./collections'); +const conditional = require('./conditional'); +const core = require('./core'); +const crypto = require('./crypto'); +const date = require('./date'); +const number = require('./number'); +const string = require('./string'); + +module.exports = { + ...collections, + ...conditional, + ...core, + ...crypto, + ...date, + ...number, + ...string +}; \ No newline at end of file diff --git a/lib/formatters/number.js b/lib/formatters/number.js new file mode 100644 index 0000000..4ae2d4b --- /dev/null +++ b/lib/formatters/number.js @@ -0,0 +1,73 @@ +const { padNumber, formatNumber } = require('../core/util'); + +module.exports = { + + floor: function(num) { + return Math.floor(Number(num)); + }, + + ceil: function(num) { + return Math.ceil(Number(num)); + }, + + round: function(num) { + return Math.round(Number(num)); + }, + + abs: function(num) { + return Math.abs(Number(num)); + }, + + pow: function(num, exp) { + return Math.pow(num, exp); + }, + + padNumber: function(num, digids) { + num = Number(num); + + if (isNaN(num) || !isFinite(num)) return 'NaN'; + + return padNumber(num, digids); + }, + + formatNumber: function(num, decimals, decimalSeparator, groupingSeparator) { + return formatNumber(num, decimals, decimalSeparator, groupingSeparator); + }, + + hex: function(num) { + return parseInt(String(num), 16) || NaN; + }, + + currency: function(num, unit, decimalSeparator, groupingSeparator, decimals) { + unit = typeof unit == 'string' ? unit : '$'; + decimalSeparator = typeof decimalSeparator == 'string' ? decimalSeparator : '.'; + groupingSeparator = typeof groupingSeparator == 'string' ? groupingSeparator : ','; + decimals = typeof decimals == 'number' ? Math.abs(decimals) : 2; + + let formatted = formatNumber(num, decimals, decimalSeparator, groupingSeparator); + let minus = formatted[0] == '-'; + + return (minus ? '-' : '') + unit + formatted.replace(/^\-/, ''); + }, + + formatSize: function(num, decimals, binary) { + num = Number(num); + + if (isNaN(num) || !isFinite(num)) return 'NaN'; + + decimals = typeof decimals == 'number' ? Math.abs(decimals) : 2; + + let base = binary ? 1024 : 1000; + let suffix = binary ? ['KiB', 'MiB', 'GiB', 'TiB'] : ['kB', 'MB', 'GB', 'TB']; + + for (let i = 3; i >= 0; i--) { + let n = Math.pow(base, i + 1); + if (num >= n) { + return formatNumber(num / n, decimals) + suffix[i]; + } + } + + return num + 'B'; + }, + +}; \ No newline at end of file diff --git a/lib/formatters/string.js b/lib/formatters/string.js new file mode 100644 index 0000000..18499ea --- /dev/null +++ b/lib/formatters/string.js @@ -0,0 +1,210 @@ +const { escapeRegExp } = require('../core/util'); + +module.exports = { + + lowercase: function(str) { + if (str == null) return str; + + return String(str).toLowerCase(); + }, + + uppercase: function(str) { + if (str == null) return str; + + return String(str).toUpperCase(); + }, + + camelize: function(str) { + if (str == null) return str; + + return String(str) + .trim() + .replace(/(\-|_|\s)+(.)?/g, (a, b, c) => c ? c.toUpperCase() : ''); + }, + + capitalize: function(str) { + if (str == null) return str; + + str = String(str); + + return str.charAt(0).toUpperCase() + str.substr(1); + }, + + dasherize: function(str) { + if (str == null) return str; + + return String(str) + .replace(/[_\s]+/g, '-') + .replace(/([A-Z])/g, '-$1') + .replace(/-+/g, '-') + .toLowerCase(); + }, + + humanize: function(str) { + if (str == null) return str; + + str = String(str) + .trim() + .replace(/([a-z\D])([A-Z]+)/g, '$1_$2') + .replace(/[-\s]+/g, '_') + .toLowerCase() + .replace(/_id$/, '') + .replace(/_/g, ' ') + .trim(); + + return str.charAt(0).toUpperCase() + str.substr(1); + }, + + slugify: function(str) { + if (str == null) return str; + + const map = {"2d":"-","20":"-","24":"s","26":"and","30":"0","31":"1","32":"2","33":"3","34":"4","35":"5","36":"6","37":"7","38":"8","39":"9","41":"A","42":"B","43":"C","44":"D","45":"E","46":"F","47":"G","48":"H","49":"I","50":"P","51":"Q","52":"R","53":"S","54":"T","55":"U","56":"V","57":"W","58":"X","59":"Y","61":"a","62":"b","63":"c","64":"d","65":"e","66":"f","67":"g","68":"h","69":"i","70":"p","71":"q","72":"r","73":"s","74":"t","75":"u","76":"v","77":"w","78":"x","79":"y","100":"A","101":"a","102":"A","103":"a","104":"A","105":"a","106":"C","107":"c","108":"C","109":"c","110":"D","111":"d","112":"E","113":"e","114":"E","115":"e","116":"E","117":"e","118":"E","119":"e","120":"G","121":"g","122":"G","123":"g","124":"H","125":"h","126":"H","127":"h","128":"I","129":"i","130":"I","131":"i","132":"IJ","133":"ij","134":"J","135":"j","136":"K","137":"k","138":"k","139":"L","140":"l","141":"L","142":"l","143":"N","144":"n","145":"N","146":"n","147":"N","148":"n","149":"n","150":"O","151":"o","152":"OE","153":"oe","154":"R","155":"r","156":"R","157":"r","158":"R","159":"r","160":"S","161":"s","162":"T","163":"t","164":"T","165":"t","166":"T","167":"t","168":"U","169":"u","170":"U","171":"u","172":"U","173":"u","174":"W","175":"w","176":"Y","177":"y","178":"Y","179":"Z","180":"b","181":"B","182":"b","183":"b","184":"b","185":"b","186":"C","187":"C","188":"c","189":"D","190":"E","191":"F","192":"f","193":"G","194":"Y","195":"h","196":"i","197":"I","198":"K","199":"k","200":"A","201":"a","202":"A","203":"a","204":"E","205":"e","206":"E","207":"e","208":"I","209":"i","210":"R","211":"r","212":"R","213":"r","214":"U","215":"u","216":"U","217":"u","218":"S","219":"s","220":"n","221":"d","222":"8","223":"8","224":"Z","225":"z","226":"A","227":"a","228":"E","229":"e","230":"O","231":"o","232":"Y","233":"y","234":"l","235":"n","236":"t","237":"j","238":"db","239":"qp","240":"<","241":"?","242":"?","243":"B","244":"U","245":"A","246":"E","247":"e","248":"J","249":"j","250":"a","251":"a","252":"a","253":"b","254":"c","255":"e","256":"d","257":"d","258":"e","259":"e","260":"g","261":"g","262":"g","263":"Y","264":"x","265":"u","266":"h","267":"h","268":"i","269":"i","270":"w","271":"m","272":"n","273":"n","274":"N","275":"o","276":"oe","277":"m","278":"o","279":"r","280":"R","281":"R","282":"S","283":"f","284":"f","285":"f","286":"f","287":"t","288":"t","289":"u","290":"Z","291":"Z","292":"3","293":"3","294":"?","295":"?","296":"5","297":"C","298":"O","299":"B","363":"a","364":"e","365":"i","366":"o","367":"u","368":"c","369":"d","386":"A","388":"E","389":"H","390":"i","391":"A","392":"B","393":"r","394":"A","395":"E","396":"Z","397":"H","398":"O","399":"I","400":"E","401":"E","402":"T","403":"r","404":"E","405":"S","406":"I","407":"I","408":"J","409":"jb","410":"A","411":"B","412":"V","413":"G","414":"D","415":"E","416":"ZH","417":"Z","418":"I","419":"Y","420":"R","421":"S","422":"T","423":"U","424":"F","425":"H","426":"TS","427":"CH","428":"SH","429":"SCH","430":"a","431":"b","432":"v","433":"g","434":"d","435":"e","436":"zh","437":"z","438":"i","439":"y","440":"r","441":"s","442":"t","443":"u","444":"f","445":"h","446":"ts","447":"ch","448":"sh","449":"sch","450":"e","451":"e","452":"h","453":"r","454":"e","455":"s","456":"i","457":"i","458":"j","459":"jb","460":"W","461":"w","462":"Tb","463":"tb","464":"IC","465":"ic","466":"A","467":"a","468":"IA","469":"ia","470":"Y","471":"y","472":"O","473":"o","474":"V","475":"v","476":"V","477":"v","478":"Oy","479":"oy","480":"C","481":"c","490":"R","491":"r","492":"F","493":"f","494":"H","495":"h","496":"X","497":"x","498":"3","499":"3","500":"d","501":"d","502":"d","503":"d","504":"R","505":"R","506":"R","507":"R","508":"JT","509":"JT","510":"E","511":"e","512":"JT","513":"jt","514":"JX","515":"JX","531":"U","532":"D","533":"Q","534":"N","535":"T","536":"2","537":"F","538":"r","539":"p","540":"z","541":"2","542":"n","543":"x","544":"U","545":"B","546":"j","547":"t","548":"n","549":"C","550":"R","551":"8","552":"R","553":"O","554":"P","555":"O","556":"S","561":"w","562":"f","563":"q","564":"n","565":"t","566":"q","567":"t","568":"n","569":"p","570":"h","571":"a","572":"n","573":"a","574":"u","575":"j","576":"u","577":"2","578":"n","579":"2","580":"n","581":"g","582":"l","583":"uh","584":"p","585":"o","586":"S","587":"u","4a":"J","4b":"K","4c":"L","4d":"M","4e":"N","4f":"O","5a":"Z","6a":"j","6b":"k","6c":"l","6d":"m","6e":"n","6f":"o","7a":"z","a2":"c","a3":"f","a5":"Y","a7":"s","a9":"c","aa":"a","ae":"r","b2":"2","b3":"3","b5":"u","b6":"p","b9":"1","c0":"A","c1":"A","c2":"A","c3":"A","c4":"A","c5":"A","c6":"AE","c7":"C","c8":"E","c9":"E","ca":"E","cb":"E","cc":"I","cd":"I","ce":"I","cf":"I","d0":"D","d1":"N","d2":"O","d3":"O","d4":"O","d5":"O","d6":"O","d7":"X","d8":"O","d9":"U","da":"U","db":"U","dc":"U","dd":"Y","de":"p","df":"b","e0":"a","e1":"a","e2":"a","e3":"a","e4":"a","e5":"a","e6":"ae","e7":"c","e8":"e","e9":"e","ea":"e","eb":"e","ec":"i","ed":"i","ee":"i","ef":"i","f0":"o","f1":"n","f2":"o","f3":"o","f4":"o","f5":"o","f6":"o","f8":"o","f9":"u","fa":"u","fb":"u","fc":"u","fd":"y","ff":"y","10a":"C","10b":"c","10c":"C","10d":"c","10e":"D","10f":"d","11a":"E","11b":"e","11c":"G","11d":"g","11e":"G","11f":"g","12a":"I","12b":"i","12c":"I","12d":"i","12e":"I","12f":"i","13a":"l","13b":"L","13c":"l","13d":"L","13e":"l","13f":"L","14a":"n","14b":"n","14c":"O","14d":"o","14e":"O","14f":"o","15a":"S","15b":"s","15c":"S","15d":"s","15e":"S","15f":"s","16a":"U","16b":"u","16c":"U","16d":"u","16e":"U","16f":"u","17a":"z","17b":"Z","17c":"z","17d":"Z","17e":"z","17f":"f","18a":"D","18b":"d","18c":"d","18d":"q","18e":"E","18f":"e","19a":"l","19b":"h","19c":"w","19d":"N","19e":"n","19f":"O","1a0":"O","1a1":"o","1a2":"P","1a3":"P","1a4":"P","1a5":"p","1a6":"R","1a7":"S","1a8":"s","1a9":"E","1aa":"l","1ab":"t","1ac":"T","1ad":"t","1ae":"T","1af":"U","1b0":"u","1b1":"U","1b2":"U","1b3":"Y","1b4":"y","1b5":"Z","1b6":"z","1b7":"3","1b8":"3","1b9":"3","1ba":"3","1bb":"2","1bc":"5","1bd":"5","1be":"5","1bf":"p","1c4":"DZ","1c5":"Dz","1c6":"dz","1c7":"Lj","1c8":"Lj","1c9":"lj","1ca":"NJ","1cb":"Nj","1cc":"nj","1cd":"A","1ce":"a","1cf":"I","1d0":"i","1d1":"O","1d2":"o","1d3":"U","1d4":"u","1d5":"U","1d6":"u","1d7":"U","1d8":"u","1d9":"U","1da":"u","1db":"U","1dc":"u","1dd":"e","1de":"A","1df":"a","1e0":"A","1e1":"a","1e2":"AE","1e3":"ae","1e4":"G","1e5":"g","1e6":"G","1e7":"g","1e8":"K","1e9":"k","1ea":"Q","1eb":"q","1ec":"Q","1ed":"q","1ee":"3","1ef":"3","1f0":"J","1f1":"dz","1f2":"dZ","1f3":"DZ","1f4":"g","1f5":"G","1f6":"h","1f7":"p","1f8":"N","1f9":"n","1fa":"A","1fb":"a","1fc":"AE","1fd":"ae","1fe":"O","1ff":"o","20a":"I","20b":"i","20c":"O","20d":"o","20e":"O","20f":"o","21a":"T","21b":"t","21c":"3","21d":"3","21e":"H","21f":"h","22a":"O","22b":"o","22c":"O","22d":"o","22e":"O","22f":"o","23a":"A","23b":"C","23c":"c","23d":"L","23e":"T","23f":"s","24a":"Q","24b":"q","24c":"R","24d":"r","24e":"Y","24f":"y","25a":"e","25b":"3","25c":"3","25d":"3","25e":"3","25f":"j","26a":"i","26b":"I","26c":"I","26d":"I","26e":"h","26f":"w","27a":"R","27b":"r","27c":"R","27d":"R","27e":"r","27f":"r","28a":"u","28b":"v","28c":"A","28d":"M","28e":"Y","28f":"Y","29a":"B","29b":"G","29c":"H","29d":"j","29e":"K","29f":"L","2a0":"q","2a1":"?","2a2":"c","2a3":"dz","2a4":"d3","2a5":"dz","2a6":"ts","2a7":"tf","2a8":"tc","2a9":"fn","2aa":"ls","2ab":"lz","2ac":"ww","2ae":"u","2af":"u","2b0":"h","2b1":"h","2b2":"j","2b3":"r","2b4":"r","2b5":"r","2b6":"R","2b7":"W","2b8":"Y","2df":"x","2e0":"Y","2e1":"1","2e2":"s","2e3":"x","2e4":"c","36a":"h","36b":"m","36c":"r","36d":"t","36e":"v","36f":"x","37b":"c","37c":"c","37d":"c","38a":"I","38c":"O","38e":"Y","38f":"O","39a":"K","39b":"A","39c":"M","39d":"N","39e":"E","39f":"O","3a0":"TT","3a1":"P","3a3":"E","3a4":"T","3a5":"Y","3a6":"O","3a7":"X","3a8":"Y","3a9":"O","3aa":"I","3ab":"Y","3ac":"a","3ad":"e","3ae":"n","3af":"i","3b0":"v","3b1":"a","3b2":"b","3b3":"y","3b4":"d","3b5":"e","3b6":"c","3b7":"n","3b8":"0","3b9":"1","3ba":"k","3bb":"j","3bc":"u","3bd":"v","3be":"c","3bf":"o","3c0":"tt","3c1":"p","3c2":"s","3c3":"o","3c4":"t","3c5":"u","3c6":"q","3c7":"X","3c8":"Y","3c9":"w","3ca":"i","3cb":"u","3cc":"o","3cd":"u","3ce":"w","3d0":"b","3d1":"e","3d2":"Y","3d3":"Y","3d4":"Y","3d5":"O","3d6":"w","3d7":"x","3d8":"Q","3d9":"q","3da":"C","3db":"c","3dc":"F","3dd":"f","3de":"N","3df":"N","3e2":"W","3e3":"w","3e4":"q","3e5":"q","3e6":"h","3e7":"e","3e8":"S","3e9":"s","3ea":"X","3eb":"x","3ec":"6","3ed":"6","3ee":"t","3ef":"t","3f0":"x","3f1":"e","3f2":"c","3f3":"j","3f4":"O","3f5":"E","3f6":"E","3f7":"p","3f8":"p","3f9":"C","3fa":"M","3fb":"M","3fc":"p","3fd":"C","3fe":"C","3ff":"C","40a":"Hb","40b":"Th","40c":"K","40d":"N","40e":"Y","40f":"U","41a":"K","41b":"L","41c":"M","41d":"N","41e":"O","41f":"P","42a":"","42b":"Y","42c":"","42d":"E","42e":"U","42f":"YA","43a":"k","43b":"l","43c":"m","43d":"n","43e":"o","43f":"p","44a":"","44b":"y","44c":"","44d":"e","44e":"u","44f":"ya","45a":"Hb","45b":"h","45c":"k","45d":"n","45e":"y","45f":"u","46a":"mY","46b":"my","46c":"Im","46d":"Im","46e":"3","46f":"3","47a":"O","47b":"o","47c":"W","47d":"w","47e":"W","47f":"W","48a":"H","48b":"H","48c":"B","48d":"b","48e":"P","48f":"p","49a":"K","49b":"k","49c":"K","49d":"k","49e":"K","49f":"k","4a0":"K","4a1":"k","4a2":"H","4a3":"h","4a4":"H","4a5":"h","4a6":"Ih","4a7":"ih","4a8":"O","4a9":"o","4aa":"C","4ab":"c","4ac":"T","4ad":"t","4ae":"Y","4af":"y","4b0":"Y","4b1":"y","4b2":"X","4b3":"x","4b4":"TI","4b5":"ti","4b6":"H","4b7":"h","4b8":"H","4b9":"h","4ba":"H","4bb":"h","4bc":"E","4bd":"e","4be":"E","4bf":"e","4c0":"I","4c1":"X","4c2":"x","4c3":"K","4c4":"k","4c5":"jt","4c6":"jt","4c7":"H","4c8":"h","4c9":"H","4ca":"h","4cb":"H","4cc":"h","4cd":"M","4ce":"m","4cf":"l","4d0":"A","4d1":"a","4d2":"A","4d3":"a","4d4":"AE","4d5":"ae","4d6":"E","4d7":"e","4d8":"e","4d9":"e","4da":"E","4db":"e","4dc":"X","4dd":"X","4de":"3","4df":"3","4e0":"3","4e1":"3","4e2":"N","4e3":"n","4e4":"N","4e5":"n","4e6":"O","4e7":"o","4e8":"O","4e9":"o","4ea":"O","4eb":"o","4ec":"E","4ed":"e","4ee":"Y","4ef":"y","4f0":"Y","4f1":"y","4f2":"Y","4f3":"y","4f4":"H","4f5":"h","4f6":"R","4f7":"r","4f8":"bI","4f9":"bi","4fa":"F","4fb":"f","4fc":"X","4fd":"x","4fe":"X","4ff":"x","50a":"H","50b":"h","50c":"G","50d":"g","50e":"T","50f":"t","51a":"Q","51b":"q","51c":"W","51d":"w","53a":"d","53b":"r","53c":"L","53d":"Iu","53e":"O","53f":"y","54a":"m","54b":"o","54c":"N","54d":"U","54e":"Y","54f":"S","56a":"d","56b":"h","56c":"l","56d":"lu","56e":"d","56f":"y","57a":"w","57b":"2","57c":"n","57d":"u","57e":"y","57f":"un"}; + + let clean = ''; + for (let i = 0; i < str.length; i++) { + clean += map[str.charCodeAt(i).toString(16)] || ''; + } + + return clean.toLowerCase().replace(/-+/g, '-').replace(/^-|-$/, ''); + }, + + underscore: function(str) { + if (str == null) return str; + + return String(str) + .trim() + .replace(/([a-z\d])([A-Z]+)/g, '$1_$2') + .replace(/[-\s]+/g, '_') + .toLowerCase(); + }, + + titlecase: function(str) { + if (str == null) return str; + + return String(str) + .replace(/(?:^|\s)\S/g, a => a.toUpperCase()); + }, + + camelcase: function(str) { + if (str == null) return str; + + return String(str) + .toLowerCase() + .replace(/\s+(\S)/g, (a, b) => b.toUpperCase()); + }, + + replace: function(str, search, value) { + if (str == null) return str; + + if (typeof search == 'string') { + search = new RegExp(escapeRegExp(search), 'g'); + } + + return String(str).replace(search, value); + }, + + trim: function(str) { + if (str == null) return str; + + return String(str).trim(); + }, + + split: function(str, separator) { + if (str == null) return []; + + return String(str).split(separator); + }, + + pad: function(str, length, chr, pos) { + if (str == null) return str; + + str = String(str); + chr = chr && typeof chr == 'string' ? chr[0] : ' '; + + if (str.length < length) { + let n = length - str.length; + + switch (pos) { + case 'right': + str += chr.repeat(n); + break; + + case 'center': + str = chr.repeat(Math.floor(n / 2)) + str + chr.repeat(Math.ceil(n / 2)); + break; + + default: + str = chr.repeat(n) + str; + } + } + + return str; + }, + + repeat: function(str, num) { + if (str == null) return str; + + return String(str).repeat(num); + }, + + substr: function(str, start, length) { + if (str == null) return str; + + return String(str).substr(start, length); + }, + + trunc: function(str, length, useWordBoundary, fragment) { + if (str == null) return str; + + str = String(str); + length = Number(length); + fragment = fragment && typeof fragment == 'string' ? fragment : '\u2026'; + + if (str.length > length) { + str = str.substr(0, length - fragment.length); + + if (useWordBoundary && str.includes(' ')) { + str = str.substr(0, str.lastIndexOf(' ')); + } + + str += fragment; + } + + return str; + }, + + stripTags: function(str) { + if (str == null) return str; + + return String(str).replace(/<[^>]+>/g, ''); + }, + + wordCount: function(str) { + if (str == null) return 0; + + return String(str) + .replace(/\.\?!,/g, ' ') + .trim() + .split(/\s+/) + .length; + }, + + length: function(str) { + if (str == null) return 0; + + return String(str).length; + }, + + urlencode: function(str) { + if (str == null) return str; + + return encodeURI(String(str)); + }, + + urldecode: function(str) { + if (str == null) return str; + + return decodeURI(String(str)); + }, + +}; \ No newline at end of file diff --git a/lib/locale/en-US.js b/lib/locale/en-US.js new file mode 100644 index 0000000..792513c --- /dev/null +++ b/lib/locale/en-US.js @@ -0,0 +1,59 @@ +module.exports = { + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + + validator: { + core: { + required: 'This field is required.', + email: 'Please enter a valid email address.', + url: 'Please enter a valid URL.', + date: 'Please enter a valid date.', + time: 'Please enter a valid time.', + month: 'Please enter a valid month.', + week: 'Please enter a valid week.', + color: 'Please enter a color in the format #xxxxxx.', + pattern: 'Invalid format.', + number: 'Please enter a valid number.', + digits: 'Please enter only digits.', + alphanumeric: 'Letters, numbers and underscores only please.', + creditcard: 'Please enter a valid credit card number.', + bic: 'Please specify a valid BIC code.', + iban: 'Please specify a valid IBAN.', + vat: 'Please specify a valid VAT number.', + integer: 'A positive or negative non-decimal number please.', + ipv4: 'Please enter a valid IP v4 address.', + ipv6: 'Please enter a valid IP v6 address.', + lettersonly: 'Letters only please.', + unicodelettersonly: 'Letters only please.', + letterswithbasicpunc: 'Letters or punctuation only please.', + nowhitespace: 'No whitespace please.', + minlength: 'Please enter at least {0} characters.', + maxlength: 'Please enter no more than {0} characters.', + minitems: 'Please select at least {0} items.', + maxitems: 'Please select no more than {0} items.', + min: 'Please enter a value greater than or equal to {0}.', + max: 'Please enter a value less than or equal to {0}.', + equalTo: 'Please enter the same value again.', + notEqualTo: 'Please enter a different value, values must not be the same.', + }, + db: { + exists: 'Value does not exist in database.', + notexists: 'Value already exists in database.', + }, + unicode: { + unicodelettersonly: 'Entered characters are not allowed.', + unicodescripts: 'Entered characters are not allowed.', + }, + upload: { + accept: 'Please select a file with a valid file type.', + minsize: 'Please select a file of at least {0} bytes.', + maxsize: 'Please select a file of no more than {0} bytes.', + mintotalsize: 'Total size of selected files should be at least {0} bytes.', + maxtotalsize: 'Total size of selected files should be no more than {0} bytes.', + minfiles: 'Please select at least {0} files.', + maxfiles: 'Please select no more than {0} files.', + }, + }, +}; \ No newline at end of file diff --git a/lib/modules/api.js b/lib/modules/api.js new file mode 100644 index 0000000..8d24541 --- /dev/null +++ b/lib/modules/api.js @@ -0,0 +1,122 @@ +const { http, https } = require('follow-redirects'); +const querystring = require('querystring'); +const zlib = require('zlib'); +const pkg = require('../../package.json'); + +module.exports = { + + send: async function(options) { + let url = this.parseRequired(options.url, 'string', 'api.send: url is required.'); + let method = this.parseOptional(options.method, 'string', 'GET'); + let data = this.parseOptional(options.data, '*', ''); + let dataType = this.parseOptional(options.dataType, 'string', 'auto'); + let verifySSL = this.parseOptional(options.verifySSL, 'boolean', false); + let params = this.parseOptional(options.params, 'object', null); + let headers = this.parseOptional(options.headers, 'object', {}); + let username = this.parseOptional(options.username, 'string', ''); + let password = this.parseOptional(options.password, 'string', ''); + let oauth = this.parseOptional(options.oauth, 'string', ''); + let throwErrors = this.parseOptional(options.throwErrors, 'boolean', false); + let passErrors = this.parseOptional(options.passErrors, 'boolean', true); + let timeout = this.parseOptional(options.timeout, 'number', 0); + + if (params) { + url += '?' + querystring.stringify(params); + } + + if (dataType == 'auto' && method == 'POST') { + dataType = 'x-www-form-urlencoded'; + } + + if (dataType != 'auto' && !headers['Content-Type']) { + headers['Content-Type'] = `application/${dataType}`; + } + + if (dataType == 'x-www-form-urlencoded') { + data = querystring.stringify(data); + } else if (typeof data != 'string') { + data = JSON.stringify(data); + } + + if (data) { + headers['Content-Length'] = Buffer.byteLength(data); + } + + const Url = new URL(url); + const opts = { method, headers, rejectUnauthorized: !!verifySSL, maxBodyLength: 1_000_000_000 }; + + if (timeout > 0) { + opts.timeout = timeout; + } + + if (username || password) { + opts.auth = `${username}:${password}`; + } + + if (oauth) { + //const provider = this.oauth[oauth]; + const provider = await this.getOAuthProvider(oauth); + if (provider && provider.access_token) { + headers['Authorization'] = 'Bearer ' + provider.access_token; + } + } + + if (!headers['User-Agent']) headers['User-Agent'] = `${pkg.name}/${pkg.version}`; + if (!headers['Accept']) headers['Accept'] = 'application/json'; + + return new Promise((resolve, reject) => { + const req = (Url.protocol == 'https:' ? https : http).request(Url, opts, res => { + let body = ''; + + let output = res; + if (res.headers['content-encoding'] == 'br') { + output = res.pipe(zlib.createBrotliDecompress()); + } + if (res.headers['content-encoding'] == 'gzip') { + output = res.pipe(zlib.createGunzip()); + } + if (res.headers['content-encoding'] == 'deflate') { + output = res.pipe(zlib.createInflate()); + } + + output.setEncoding('utf8'); + output.on('data', chunk => body += chunk); + output.on('end', () => { + if (res.statusCode >= 400) { + if (throwErrors) { + return reject(res.statusCode + ' ' + body); + } + + if (passErrors) { + this.res.status(res.statusCode).send(body); + return resolve(); + } + } + + if (body.charCodeAt(0) === 0xFEFF) { + body = body.slice(1); + } + + if (res.headers['content-type'] && res.headers['content-type'].includes('json')) { + try { + body = JSON.parse(body); + } catch(e) { + console.error(e); + } + } + + resolve({ + status: res.statusCode, + headers: res.headers, + data: body + }); + }); + }); + + req.on('error', reject); + req.write(data); + req.end(); + }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/arraylist.js b/lib/modules/arraylist.js new file mode 100644 index 0000000..79e2fa5 --- /dev/null +++ b/lib/modules/arraylist.js @@ -0,0 +1,173 @@ +// ArrayList + +const { clone } = require('../core/util'); + +// Create a new ArrayList +exports.create = function(options, name) { + const value = this.parseOptional(options.value, 'object', []); + + if (!name) throw Error('arraylist.create: name is required.'); + + if (!this.req.arrays) { + this.req.arrays = {}; + } + + this.req.arrays[name] = Array.isArray(value) ? clone(value) : []; +}; + +// Return the ArrayList as array value +exports.value = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.value: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.value: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.value: ArrayList ${ref} not found.`); + + return clone(this.req.arrays[ref]); +}; + +// Return the size of the ArrayList +exports.size = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.size: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.size: No arraylists are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.size: ArrayList ${ref} not found.`); + + return this.req.arrays[ref].length; +}; + +// Return the value at a specific index +exports.get = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.get: ref is required.'); + const index = this.parseRequired(options.index, 'number', 'arraylist.get: index is required.'); + + if (!this.req.arrays) throw Error('arraylist.get: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.get: ArrayList ${ref} not found.`); + + return clone(this.req.arrays[ref][index]); +}; + +// Add a value to the ArrayList +exports.add = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.add: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.add: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.add: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.add: ArrayList ${ref} not found.`); + + this.req.arrays[ref].push(clone(value)); +}; + +// Add an array of values to the ArrayList +exports.addAll = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.addAll: ref is required.'); + const value = this.parseRequired(options.value, 'object', 'arraylist.addAll: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.addAll: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.addAll: ArrayList ${ref} not found.`); + if (!Array.isArray(value)) throw Error('arraylist.addAll: Value must be an array.') + + for (let val of value) { + this.req.arrays[ref].push(clone(val)); + } +}; + +// Update a value at a specific index +exports.set = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.set: ref is required.'); + const index = this.parseRequired(options.index, 'number', 'arraylist.set: index is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.set: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.set: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.set: ArrayList ${ref} not found.`); + + this.req.arrays[ref][index] = clone(value); +}; + +// Remove the first occurrence of a specific value +exports.remove = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.remove: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.remove: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.remove: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.remove: ArrayList ${ref} not found.`); + + const index = this.req.arrays[ref].indexOf(value); + + if (index == -1) throw Error('arraylist.remove: Value does not exist in the ArrayList.'); + + this.req.arrays[ref].splice(index, 1); +}; + +// Remove a value from the ArrayList at a specific index +exports.removeAt = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.removeAt: ref is required.'); + const index = this.parseRequired(options.index, 'number', 'arraylist.removeAt: index is required.'); + + if (!this.req.arrays) throw Error('arraylist.removeAt: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.removeAt: ArrayList ${ref} not found.`); + + this.req.arrays[ref].splice(index, 1); +}; + +// Clear all values from the ArrayList +exports.clear = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.clear: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.clear: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.clear: ArrayList ${ref} not found.`); + + this.req.arrays[ref] = []; +}; + +// Sort the ArrayList +exports.sort = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.sort: ref is required.'); + const prop = this.parseOptional(options.prop, 'string', null); + const desc = this.parseOptional(options.desc, 'boolean', false); + + if (!this.req.arrays) throw Error('arraylist.sort: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.sort: ArrayList ${ref} not found.`); + + this.req.arrays[ref].sort((a, b) => { + if (prop) { + a = a[prop]; + b = b[prop]; + } + + if (a == b) return 0; + if (a < b) return desc ? 1 : -1; + return desc ? -1 : 1; + }); +}; + +// Return the index of the first occurrence of a specific value +exports.indexOf = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.indexOf: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.indexOf: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.indexOf: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.indexOf: ArrayList ${ref} not found.`); + + return this.req.arrays[ref].indexOf(value); +}; + +// Return true if the ArrayList contains a specific value +exports.contains = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.contains: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.contains: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.contains: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.contains: ArrayList ${ref} not found.`); + + return this.req.arrays[ref].includes(value); +}; + +// Return true when ArrayList is empty +exports.isEmpty = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.isEmpty: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.isEmpty: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.isEmpty: ArrayList ${ref} not found.`); + + return !this.req.arrays[ref].length; +}; \ No newline at end of file diff --git a/lib/modules/auth.js b/lib/modules/auth.js new file mode 100644 index 0000000..b572d6f --- /dev/null +++ b/lib/modules/auth.js @@ -0,0 +1,72 @@ +module.exports = { + + provider: async function(options, name) { + const provider = await this.setAuthProvider(name, options); + + return { identity: provider.identity }; + }, + + identify: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.validate: provider is required.')); + + return provider.identity; + }, + + validate: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.validate: provider is required.')); + const { action, username, password, remember } = this.req.body; + + if (action == 'validate') { + return provider.validate(username, password); + } + + if (action == 'login') { + return provider.login(username, password, remember); + } + + if (action == 'logout') { + return provider.logout(); + } + + if (!provider.identity) { + return provider.unauthorized(); + } + }, + + login: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.login: provider is required.')); + const username = this.parseOptional(options.username, 'string', this.parse('{{$_POST.username}}')); + const password = this.parseOptional(options.password, 'string', this.parse('{{$_POST.password}}')); + const remember = this.parseOptional(options.remember, '*', this.parse('{{$_POST.remember}}')); + + return provider.login(username, password, remember); + }, + + logout: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.logout: provider is required.')); + + return provider.logout(); + }, + + restrict: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.restrict: provider is required.')); + + return provider.restrict(this.parse(options)); + }, + + impersonate: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.impersonate: provider is required.')); + const identity = this.parseRequired(options.identity, 'string', 'auth.impersonate: identity is required.'); + + return provider.impersonate(identity); + }, + + verify: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.verify: provider is required.')); + const username = this.parseRequired(options.username, 'string', 'auth.verify: username is required.'); + const password = this.parseRequired(options.password, 'string', 'auth.verify: password is required.'); + + return provider.validate(username, password); + }, + +}; \ No newline at end of file diff --git a/lib/modules/collections.js b/lib/modules/collections.js new file mode 100644 index 0000000..7cec883 --- /dev/null +++ b/lib/modules/collections.js @@ -0,0 +1,185 @@ +const { clone } = require('../core/util'); + +module.exports = { + + addColumns: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.addColumns: collection is required.'); + let add = this.parseRequired(options.add, 'object', 'collections.addColumns: add is required.'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let output = []; + + for (let row of collection) { + let newRow = clone(row); + + for (let column in add) { + if (overwrite || newRow[column] == null) { + newRow[column] = add[column]; + } + } + + output.push(newRow); + } + + return output; + }, + + filterColumns: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.filterColumns: collection is required.'); + let columns = this.parseRequired(options.columns, 'object'/*array[string]*/, 'collections.filterColumns: columns is required.'); + let keep = this.parseOptional(options.keep, 'boolean', false); + let output = []; + + for (let row of collection) { + let newRow = {}; + + for (let column in row) { + if (columns.includes(column)) { + if (keep) { + newRow[column] = clone(row[column]); + } + } else if (!keep) { + newRow[column] = clone(row[column]); + } + } + + output.push(newRow); + } + + return output; + }, + + renameColumns: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.renamecolumns: collection is required.'); + let rename = this.parseRequired(options.rename, 'object', 'collections.renamecolumns: rename is required.'); + let output = []; + + for (let row of collection) { + let newRow = {}; + + for (let column in row) { + newRow[rename[column] || column] = clone(row[column]); + } + + output.push(newRow); + } + + return output; + }, + + fillDown: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.fillDown: collection is required.'); + let columns = this.parseRequired(options.columns, 'object'/*array[string]*/, 'collections.fillDown: columns is required.'); + let output = []; + let values = {}; + + for (let column of columns) { + values[column] = null; + } + + for (let row of collection) { + let newRow = clone(row); + + for (let column in values) { + if (newRow[column] == null) { + newRow[column] = values[column]; + } else { + values[column] = newRow[column]; + } + } + + output.push(newRow); + } + + return output; + }, + + addRows: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.addRows: collection is required.'); + let rows = this.parseRequired(options.rows, 'object'/*array[object]*/, 'collections.addRows: rows is required.'); + + return clone(collection).concat(clone(rows)); + }, + + addRownumbers: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.addRownumbers: collection is required.'); + let column = this.parseOptional(options.column, 'string', 'nr'); + let startAt = this.parseOptional(options.startAt, 'number', 1); + let desc = this.parseOptional(options.desc, 'boolean', false); + let output = []; + let nr = desc ? collection.length + startAt - 1 : startAt; + + for (let row of collection) { + let newRow = clone(row); + + newRow[column] = desc ? nr-- : nr++; + + output.push(newRow); + } + + return output; + }, + + join: function(options) { + let collection1 = this.parseRequired(options.collection1, 'object'/*array[object]*/, 'collections.join: collection1 is required.'); + let collection2 = this.parseRequired(options.collection2, 'object'/*array[object]*/, 'collections.join: collection2 is required.'); + let matches = this.parseRequired(options.matches, 'object', 'collections.join: matches is required.'); + let matchAll = this.parseOptional(options.matchAll, 'boolean', false); + let output = []; + + for (let row1 of collection1) { + let newRow = clone(row1); + + for (let row2 of collection2) { + let join = false; + + for (let match in matches) { + if (row1[match] == row2[matches[match]]) { + join = true; + if (!matchAll) break; + } else if (matchAll) { + join = false; + break; + } + } + + if (join) { + for (let column in row2) { + newRow[column] = clone(row2[column]); + } + break; + } + } + + output.push(newRow); + } + + return output; + }, + + normalize: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.normalize: collection is required.'); + let columns = []; + let output = []; + + for (let row of collection) { + for (let column in row) { + if (!columns.includes(column)) { + columns.push(column); + } + } + } + + for (let row of collection) { + let newRow = {}; + + for (let column of columns) { + newRow[column] = row[column] == null ? null : clone(row[column]); + } + + output.push(newRow); + } + + return output; + }, + +}; \ No newline at end of file diff --git a/lib/modules/core.js b/lib/modules/core.js new file mode 100644 index 0000000..7f509b3 --- /dev/null +++ b/lib/modules/core.js @@ -0,0 +1,273 @@ +const fs = require('fs-extra'); +const { clone } = require('../core/util'); + +module.exports = { + + wait: function(options) { + let delay = this.parseOptional(options.delay, 'number', 1000); + return new Promise(resolve => setTimeout(resolve, delay)) + }, + + log: function(options) { + let message = this.parse(options.message); + console.log(message); + return message; + }, + + repeat: async function(options) { + let repeater = this.parseRequired(options.repeat, '*', 'core.repeater: repeat is required.'); + let outputFilter = this.parseOptional(options.outputFilter, 'string', 'include'); // include/exclude + let outputFields = this.parseOptional(options.outputFields, 'object'/*array[string]*/, []); + let index = 0, data = [], parentData = this.data; + + switch (typeof repeater) { + case 'boolean': + repeater = repeater ? [0] : []; + break; + + case 'string': + repeater = repeater.split(','); + break; + + case 'number': + repeater = (n => { + let a = [], i = 0; + while (i < n) a.push(i++); + return a; + })(repeater); + break; + } + + if (!(Array.isArray(repeater) || repeater instanceof Object)) { + throw new Error('Repeater data is not an array or object'); + } + + for (let key in repeater) { + if (Object.hasOwn(repeater, key)) { + let scope = {}; + this.data = {}; + + if (repeater[key] instanceof Object) { + for (var prop in repeater[key]) { + if (Object.hasOwn(repeater[key], prop)) { + scope[prop] = repeater[key][prop]; + + if (outputFilter == 'exclude') { + if (!outputFields.includes(prop)) { + this.data[prop] = repeater[key][prop]; + } + } else { + if (outputFields.includes(prop)) { + this.data[prop] = repeater[key][prop]; + } + } + } + } + } + + scope.$key = key; + scope.$name = key; + scope.$value = clone(repeater[key]); + scope.$index = index; + scope.$number = index + 1; + scope.$oddeven = index % 2; + + if (repeater[key] == null) { + repeater[key] = {}; + } + + this.scope = this.scope.create(scope, clone(repeater[key])); + await this.exec(options.exec, true); + this.scope = this.scope.parent; + + data.push({ ...this.data }); + + index++; + } + } + + this.data = parentData; + + return data; + }, + + while: async function(options) { + let max = this.parseOptional(options.max, 'number', Number.MAX_SAFE_INTEGER); + let i = 0; + + while (this.parse(options.while)) { + await this.exec(options.exec, true); + if (++i == max) break; + } + }, + + condition: async function(options) { + let condition = this.parse(options.if); + + if (!!condition) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + }, + + conditions: async function(options) { + if (Array.isArray(options.conditions)) { + for (let condition of options.conditions) { + let when = this.parse(condition.when); + + if (!!when) { + return this.exec(condition.then, true); + } + } + } + }, + + select: async function(options) { + let expression = this.parse(options.expression); + + if (Array.isArray(options.cases)) { + for (let item of options.cases) { + let value = this.parse(item.value); + + if (expression === value) { + return this.exec(item.exec, true); + } + } + } + }, + + setvalue: function(options) { + let key = this.parseOptional(options.key, 'string', ''); + let value = this.parse(options.value); + if (key) this.set(key, value); + return value; + }, + + setsession: function(options, name) { + let value = this.parse(options.value); + this.req.session[name] = value; + return value; + }, + + removesession: function(options, name) { + delete this.req.session[name]; + }, + + setcookie: function(options, name) { + options = this.parse(options); + + let cookieOptions = { + domain: options.domain || undefined, + httpOnly: !!options.httpOnly, + maxAge: options.expires === 0 ? undefined : (options.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: options.path || '/', + secure: !!options.secure, + sameSite: options.sameSite || false + }; + + this.setCookie(name, options.value, cookieOptions); + }, + + removecookie: function(options, name) { + options = this.parse(options); + + let cookieOptions = { + domain: options.domain || undefined, + httpOnly: !!options.httpOnly, + maxAge: options.expires === 0 ? undefined : (options.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: options.path || '/', + secure: !!options.secure, + sameSite: !!options.sameSite + }; + + this.removeCookie(name, cookieOptions); + }, + + response: function(options) { + let data = this.parseOptional(options.data, '*', null); + let status = this.parseOptional(options.status, 'number', 200); + let contentType = this.parseOptional(options.contentType, 'string', 'application/json'); + if (contentType != 'application/json') { + this.res.set('Content-Type', contentType); + this.res.status(status).send(data); + } else { + this.res.status(status).json(data); + } + }, + + end: function(options) { + this.res.json(this.data); + }, + + error: function(options) { + let message = this.parseRequired(options.message, 'string', 'core.error: message is required.'); + throw new Error(message); + }, + + redirect: function(options) { + let url = this.parseRequired(options.url, 'string', 'core.redirect: url is required.'); + let status = this.parseOptional(options.status, 'number', 302); + + this.res.redirect(status, url); + }, + + trycatch: async function(options) { + try { + await this.exec(options.try, true); + } catch (error) { + this.scope.set('$_ERROR', error.message); + this.error = false; + if (options.catch) { + await this.exec(options.catch, true); + } + } + }, + + exec: async function(options) { + var data = {}; + + if (options.exec && fs.existsSync(`app/modules/lib/${options.exec}.json`)) { + let parentData = this.data; + this.data = {}; + this.scope = this.scope.create({ $_PARAM: this.parse(options.params) }); + await this.exec(await fs.readJSON(`app/modules/lib/${options.exec}.json`), true); + data = this.data; + this.scope = this.scope.parent; + this.data = parentData; + } else { + throw new Error(`There is no action called '${options.exec}' found in the library.`); + } + + return data; + }, + + group: async function(options, name) { + if (name) { + return this.sub(options.exec); + } else { + return this.exec(options.exec, true); + } + }, + + parallel: async function(options, name) { + let actions = options.exec.steps || options.exec; + if (!Array.isArray(actions)) actions = [actions]; + return await Promise.all(actions.map(exec => { + if (name) { + return this.sub(exec); + } else { + return this.exec(exec, true); + } + })); + }, + + randomUUID: function(options) { + const { randomUUID } = require('crypto'); + const { v4: uuidv4 } = require('uuid'); + return randomUUID ? randomUUID() : uuidv4(); + }, + +}; \ No newline at end of file diff --git a/lib/modules/crypto.js b/lib/modules/crypto.js new file mode 100644 index 0000000..11e274e --- /dev/null +++ b/lib/modules/crypto.js @@ -0,0 +1,31 @@ +// Perhaps use hashy as reference to implement bcrypt and needRehash action +// https://github.com/JsCommunity/hashy + +// supports argon2i, argon2d and argon2id +exports.passwordHash = async function(options) { + const password = this.parseRequired(options.password, 'string', 'crypto.passwordHash: password is required.') + const algo = this.parseOptional(options.algo, 'string', 'argon2i') + const argon2 = require('argon2') + const type = argon2[algo] + return argon2.hash(password, { type }) +} + +exports.passwordVerify = async function(options) { + const password = this.parseRequired(options.password, 'string', 'crypto.passwordVerify: password is required.') + const hash = this.parseRequired(options.hash, 'string', 'crypto.passwordVerify: hash is required.') + const argon2 = require('argon2') + return argon2.verify(hash, password) +} + +exports.passwordNeedsRehash = async function(options) { + const hash = this.parseRequired(options.hash, 'string', 'crypto.passwordNeedsRehash: hash is required.') + const algo = this.parseOptional(options.algo, 'string', 'argon2i') + const argon2 = require('argon2') + return argon2.needsRehash(hash, {}) +} + +exports.uuid = async function(options) { + const { randomUUID } = require('crypto') + const { v4: uuidv4 } = require('uuid') + return randomUUID ? randomUUID() : uuidv4() +} \ No newline at end of file diff --git a/lib/modules/csrf.js b/lib/modules/csrf.js new file mode 100644 index 0000000..d135ae2 --- /dev/null +++ b/lib/modules/csrf.js @@ -0,0 +1,7 @@ +module.exports = { + + generateToken: async function (options) { + return this.req.csrfToken(options.overwrite); + }, + +}; diff --git a/lib/modules/dataset.js b/lib/modules/dataset.js new file mode 100644 index 0000000..b28dca3 --- /dev/null +++ b/lib/modules/dataset.js @@ -0,0 +1,24 @@ +const { readFileSync } = require('fs'); +const { resolve } = require('path'); + +module.exports = { + + json: function(options) { + return JSON.parse(this.parse(options.data)); + }, + + local: function(options) { + let path = this.parse(options.path); + + if (typeof path !== 'string') throw new Error('dataset.local: path is required.'); + + let data = readFileSync(resolve('public', path)); + + return JSON.parse(data); + }, + + remote: function(options) { + throw new Error('dataset.remote: not implemented, use api instead.'); + }, + +}; \ No newline at end of file diff --git a/lib/modules/dbconnector.js b/lib/modules/dbconnector.js new file mode 100644 index 0000000..9acf8b9 --- /dev/null +++ b/lib/modules/dbconnector.js @@ -0,0 +1,765 @@ +const db = require('../core/db'); +const { where } = require('../formatters'); +const debug = require('debug')('server-connect:db'); + +module.exports = { + + connect: function(options, name) { + if (!name) throw new Error('dbconnector.connect has no name.'); + this.setDbConnection(name, options); + }, + + select: async function(options, name, meta) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.select: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.select: sql is required.'); + if (!sql.table) throw new Error('dbconnector.select: sql.table is required.'); + if (typeof sql.sort != 'string') sql.sort = this.parseOptional('{{ $_GET.sort }}', 'string', null); + if (typeof sql.dir != 'string') sql.dir = this.parseOptional('{{ $_GET.dir }}', 'string', 'asc'); + + if (sql.sort && sql.columns) { + if (!sql.orders) sql.orders = []; + + for (let column of sql.columns) { + if (column.column == sql.sort || column.alias == sql.sort) { + let order = { + column: column.alias || column.column, + direction: sql.dir.toLowerCase() == 'desc' ? 'desc' : 'asc' + }; + + if (column.table && !column.alias) order.table = column.table; + + sql.orders.unshift(order); + + break; + } + } + } + + sql.type = 'select'; + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.orders && sql.orders.length) { + rows.sort((a, b) => { + for (let order of sql.orders) { + if (a[order.column] == b[order.column]) continue; + let desc = order.direction && order.direction.toLowerCase() == 'desc'; + if (a[order.column] < b[order.column]) { + return desc ? 1 : -1; + } else { + return desc ? -1 : 1; + } + } + + return 0; + }); + } + + if (sql.columns && sql.columns.length) { + // we can also skip if user want just all columns + if (!(sql.columns.length == 1 && sql.columns[0].column == '*')) { + rows = rows.map(doc => { + const row = {}; + + for (let column of sql.columns) { + if (column.column == '*') { + Object.assign(row, doc); + } else { + // only support single level for now + row[column.alias || column.column || column] = doc[column.column || column]; + } + } + + return row; + }); + } + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + let offset = Number(sql.offset || 0); + let limit = Number(sql.limit || 0); + return rows.slice(offset, limit ? offset + limit : undefined); + } + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + if (hasSubs(sql)) { + prepareColumns(sql); + + const results = await db.fromJSON(sql, meta); + + if (results.length) { + if (sql.sub) { + await _processSubQueries.call(this, db, results, sql.sub, meta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, results, join.sub, meta, '_' + (join.alias || join.table)); + } + } + } + + cleanupResults(results); + } + + return results; + } + + return db.fromJSON(sql, meta); + }, + + count: async function(options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.count: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.count: sql is required.'); + if (!sql.table) throw new Error('dbconnector.count: sql.table is required.'); + + sql.type = 'count'; + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + return rows.length; + } + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + return (await db.fromJSON(sql)).Total; + }, + + single: async function(options, name, meta) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.single: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.single: sql is required.'); + if (!sql.table) throw new Error('dbconnector.single: sql.table is required.'); + if (typeof sql.sort != 'string') sql.sort = this.parseOptional('{{ $_GET.sort }}', 'string', null); + if (typeof sql.dir != 'string') sql.dir = this.parseOptional('{{ $_GET.dir }}', 'string', 'asc'); + + sql.type = 'first'; + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.orders && sql.orders.length) { + rows.sort((a, b) => { + for (let order of sql.orders) { + if (a[order.column] == b[order.column]) continue; + let desc = order.direction && order.direction.toLowerCase() == 'desc'; + if (a[order.column] < b[order.column]) { + return desc ? 1 : -1; + } else { + return desc ? -1 : 1; + } + } + + return 0; + }); + } + + if (sql.columns && sql.columns.length) { + // we can also skip if user want just all columns + if (!(sql.columns.length == 1 && sql.columns[0].column == '*')) { + rows = rows.map(doc => { + const row = {}; + + for (let column of sql.columns) { + if (column.column == '*') { + Object.assign(row, doc); + } else { + // only support single level for now + row[column.alias || column.column || column] = doc[column.column || column]; + } + } + + return row; + }); + } + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + return rows.length ? rows[0] : null; + } + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + if (hasSubs(sql)) { + prepareColumns(sql); + + const result = await db.fromJSON(sql, meta); + + if (!result) return null; + + if (sql.sub) { + await _processSubQueries.call(this, db, [result], sql.sub, meta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, [result], join.sub, meta, '_' + (join.alias || join.table)); + } + } + } + + cleanupResults([result]); + + return result; + } + + return db.fromJSON(sql, meta) || null; + }, + + paged: async function(options, name, meta) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.paged: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.paged: sql is required.'); + if (!sql.table) throw new Error('dbconnector.paged: sql.table is required.'); + if (typeof sql.offset != 'number') sql.offset = Number(this.parseOptional('{{ $_GET.offset }}', '*', 0)); + if (typeof sql.limit != 'number') sql.limit = Number(this.parseOptional('{{ $_GET.limit }}', '*', 25)); + if (typeof sql.sort != 'string') sql.sort = this.parseOptional('{{ $_GET.sort }}', 'string', null); + if (typeof sql.dir != 'string') sql.dir = this.parseOptional('{{ $_GET.dir }}', 'string', 'asc'); + + if (sql.sort && sql.columns) { + if (!sql.orders) sql.orders = []; + + for (let column of sql.columns) { + if (column.column == sql.sort || column.alias == sql.sort) { + let order = { + column: column.alias || column.column, + direction: sql.dir.toLowerCase() == 'desc' ? 'desc' : 'asc' + }; + + if (column.table && !column.alias) order.table = column.table; + + sql.orders.unshift(order); + + break; + } + } + } + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.orders && sql.orders.length) { + rows.sort((a, b) => { + for (let order of sql.orders) { + if (a[order.column] == b[order.column]) continue; + let desc = order.direction && order.direction.toLowerCase() == 'desc'; + if (a[order.column] < b[order.column]) { + return desc ? 1 : -1; + } else { + return desc ? -1 : 1; + } + } + + return 0; + }); + } + + if (sql.columns && sql.columns.length) { + // we can also skip if user want just all columns + if (!(sql.columns.length == 1 && sql.columns[0].column == '*')) { + rows = rows.map(doc => { + const row = {}; + + for (let column of sql.columns) { + if (column.column == '*') { + Object.assign(row, doc); + } else { + // only support single level for now + row[column.alias || column.column || column] = doc[column.column || column]; + } + } + + return row; + }); + } + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + let offset = Number(sql.offset || 0); + let limit = Number(sql.limit || 0); + let total = rows.length; + + return { + offset, + limit, + total, + page: { + offset: { + first: 0, + prev: offset - limit > 0 ? offset - limit : 0, + next: offset + limit < total ? offset + limit : offset, + last: (Math.ceil(total / limit) - 1) * limit + }, + current: Math.floor(offset / limit) + 1, + total: Math.ceil(total / limit) + }, + data: rows.slice(offset, limit ? offset + limit : undefined) + }; + } + + sql.type = 'count'; + let total = +(await db.fromJSON(sql, meta))['Total']; + + sql.type = 'select'; + let data = []; + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + if (hasSubs(sql)) { + prepareColumns(sql); + + const results = await db.fromJSON(sql, meta); + + if (results.length) { + if (sql.sub) { + await _processSubQueries.call(this, db, results, sql.sub, meta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, results, join.sub, meta, '_' + (join.alias || join.table)); + } + } + } + + cleanupResults(results); + } + + data = results; + } else { + data = await db.fromJSON(sql, meta); + } + + return { + offset: sql.offset, + limit: sql.limit, + total, + page: { + offset: { + first: 0, + prev: sql.offset - sql.limit > 0 ? sql.offset - sql.limit : 0, + next: sql.offset + sql.limit < total ? sql.offset + sql.limit : sql.offset, + last: (Math.ceil(total / sql.limit) - 1) * sql.limit + }, + current: Math.floor(sql.offset / sql.limit) + 1, + total: Math.ceil(total / sql.limit) + }, + data + } + }, + +}; + +async function _processSubQueries(db, results, sub, meta, prefix = '') { + const lookup = new Map(); + const keys = new Set(); + + // get keys from results and create lookup table + // add initial sub field to results (empty array) + for (const result of results) { + const key = String(result['__dmxPrimary' + prefix]); + + if (lookup.has(key)) { + lookup.get(key).push(result); + } else { + lookup.set(key, [result]); + } + + keys.add(key); + + for (const field in sub) { + result[field] = []; + } + } + + for (const field in sub) { + const sql = this.parseSQL(sub[field]); + + sql.type = 'select'; + + prepareColumns(sql); + + let submeta = meta && meta.find(data => data.name == field); + if (submeta && submeta.sub) submeta = submeta.sub; + + // get all subresults with a single query + const subResults = await db.fromJSON(sql, submeta).whereIn(sql.key, Array.from(keys)); + + if (subResults.length) { + if (sql.sub) { + await _processSubQueries.call(this, db, subResults, sql.sub, submeta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, subResults, join.sub, submeta, '_' + (join.alias || join.table)); + } + } + } + + // map the sub results to the parent recordset + for (const subResult of subResults) { + const results = lookup.get(String(subResult['__dmxForeign'])); + + if (results) { + for (const result of results) { + result[field].push(subResult); + } + } + } + } + } + + // we don't need to return anything since all is updated by reference +} + +function hasSubs(sql) { + if (sql.sub) return true; + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) return true; + } + } + + return false; +} + +function prepareColumns(sql) { + const table = sql.table.alias || sql.table.name || sql.table; + + if (!Array.isArray(sql.columns) || !sql.columns.length) { + sql.columns = [{ + table: table, + column: '*' + }]; + + if (Array.isArray(sql.joins) && sql.joins.length) { + for (join of sql.joins) { + sql.columns.push({ + table: join.alias || join.table, + column: '*' + }); + } + } + } + + if (sql.sub && sql.primary) { + sql.columns.push({ + table: table, + column: sql.primary, + alias: '__dmxPrimary' + }); + + if (sql.groupBy && sql.groupBy.length) { + sql.groupBy.push({ + table: table, + column: sql.primary + }); + } + } + + if (sql.key) { + sql.columns.push({ + table: table, + column: sql.key, + alias: '__dmxForeign' + }); + + if (sql.groupBy && sql.groupBy.length) { + sql.groupBy.push({ + table: table, + column: sql.key + }); + } + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub && join.primary) { + sql.columns.push({ + table: join.alias || join.table, + column: join.primary, + alias: '__dmxPrimary_' + (join.alias || join.table) + }); + + if (sql.groupBy && sql.groupBy.length) { + sql.groupBy.push({ + table: join.alias || join.table, + column: join.primary + }); + } + } + } + } +} + +function cleanupResults(results) { + for (const result of results) { + for (const field of Object.keys(result)) { + if (field.startsWith('__dmx')) { + delete result[field]; + } else if (Array.isArray(result[field])) { + cleanupResults(result[field]); + } + } + } +} \ No newline at end of file diff --git a/lib/modules/dbupdater.js b/lib/modules/dbupdater.js new file mode 100644 index 0000000..285cec0 --- /dev/null +++ b/lib/modules/dbupdater.js @@ -0,0 +1,453 @@ +module.exports = { + + insert: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.insert: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.insert: sql is required.'); + if (!sql.table) throw new Error('dbconnector.insert: sql.table is required.'); + + sql.type = 'insert'; + + if (db.client == 'couchdb') { + const doc = {}; + + for (const value of sql.values) { + doc[value.column] = value.value; + } + + const result = await db.insert(doc, sql.table + '/' + Date.now()); + + if (result.ok) { + return { affected: 1, identity: result.id }; + } else { + //throw new Error('dbconnector.insert: error inserting document into couchdb.'); + return { affected: 0 }; + } + } + + if (options.test) { + return { + options: options, + query: sql.toString() + }; + } + + if (sql.sub) { + return db.transaction(async trx => { + // TODO: test how identity is returned for each database + // main insert, returns inserted id + const [identity] = (await trx.fromJSON(sql)).map(value => value[sql.returning] || value); + + // loop sub (relation table) + for (let { table, key, value, values } of Object.values(sql.sub)) { + if (!Array.isArray(value)) break; + + for (const current of value) { + if (typeof current == 'object') { + current[key] = identity; + await trx(table).insert(current); + } else { + if (values.length != 1) throw new Error('Invalid value mapping'); + await trx(table).insert({ + [key]: identity, + [values[0].column]: current + }); + } + } + } + + return { affected: 1, identity }; + }); + } + + let identity = await db.fromJSON(sql); + + if (identity) { + if (Array.isArray(identity)) { + identity = identity[0]; + } + + if (typeof identity == 'object') { + identity = identity[Object.keys(identity)[0]]; + } + } + + return { affected: 1, identity }; + }, + + update: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.update: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.update: sql is required.'); + if (!sql.table) throw new Error('dbconnector.update: sql.table is required.'); + + sql.type = 'update'; + + if (db.client == 'couchdb') { + let { rows } = await db.list({ include_docs: true, startkey: sql.table + '/', endkey: sql.table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + let result = [] + if (rows.length) { + result = await db.bulk({ + docs: rows.map(doc => { + for (const value of sql.values) { + doc[value.column] = value.value; + } + return doc; + }) + }); + } + + return { affected: Array.isArray(result) ? result.filter(result => result.ok).length : rows.length }; + } + + if (options.test) { + return { + options: options, + query: sql.toString() + }; + } + + if (sql.sub) { + return db.transaction(async trx => { + let updated = await trx.fromJSON(sql); + + if (!Array.isArray(updated)) { + // check if is single update + const single = ( + sql.wheres && + sql.wheres.rules && + sql.wheres.rules.length == 1 && + sql.wheres.rules[0].field == sql.returning && + sql.wheres.rules[0].operation == '=' + ); + + if (single) { + // get id from where condition + updated = [sql.wheres.rules[0].value]; + } else { + // create a select with same where conditions + updated = await trx.fromJSON({ + ...sql, + type: 'select', + columns: [sql.returning] + }); + + updated = updated.map(value => value[sql.returning]); + } + } else { + updated = updated.map(value => value[sql.returning]); + } + + // loop sub + for (let { table, key, value, values } of Object.values(sql.sub)) { + if (!Array.isArray(value)) continue; + + // delete old related data first + await trx(table).whereIn(key, updated).del(); + + // for each updated item + for (const identity of updated) { + // insert value + for (const current of value) { + if (typeof current == 'object') { + current[key] = identity; + await trx(table).insert(current); + } else { + if (values.length != 1) throw new Error('Invalid value mapping'); + await trx(table).insert({ + [key]: identity, + [values[0].column]: current + }); + } + } + } + } + + return { affected: updated.length }; + }); + } + + let affected = await db.fromJSON(sql); + + return { affected }; + }, + + delete: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.delete: connection is required.'); + const sql = this.parseSQL(options.sql); + + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.delete: sql is required.'); + if (!sql.table) throw new Error('dbconnector.delete: sql.table is required.'); + + sql.type = 'del'; + + if (db.client == 'couchdb') { + + let { rows } = await db.list({ include_docs: true, startkey: sql.table + '/', endkey: sql.table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + let result = [] + if (rows.length) { + result = await db.bulk({ + docs: rows.map(doc => { + doc._deleted = true; + return doc; + }) + }); + } + + return { affected: Array.isArray(result) ? result.filter(result => result.ok).length : rows.length }; + } + + if (options.test) { + return { + options: options, + query: sql.toString() + }; + } + + if (sql.sub) { + return db.transaction(async trx => { + const deleted = (await trx.fromJSON(sql)).map(value => value[sql.returning] || value); + + // loop sub + for (let { table, key } of Object.values(sql.sub)) { + // delete related data + await trx(table).whereIn(key, deleted).del(); + } + + return { affected: deleted.length }; + }); + } + + let affected = await db.fromJSON(sql); + + return { affected }; + }, + + custom: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.custom: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.custom: sql is required.'); + if (typeof sql.query != 'string') throw new Error('dbupdater.custom: sql.query is required.'); + if (!Array.isArray(sql.params)) throw new Error('dbupdater.custom: sql.params is required.'); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.custom: couchdb is not supported.'); + } + + const params = []; + const query = sql.query.replace(/([:@][a-zA-Z_]\w*|\?)/g, param => { + if (param == '?') { + params.push(sql.params[params.length].value); + return '?'; + } + + let p = sql.params.find(p => p.name == param); + if (p) { + params.push(p.value); + return '?'; + } + + return param; + }); + + let results = await db.raw(query, params); + + if (db.client.config.client == 'mysql' || db.client.config.client == 'mysql2') { + results = results[0]; + } else if (db.client.config.client == 'postgres' || db.client.config.client == 'redshift') { + results = results.rows; + } + + return results; + }, + + execute: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.execute: connection is required.'); + const query = this.parseRequired(options.query, 'string', 'dbupdater.execute: query is required.'); + const params = this.parseOptional(options.params, 'object', []); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.execute: couchdb is not supported.'); + } + + let results = await db.raw(query, params); + + if (db.client.config.client == 'mysql' || db.client.config.client == 'mysql2') { + results = results[0]; + } else if (db.client.config.client == 'postgres' || db.client.config.client == 'redshift') { + results = results.rows; + } + + return results; + }, + + // bulk insert + bulkinsert: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.bulkinsert: connection is required.'); + const sql = this.parseSQL(options.sql); + const source = this.parseRequired(options.source, 'object', 'dbupdater.bulkinsert: source is required.'); + const batchsize = this.parseOptional(options.batchsize, 'number', 100); + const db = this.getDbConnection(connection); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.bulkinsert: couchdb is not supported.'); + } + + return await db.transaction(async transaction => { + for (let i = 0; i < source.length; i += batchsize) { + let batch = source.slice(i, i + batchsize).map(data => { + let values = {}; + + for (let value of sql.values) { + if (value.type == 'json') { + values[value.column] = JSON.stringify(data[value.value]); + } else { + values[value.column] = data[value.value]; + } + } + + return values; + }); + + await transaction(sql.table.name || sql.table).insert(batch); + } + + return { affected: source.length }; + }); + }, + + transaction: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.transaction: connection is required.'); + //const exec = this.parseRequired(options.exec, 'object', 'dbupdater.transaction: exec is required.'); + const db = this.getDbConnection(connection); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.transaction: couchdb is not supported.'); + } + + return await db.transaction(async trx => { + this.trx[connection] = trx; + return await this.exec(options.exec, true).finally(() => { + this.trx[connection] = null; + }); + }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/export.js b/lib/modules/export.js new file mode 100644 index 0000000..4c09f65 --- /dev/null +++ b/lib/modules/export.js @@ -0,0 +1,109 @@ +const fs = require('fs-extra'); +const { toSystemPath } = require('../core/path'); + +module.exports = { + + csv: async function(options) { + let path = this.parse(options.path); + let data = this.parse(options.data); + let header = this.parse(options.header); + let delimiter = this.parse(options.delimiter); + let overwrite = this.parse(options.overwrite); + + if (typeof path != 'string') throw new Error('export.csv: path is required.'); + if (!Array.isArray(data) || !data.length) throw new Error ('export.csv: data is required.'); + + delimiter = typeof delimiter == 'string' ? delimiter : ','; + + if (delimiter == '\\t') delimiter = '\t'; + + const fd = await fs.open(toSystemPath(path), overwrite ? 'w' : 'wx'); + + if (header) { + await putcsv(fd, Object.keys(data[0]), delimiter); + } + + for (let row of data) { + await putcsv(fd, row, delimiter); + } + + await fs.close(fd); + + return path; + }, + + xml: async function(options) { + let path = this.parse(options.path); + let data = this.parse(options.data); + let root = this.parse(options.root); + let item = this.parse(options.item); + let overwrite = this.parse(options.overwrite); + + if (typeof path != 'string') throw new Error('export.xml: path is required.'); + if (!Array.isArray(data) || !data.length) throw new Error('export.xml: data is required.'); + + root = typeof root == 'string' ? root : 'export'; + item = typeof item == 'string' ? item : 'item'; + + const fd = await fs.open(toSystemPath(path), overwrite ? 'w' : 'wx'); + + await fs.write(fd, `<${root}>`); + for (let row of data) { + await fs.write(fd, `<${item}>`); + for (let prop in row) { + await fs.write(fd, `<${prop}>`); + } + await fs.write(fd, ``); + } + await fs.write(fd, ``); + + await fs.close(fd); + + return path; + }, + +}; + +async function putcsv(fd, data, delimiter) { + let str = ''; + + if (typeof data != 'object') { + throw new Error('putcsv: Invalid data.'); + } + + for (let prop in data) { + if (Object.hasOwn(data, prop)) { + let value = String(data[prop]); + + if (/["\n\r\t\s]/.test(value) || value.includes(delimiter)) { + let escaped = false; + + str += '"'; + + for (let i = 0; i < value.length; i++) { + if (value.charAt(i) == '\\') { + escaped = true; + } else if (!escaped && value.charAt(i) == '"') { + str += '"'; + } else { + escaped = false; + } + + str += value.charAt(i); + } + + str += '"'; + } else { + str += value; + } + + str += delimiter; + } + } + + if (!str) { + throw new Error('putcsv: No data.'); + } + + return fs.write(fd, str.substr(0, str.length - delimiter.length) + '\r\n'); +} \ No newline at end of file diff --git a/lib/modules/fs.js b/lib/modules/fs.js new file mode 100644 index 0000000..e3f0ebc --- /dev/null +++ b/lib/modules/fs.js @@ -0,0 +1,258 @@ +const fs = require('fs-extra'); +const { join, dirname, basename, extname } = require('path'); +const { toSystemPath, toAppPath, toSiteUrl, getUniqFile, parseTemplate } = require('../core/path'); +const { map } = require('../core/async'); + +module.exports = { + + download: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.download: path is required.')); + let filename = this.parseOptional(options.filename, 'string', basename(path)); + + if (fs.existsSync(path)) { + this.res.download(path, filename); + this.noOutput = true; + } else { + this.res.sendStatus(404); + } + }, + + exists: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.exists: path is required.')); + + if (await isFile(path)) { + if (options.then) { + await this.exec(options.then, true); + } + return true; + } else { + if (options.else) { + await this.exec(options.else, true); + } + return false; + } + }, + + direxists: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.direxists: path is required.')); + + if (await isDirectory(path)) { + if (options.then) { + await this.exec(options.then, true); + } + return true; + } else { + if (options.else) { + await this.exec(options.else, true); + } + return false; + } + }, + + createdir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.createdir: path is required.')); + + await fs.ensureDir(path); + + return toAppPath(path); + }, + + removedir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.removedir: path is required.')); + + await fs.remove(path); + + return toAppPath(path); + }, + + emptydir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.emptydir: path is required.')); + + await fs.emptyDir(path) + + return toAppPath(path); + }, + + move: async function(options) { + let from = toSystemPath(this.parseRequired(options.from, 'string', 'fs.move: from is required.')); + let to = toSystemPath(this.parseRequired(options.to, 'string', 'fs.move: to is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createdir = this.parseOptional(options.createdir, 'boolean', true); + + if (!fs.existsSync(to)) { + if (createdir) { + await fs.ensureDir(to); + } else { + throw new Error(`Destination path doesn't exists.`); + } + } + + to = join(to, basename(from)); + + if (!overwrite) { + to = getUniqFile(to); + } + + await fs.move(from, to, { overwrite: true }); + + return toAppPath(to); + }, + + rename: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.rename: path is required.')); + let template = this.parseRequired(options.template, 'string', 'fs.rename: template is required.'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let to = parseTemplate(path, template); + + if (!overwrite && fs.existsSync(to)) { + throw new Error(`fs.rename: file "${to}" already exists.`); + } + + await fs.rename(path, to); + + return toAppPath(to); + }, + + copy: async function(options) { + let from = toSystemPath(this.parseRequired(options.from, 'string', 'fs.copy: from is required.')); + let to = toSystemPath(this.parseRequired(options.to, 'string', 'fs.copy: to is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createdir = this.parseOptional(options.createdir, 'boolean', true); + + if (!fs.existsSync(to)) { + if (createdir) { + await fs.ensureDir(to); + } else { + throw new Error(`Destination path doesn't exist.`); + } + } + + to = join(to, basename(from)); + + if (!overwrite && fs.existsSync(to)) { + to = getUniqFile(to); + } + + await fs.copy(from, to); + + return toAppPath(to); + }, + + remove: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.remove: path is required.')); + + await fs.unlink(path); + + return true; + }, + + dir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.dir: path is required.')); + let allowedExtensions = this.parseOptional(options.allowedExtensions, 'string', ''); + let showHidden = this.parseOptional(options.showHidden, 'boolean', false); + let includeFolders = this.parseOptional(options.includeFolders, 'boolean', false); + let folderSize = this.parseOptional(options.folderSize, 'string', 'none'); + let concurrency = this.parseOptional(options.concurrency, 'number', 4); + + folderSize = ['none', 'files', 'recursive'].includes(folderSize) ? folderSize : 'none'; + allowedExtensions = allowedExtensions ? allowedExtensions.split(/\s*,\s*/).map(ext => lowercase(ext[0] == '.' ? ext : '.' + ext)) : []; + + let files = await fs.readdir(path, { withFileTypes: true }); + + files = files.filter(entry => { + if (!includeFolders && entry.isDirectory()) return false; + if (!showHidden && entry.name[0] == '.') return false; + if (allowedExtensions.length && entry.isFile() && !allowedExtensions.includes(lowercase(extname(entry.name)))) return false; + return entry.isFile() || entry.isDirectory(); + }); + + // Fast parallel map + return map(files, async (entry) => { + let curr = join(path, entry.name); + let stat = await fs.stat(curr); + + if (folderSize != 'none' && entry.isDirectory()) { + stat.size = await calcSize(curr, folderSize == 'recursive', concurrency); + } + + return { + type: entry.isFile() ? 'file' : 'dir', + name: entry.name, + folder: toAppPath(dirname(curr)), + basename: basename(curr, extname(curr)), + extension: extname(curr), + path: toAppPath(curr), + url: toSiteUrl(curr), + size: stat.size, + created: stat.ctime, + accessed: stat.atime, + modified: stat.mtime + }; + }, concurrency); + }, + + stat: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.stat: path is required.')); + let folderSize = this.parseOptional(options.folderSize, 'string', 'none'); + let concurrency = this.parseOptional(options.concurrency, 'number', 4); + + folderSize = ['none', 'files', 'recursive'].includes(folderSize) ? folderSize : 'none'; + + let stat = await fs.stat(path); + + if (folderSize != 'none' && stat.isDirectory()) { + stat.size = await calcSize(path, folderSize == 'recursive', concurrency); + } + + return { + type: stat.isFile() ? 'file' : 'dir', + name: basename(path), + folder: toAppPath(dirname(path)), + basename: basename(path, extname(path)), + extension: extname(path), + path: toAppPath(path), + url: toSiteUrl(path), + size: stat.size, + created: stat.ctime, + accessed: stat.atime, + modified: stat.mtime + }; + }, + +}; + +function lowercase(str) { + return str.toLowerCase(); +} + +async function calcSize(folder, recursive, concurrency) { + let entries = await fs.readdir(folder); + + return map(entries, async (entry) => { + let stat = await fs.stat(join(folder, entry)); + + if (stat.isDirectory() && recursive) { + return calcSize(join(folder, entry), recursive, concurrency); + } + + return stat.size; + }, concurrency).then(arr => arr.reduce((size, curr) => size + curr, 0)); +}; + +async function isFile(path) { + try { + let stats = await fs.stat(path); + return stats.isFile(); + } catch (err) { + return false; + } +} + +async function isDirectory(path) { + try { + let stats = await fs.stat(path); + return stats.isDirectory(); + } catch (err) { + return false; + } +} \ No newline at end of file diff --git a/lib/modules/image.js b/lib/modules/image.js new file mode 100644 index 0000000..a76db50 --- /dev/null +++ b/lib/modules/image.js @@ -0,0 +1,505 @@ +const fs = require('fs-extra'); +const Sharp = require('sharp'); +const debug = require('debug')('server-connect:image'); +const { basename, extname, join } = require('path'); +const { toAppPath, toSystemPath, parseTemplate, getUniqFile } = require('../core/path'); + +const positions = { + 'center': 0, + 'centre': 0, + 'top': 1, + 'north': 1, + 'right': 2, + 'east': 2, + 'bottom': 3, + 'south': 3, + 'left': 4, + 'west': 4, + 'top right': 5, + 'right top': 5, + 'northeast': 5, + 'bottom right': 6, + 'right bottom': 6, + 'southeast': 6, + 'bottom left': 7, + 'left bottom': 7, + 'southwest': 7, + 'top left': 8, + 'left top': 8, + 'northwest': 8, + 'entropy': 16, + 'attention': 17 +}; + +function cw(w, meta) { + if (typeof w == 'string') { + if (/%$/.test(w)) { + w = meta.width * parseFloat(w) / 100; + } + } + + if (w < 0) { + w = meta.width + w; + } + + return parseInt(w); +} + +function ch(h, meta) { + if (typeof h == 'string') { + if (/%$/.test(h)) { + h = meta.height * parseFloat(h) / 100; + } + } + + if (h < 0) { + h = meta.height + h; + } + + return parseInt(h); +} + +function cx(x, w, meta) { + if (typeof x == 'string') { + switch (x) { + case 'left': + x = 0; + break; + case 'center': + x = (meta.width - w) / 2; + break; + case 'right': + x = meta.width - w; + break; + default: + if (/%$/.test(x)) { + x = (meta.width - w) * parseFloat(x) / 100; + } + } + } + + if (x < 0) { + x = meta.width - w + x; + } + + return parseInt(x); +} + +function cy(y, h, meta) { + if (typeof y == 'string') { + switch (y) { + case 'top': + y = 0; + break; + case 'middle': + y = (meta.height - h) / 2; + break; + case 'bottom': + y = meta.height - h; + break; + default: + if (/%$/.test(y)) { + y = (meta.height - h) * parseFloat(y) / 100; + } + } + } + + if (y < 0) { + y = meta.height - h + y; + } + + return parseInt(y); +} + +async function updateImage(sharp) { + sharp.image = Sharp(await sharp.image.toBuffer()); + sharp.metadata = await sharp.image.metadata(); +} + +module.exports = { + + getImageSize: async function (options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.getImageSize: path is required.')); + + const image = Sharp(path); + const metadata = await image.metadata(); + + return { + width: metadata.width, + height: metadata.height + }; + }, + + load: async function (options, name) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.load: path is required.')); + let orient = this.parseOptional(options.autoOrient, 'boolean', false); + + this.req.image = this.req.image || {}; + this.req.image[name] = { name: basename(path), image: Sharp(path), metadata: null }; + + const sharp = this.req.image[name]; + if (orient) sharp.image.rotate(); + + await updateImage(sharp); + + return { + name: basename(path), + width: sharp.metadata.width, + height: sharp.metadata.height + }; + }, + + save: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.save: instance "${options.instance} doesn't exist.`); + + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.save: path is required.')); + let format = this.parseOptional(options.format, 'string', 'jpeg').toLowerCase(); + let template = this.parseOptional(options.template, 'string', '{name}{ext}'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createPath = this.parseOptional(options.createPath, 'boolean', true); + let background = this.parseOptional(options.background, 'string', '#FFFFFF'); + let quality = this.parseOptional(options.quality, 'number', 75); + + if (!fs.existsSync(path)) { + if (createPath) { + await fs.ensureDir(path); + } else { + throw new Error(`image.save: path "${path}" doesn't exist.`); + } + } + + let file = join(path, sharp.name); + + if (template) { + file = parseTemplate(file, template); + } + + if (format == 'auto') { + switch (extname(file).toLowerCase()) { + case '.png': format = 'png'; break; + case '.gif': format = 'gif'; break; + case '.webp': format = 'webp'; break; + default: format = 'jpeg'; + } + } + + if (format == 'jpeg') { + sharp.image.flatten({ background }); + sharp.image.toFormat(format, { quality }); + } else if (format == 'webp') { + sharp.image.toFormat(format, { quality }); + } else { + sharp.image.toFormat(format); + } + + const data = await sharp.image.toBuffer(); + + file = file.replace(extname(file), '.' + format.replace('jpeg', 'jpg')); + + if (fs.existsSync(file)) { + if (overwrite) { + await fs.unlink(file); + } else { + file = getUniqFile(file); + } + } + + await fs.writeFile(file, data) //.toFile(file); + + return toAppPath(file); + }, + + resize: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.resize: instance "${options.instance} doesn't exist.`); + + let width = this.parseOptional(cw(this.parse(options.width), sharp.metadata), 'number', null); + let height = this.parseOptional(ch(this.parse(options.height), sharp.metadata), 'number', null); + let upscale = this.parseOptional(options.upscale, 'boolean', false); + + if (isNaN(width)) width = null; + if (isNaN(height)) height = null; + + sharp.image.resize(width, height, { fit: width && height ? 'fill' : 'cover', withoutEnlargement: !upscale }); + + await updateImage(sharp); + }, + + crop: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.crop: instance "${options.instance} doesn't exist.`); + + let width = this.parseRequired(cw(this.parse(options.width)), 'number', 'image.crop: width is required.'); + let height = this.parseRequired(ch(this.parse(options.height)), 'number', 'image.crop: height is required.'); + if (width > sharp.metadata.width) width = sharp.metadata.width; + if (height > sharp.metadata.height) height = sharp.metadata.height; + let left = this.parseRequired(cx(this.parse(options.x), width, sharp.metadata), 'number', 'image.crop: x is required.'); + let top = this.parseRequired(cy(this.parse(options.y), height, sharp.metadata), 'number', 'image.crop: y is required.'); + + sharp.image.extract({ left, top, width, height }); + + await updateImage(sharp); + }, + + cover: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.cover: instance "${options.instance}" doesn't exist.`); + + let width = this.parseRequired(options.width, 'number', 'image.cover: width is required.'); + let height = this.parseRequired(options.height, 'number', 'image.cover: height is required.'); + // position: see positions object for options + let position = this.parseOptional(options.position, 'string', 'center'); + // kernel: 'nearest', 'cubic', 'mitchell', 'lanczos2', 'lanczos3' + let kernel = this.parseOptional(options.kernel, 'string', 'lanczos3'); + + position = positions[position] || 0; + + sharp.image.resize({ width, height, position, kernel }); + + await updateImage(sharp); + }, + + watermark: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.watermark: instance "${options.instance} doesn't exist.`); + + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.watermark: path is required.')); + let image = Sharp(path); + let metadata = await image.metadata(); + let input = await image.toBuffer(); + let left = this.parseRequired(cx(this.parse(options.x), metadata.width, sharp.metadata), 'number', 'image.watermark: x is required.'); + let top = this.parseRequired(cy(this.parse(options.y), metadata.height, sharp.metadata), 'number', 'image.watermark: y is required.'); + + sharp.image.composite([{ input, left, top }]); + }, + + text: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.text: instance "${options.instance} doesn't exist.`); + + let x = this.parse(options.x); + let y = this.parse(options.y); + let text = this.parseRequired(options.text, 'string', 'image.text: text is required.'); + let font = this.parseOptional(options.font, 'string', 'Verdana'); + let size = this.parseOptional(options.size, 'number', 24); + let color = this.parseOptional(options.color, 'string', '#ffffff'); + + let width = sharp.metadata.width; + let height = sharp.metadata.height; + let anchor = 'start'; + + switch (x) { + case 'left': + x = '0%'; + anchor = 'start'; + break; + case 'center': + x = '50%'; + anchor = 'middle'; + break; + case 'right': + x = '100%'; + anchor = 'end'; + break; + default: + if (x < 0) { + x = width - x; + anchor = 'end'; + } + } + + switch (y) { + case 'top': + y = size; + break; + case 'middle': + y = (height / 2) - (size / 2); + break; + case 'bottom': + y = height; + break; + default: + if (y < 0) { + y = height - size - y; + } + } + + let svg = ` + + + ${text} + + `; + + const input = await Sharp(Buffer.from(svg)).toBuffer(); + + sharp.image.composite([{ input, left: 0, top: 0 }]); + }, + + tiled: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.tiled: instance "${options.instance} doesn't exist.`); + + let input = toSystemPath(this.parseRequired(options.path, 'string', 'image.tiled: path is required.')); + let padding = this.parseOptional(options.padding, 'number', 0); + + if (padding) { + input = await Sharp(input).extend({ + top: padding, left: padding, bottom: 0, right: 0, background: { r: 0, g: 0, b: 0, alpha: 0 } + }).toBuffer(); + } + + sharp.image.composite([{ input, left: 0, top: 0, tile: true }]); + }, + + flip: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.flip: instance "${options.instance} doesn't exist.`); + + let horizontal = this.parseOptional(options.horizontal, 'boolean', false); + let vertical = this.parseOptional(options.vertical, 'boolean', false); + + if (horizontal) sharp.image.flop(); + if (vertical) sharp.image.flip(); + }, + + rotateLeft: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.rotateLeft: instance "${options.instance} doesn't exist.`); + + sharp.image.rotate(-90); + + await updateImage(sharp); + }, + + rotateRight: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.rotateRight: instance "${options.instance} doesn't exist.`); + + sharp.image.rotate(90); + + await updateImage(sharp); + }, + + smooth: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.smooth: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + 1, 1, 1, + 1, 1, 1, + 1, 1, 1 + ] + }); + }, + + blur: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.blur: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + 1, 2, 1, + 2, 4, 2, + 1, 2, 1 + ] + }); + }, + + sharpen: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.sharpen: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + 0, -2, 0, + -2, 15, -2, + 0, -2, 0 + ] + }); + }, + + meanRemoval: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.meanRemoval: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + -1, -1, -1, + -1, 9, -1, + -1, -1, -1 + ] + }); + }, + + emboss: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.emboss: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + -1, 0, -1, + 0, 4, 0, + -1, 0, -1 + ], + offset: 127 + }); + }, + + edgeDetect: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.edgeDetect: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + -1, -1, -1, + 0, 0, 0, + 1, 1, 1 + ], + offset: 127 + }); + }, + + grayscale: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.grayscale: instance "${options.instance} doesn't exist.`); + + sharp.image.grayscale(); + }, + + sepia: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.sepia: instance "${options.instance} doesn't exist.`); + + sharp.image.tint({ r: 112, g: 66, b: 20 }); + }, + + invert: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.invert: instance "${options.instance} doesn't exist.`); + + sharp.image.negate(); + }, + +}; \ No newline at end of file diff --git a/lib/modules/import.js b/lib/modules/import.js new file mode 100644 index 0000000..053d8c4 --- /dev/null +++ b/lib/modules/import.js @@ -0,0 +1,129 @@ +const fs = require('fs-extra'); +const { toSystemPath } = require('../core/path'); +const { keysToLowerCase } = require('../core/util'); + +// simple inital implementation +// better implementation at: +// https://github.com/adaltas/node-csv-parse +// https://github.com/mafintosh/csv-parser +function parseCSV(csv, options) { + if (!csv) return []; + + if (csv.charCodeAt(0) === 0xFEFF) { + csv = csv.slice(1); + } + + let delimiter = options.delimiter.replace('\\t', '\t'); + let keys = options.fields; + let line = 1; + let data = []; + + if (options.header) { + keys = getcsv(); + + options.fields.forEach(field => { + if (keys.indexOf(field) == -1) { + throw new Error('parseCSV: ' + field + ' is missing in ' + options.path); + } + }); + + line++; + } + + let size = keys.length; + + while (csv.length) { + let values = getcsv(); + let o = {}; + + if (values.length != size) { + throw new Error('parseCSV: columns do not match. keys: ' + size + ', values: ' + values.length + ' at line ' + line); + } + + for (let i = 0; i < size; i++) { + o[keys[i]] = values[i]; + } + + data.push(o); + + line++; + } + + return data; + + function getcsv() { + let data = [''], l = csv.length, + esc = false, escesc = false, + n = 0, i = 0; + + while (i < l) { + let s = csv.charAt(i); + + if (s == '\n') { + if (esc) { + data[n] += s; + } else { + i++; + break; + } + } else if (s == '\r') { + if (esc) { + data[n] += s; + } + } else if (s == delimiter) { + if (esc) { + data[n] += s; + } else { + data[++n] = ''; + esc = false; + escesc = false; + } + } else if (s == '"') { + if (escesc) { + data[n] += s; + escesc = false; + } + + if (esc) { + esc = false; + escesc = true; + } else { + esc = true; + escesc = false; + } + } else { + if (escesc) { + data[n] += '"'; + escesc = false; + } + + data[n] += s; + } + + i++; + } + + csv = csv.substr(i); + + return data; + } +} + +module.exports = { + + csv: async function(options) { + let path = this.parseRequired(options.path, 'string', 'export.csv: path is required.'); + let fields = this.parseOptional(options.fields, 'object', []); + let header = this.parseOptional(options.header, 'boolean', false); + let delimiter = this.parseOptional(options.delimiter, 'string', ','); + let csv = await fs.readFile(toSystemPath(path), 'utf8'); + + return parseCSV(csv, { fields, header, delimiter }); + }, + + xml: async function(options) { + // TODO: import.xml + throw new Error('import.xml: not implemented.'); + }, + +}; \ No newline at end of file diff --git a/lib/modules/jwt.js b/lib/modules/jwt.js new file mode 100644 index 0000000..6299f05 --- /dev/null +++ b/lib/modules/jwt.js @@ -0,0 +1,22 @@ +exports.sign = function(options, name) { + return this.setJSONWebToken(name, options) +}; + +exports.decode = function(options) { + const jwt = require('jsonwebtoken'); + return jwt.decode(this.parse(options.token), { complete: true }); +}; + +exports.verify = function(options) { + const jwt = require('jsonwebtoken'); + options = this.parse(options); + + try { + return jwt.verify(options.token, options.key, options); + } catch (error) { + const debug = require('debug')('server-connect:jwt'); + debug('jwt verify failed: %o', error); + if (options.throw) throw error; + return { error }; + } +}; \ No newline at end of file diff --git a/lib/modules/mail.js b/lib/modules/mail.js new file mode 100644 index 0000000..34721fe --- /dev/null +++ b/lib/modules/mail.js @@ -0,0 +1,92 @@ +const fs = require('fs-extra'); +const { getFilesArray, toSystemPath } = require('../core/path'); +const { basename, posix } = require('path'); +const { v4: uuidv4 } = require('uuid'); +const IMPORTANCE = { 0: 'low', 1: 'normal', 2: 'high' }; + +module.exports = { + + setup: function(options, name) { + if (!name) throw new Error('mail.setup has no name.'); + this.setMailer(name, options); + }, + + send: async function(options) { + let setup = this.getMailer(this.parseOptional(options.instance, 'string', 'system')); + let subject = this.parseRequired(options.subject, 'string', 'mail.send: subject is required.'); + let fromEmail = this.parseRequired(options.fromEmail, 'string', 'mail.send: fromEmail is required.'); + let fromName = this.parseOptional(options.fromName, 'string', ''); + let toEmail = this.parseRequired(options.toEmail, 'string', 'mail.send: toEmail is required.'); + let toName = this.parseOptional(options.toName, 'string', ''); + let replyTo = this.parseOptional(options.replyTo, 'string', ''); + let cc = this.parseOptional(options.cc, 'string', ''); + let bcc = this.parseOptional(options.bcc, 'string', ''); + let source = this.parseOptional(options.source, 'string', 'static'); // static, file + let contentType = this.parseOptional(options.contentType, 'string', 'text'); // text / html + let body = this.parseOptional(options.body, 'string', ''); + let bodyFile = this.parseOptional(options.bodyFile, 'string', ''); + let embedImages = this.parseOptional(options.embedImages, 'boolean', false); + let priority = IMPORTANCE[this.parseOptional(options.importance, 'number', 1)]; + let attachments = this.parseOptional(options.attachments, '*', []); // "/file.ext" / ["/file.ext"] / {path:"/file.ext"} / [{path:"/file.ext"}] + + let from = fromName ? `"${fromName}" <${fromEmail}>` : fromEmail; + let to = toName ? `"${toName}" <${toEmail}>` : toEmail; + let text = body; + let html = null; + + if (source == 'file') { + body = this.parse(await fs.readFile(toSystemPath(bodyFile), 'utf8')); + } + + if (attachments) { + attachments = getFilesArray(attachments).map((path) => ({ filename: basename(path), path })); + } + + if (contentType == 'html') { + html = body; + + if (embedImages) { + let cid = {}; + + html = html.replace(/(?:"|')([^"']+\.(jpg|png|gif))(?:"|')/gi, (m, url) => { + let path = toSystemPath(url); + + if (fs.existsSync(path)) { + if (!cid[path]) { + cid[path] = uuidv4(); + attachments.push({ + filename: basename(path), + path: path, + cid: cid[path] + }); + } + + return `"cid:${cid[path]}"`; + } else { + console.warn(`${path} not found`); + } + + return `"${url}"`; + }); + } + + if (this.req.get) { // we can only do this if we have a request to get our hostname + const hasProxy = !!this.req.get('x-forwarded-host'); + const host = hasProxy ? `${this.req.protocol}://${this.req.hostname}` : this.req.get('host'); + + html = html.replace(/(href|src)(?:\s*=\s*)(?:"|')([^"']+)(?:"|')/gi, (m, attr, url) => { + if (!url.includes(':')) { + url = posix.join(host, url); + } + + return `${attr}="${url}"`; + }); + } + } + + const nodemailer = require('nodemailer'); + let transport = nodemailer.createTransport(setup); + return transport.sendMail({ from, to, cc, bcc, replyTo, subject, html, text, priority, attachments }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/metadata.js b/lib/modules/metadata.js new file mode 100644 index 0000000..9ebe541 --- /dev/null +++ b/lib/modules/metadata.js @@ -0,0 +1,484 @@ +const fs = require('fs-extra'); + +const imageTypes = ['PNG', 'GIF', 'BMP', 'JPEG', 'TIFF']; +const videoTypes = ['AVI', 'MP4', 'MOV', 'MKV', 'WEBM', 'OGV']; +const soundTypes = ['OGG', 'WAV', 'MP3', 'FLAC']; + +const read = async (path, offset, length) => { + const fp = await fs.open(path); + const buff = Buffer.alloc(length); + + await fs.read(fd, buff, 0, length, offset); + await fs.close(); + + return buff; +}; + + +const parser = { + + PNG: async (path, result) => { + const buff = await read(path, 18, 6); + result.width = buff.readUInt16BE(0); + result.height = buff.readUInt16BE(4); + }, + + GIF: async (path, result) => { + const buff = await read(path, 6, 4); + result.width = buff.readUInt16LE(0); + result.height = buff.readUInt16LE(2); + }, + + BMP: async (path, result) => { + const buff = await read(path, 18, 8); + result.width = buff.readUInt32LE(0); + result.height = buff.readUInt32LE(4); + }, + + JPEG: async (path, result) => { + const sof = [0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xde]; + const buff = await read(path, 2, 64000); + + let pos = 0; + + while (buff[pos++] == 0xff) { + let marker = buff[pos++]; + let size = buff.readUInt16BE(pos); + + if (marker == 0xda) break; + + if (sof.includes(marker)) { + result.height = buff.readUInt16BE(pos + 3); + result.width = buff.readUInt16BE(pos + 5); + break; + } + + pos += size; + } + }, + + TIFF: async (path, result) => { + const buff = await read(path, 0, 64000); + const le = buff.toString('ascii', 0, 2) == 'II'; + let pos = 0; + const readUInt16 = () => { pos += 2; return buff[le ? 'readUInt16LE' : 'readUInt16BE'](pos - 2); } + const readUInt32 = () => { pos += 4; return buff[le ? 'readUInt32LE' : 'readUInt32BE'](pos - 4); } + + let offset = readUInt32(); + + while (pos < buff.length && offset > 0) { + let entries = readUInt16(offset); + let start = pos; + + for (let i = 0; i < entries; i++) { + let tag = readUInt16(); + let type = readUInt16(); + let length = readUInt32(); + let data = (type == 3) ? readUInt16() : readUInt32(); + if (type == 3) pos += 2; + + if (tag == 256) { + result.width = data; + } else if (tag == 257) { + result.height = data; + } + + if (result.width > 0 && result.height > 0) { + return; + } + } + + offset = readUInt32(); + pos += offset; + } + }, + + AVI: async (path, result) => { + const buff = await read(path, 0, 144); + result.width = buff.readUInt32LE(64); + result.height = buff.readUInt32LE(68); + result.duration = ~~(buff.readUInt32LE(128) / buff.readUInt32LE(132) * buff.readUInt32LE(140)); + }, + + MP4: async (path, result) => { + return parser.MOV(path, result); + }, + + MOV: async (path, result, pos = 0) => { + const buff = await read(path, 0, 64000); + + while (pos < buff.length) { + let size = buff.readUInt32BE(pos); + let name = buff.toString('ascii', pos + 4, 4); + + if (name == 'mvhd') { + let scale = buff.readUInt32BE(pos + 20); + let duration = buff.readUInt32BE(pos + 24); + result.duration = ~~(duration / scale); + } + + if (name == 'tkhd') { + let m0 = buff.readUInt32BE(pos + 48); + let m4 = buff.readUInt32BE(pos + 64); + let w = buff.readUInt32BE(pos + 84); + let h = buff.readUInt32BE(pos + 88); + if (w > 0 && h > 0) { + result.width = w / m0; + result.height = h / m4; + return; + } + } + + if (name == 'moov' || name == 'trak') { + await parser.MOV(path, pos + 8); + } + + pos += size; + } + }, + + WEBM: async (path, result) => { + return parser.EBML(path, result); + }, + + MKV: async (path, result) => { + return parser.EBML(path, result); + }, + + EBML: async (path, result) => { + const containers = ['\x1a\x45\xdf\xa3', '\x18\x53\x80\x67', '\x15\x49\xa9\x66', '\x16\x54\xae\x6b', '\xae', '\xe0']; + const buff = await read(path, 0, 64000); + + // TODO parse EBML + }, + + OGV: async (path, result) => { + return parser.OGG(path, result); + }, + + OGG: async (path, result) => { + const buff = await read(apth, 0, 64000); + let pos = 0, vorbis; + + while (buff.toString('ascii', pos, pos + 4) == 'OggS') { + let version = buff[pos + 4]; + let b = buff[pos + 5]; + let continuation = !!(b & 0x01); + let bos = !!(b & 0x02); + let eos = !!(b & 0x04); + let position = Number(buff.readBigUInt64LE(pos + 6)); + let serial = buff.readUInt32LE(pos + 14); + let pageNumber = buff.readUInt32LE(pos + 18); + let checksum = buff.readUInt32LE(pos + 22); + let pageSegments = buff[path + 26]; + let lacing = buff.slice(pos + 27, pos + 27 + pageSegments); + let pageSize = lacing.reduce((p, v) => p + v, 0); + let start = pos + 27 + pageSegments; + let pageHeader = buff.slice(start, start + 7); + + if (pageHeader.compare(Buffer.from([0x01, 'v', 'o', 'r', 'b', 'i', 's']))) { + vorbis = { serial, sampleRate: buff.readUInt32LE(start + 12) }; + } + + if (pageHeader.compare(Buffer.from([0x80, 't', 'h', 'e', 'o', 'r', 'a']))) { + let version = buff.slice(start + 7, start + 10); + result.width = buff.readUInt16BE(start + 10) << 4; + result.height = buff.readUInt16BE(start + 12) << 4; + + if (version >= 0x030200) { + let width = buff.slice(start + 14, start + 17); + let height = buff.slice(start + 17, start + 20); + + if (width <= result.width && width > result.width - 16 && height <= result.height && height > result.height - 16) { + result.width = width; + result.height = height; + } + } + } + + if (eos && vorbis && serail == vorbis.serial) { + result.duration = ~~(position / vorbis.sampleRate); + } + + pos = start + pageSize; + } + }, + + WAV: async (path, result) => { + const buff = await read(path, 0, 32); + let size = buff.readUInt32LE(4); + let rate = buff.readUInt32LE(28); + result.duration = ~~(size / rate); + }, + + MP3: async (path, result) => { + const versions = [2.5, 0, 2, 1]; + const layers = [0, 3, 2, 1]; + const bitrates = [ + [ // version 2.5 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 3 + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 2 + [0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256] // layer 1 + ], + [ // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // reserved + ], + [ // version 2 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 3 + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 2 + [0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256] // layer 1 + ], + [ // version 1 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320], // layer 3 + [0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384], // layer 2 + [0,32,64,96,128,160,192,224,256,288,320,352,384,416,448] // layer 1 + ] + ] + const srates = [ + [11025, 12000, 8000, 0], // mpeg 2.5 + [ 0, 0, 0, 0], // reserved + [22050, 24000, 16000, 0], // mpeg 2 + [44100, 48000, 32000, 0] // mpeg 1 + ] + const tsamples = [ + [0, 576, 1152, 384], // mpeg 2.5 + [0, 0, 0, 0], // reserved + [0, 576, 1152, 384], // mpeg 2 + [0, 1152, 1152, 384] // mpeg 1 + ]; + const slotSizes = [0, 1, 1, 4]; + const modes = ['stereo', 'joint_stereo', 'dual_channel', 'mono']; + const buff = await read(path, 0, 64000); + + let duration = 0; + let count = 0; + let skip = 0; + let pos = 0; + + while (pos < buff.length) { + let start = pos; + + if (buff.toString('ascii', pos, 4) == 'TAG+') { + skip += 227; + pos += 227; + } else if (buff.toString('ascii', pos, 3) == 'TAG') { + skip += 128; + pos += 128; + } else if (buff.toString('ascii', pos, 3) == 'ID3') { + let bytes = buff.readUInt32BE(pos + 6); + let size = 10 + (bytes[0] << 21 | bytes[1] << 14 | bytes[2] << 7 | bytes[3]); + skip += size; + pos += size; + } else { + let hdr = buff.slice(pos, pos + 4); + + while (pos < buff.length && !(hdr[0] == 0xff && (hdr[1] & 0xe0) == 0xe0)) { + pos++; + hdr = buff.slice(pos, pos + 4); + } + + let ver = (hdr[1] & 0x18) >> 3; + let lyr = (hdr[1] & 0x06) >> 1; + let pad = (hdr[2] & 0x02) >> 1; + let brx = (hdr[2] & 0xf0) >> 4; + let srx = (hdr[2] & 0x0c) >> 2; + let mdx = (hdr[3] & 0xc0) >> 6; + + let version = versions[ver]; + let layer = layers[lyr]; + let bitrate = bitrates[ver][lyr][brx] * 1000; + let samprate = srates[ver][srx]; + let samples = tsamples[ver][lyr]; + let slotSize = slotSizes[lyr]; + let mode = modes[mdx]; + let fsize = ~~(((samples / 8 * bitrate) / samprate) + (pad ? slotSize : 0)); + + count++; + + if (count == 1) { + if (layer != 3) { + pos += 2; + } else { + if (mode != 'mono') { + if (version == 1) { + pos += 32; + } else { + pos += 17; + } + } else { + if (version == 1) { + pos += 17; + } else { + pos += 9; + } + } + } + + if (buff.toString('ascii', pos, pos + 4) == 'Xing' && (buff.readUInt32BE(pos + 4) & 0x0001) == 0x0001) { + let totalFrames = buff.readUInt32BE(pos + 8); + duration = totalFrames * samples / samprate; + break; + } + } + + if (fsize < 1) break; + + pos = start + fsize; + + duration += (samples / samprate); + } + } + + result.duration = ~~duration; + }, + + FLAC: async (path, result) => { + const buff = await read(path, 18, 8); + let rate = (buff[0] << 12) | (buff[1] << 4) | ((buff[2] & 0xf0) >> 4); + let size = ((buff[3] & 0x0f) << 32) | (buff[4] << 24) | (buff[5] << 16) | (buff[6] << 8) | buff[7]; + result.duration = ~~(size / rate); + }, + +}; + +async function detect(path) { + const buff = await read(path, 0, 12); + + if (buff.slice(0, 8).compare(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) { + return 'PNG'; + } + + if (buff.toString('ascii', 0, 3) == 'GIF') { + return 'GIF'; + } + + if (buff.toString('ascii', 0, 2) == 'BM') { + return 'BMP'; + } + + if (buff.slice(0, 2).compare(Buffer.from([0xff, 0xd8]))) { + return 'JPEG'; + } + + if (buff.toString('ascii', 0, 2) == 'II' && buff.readUInt16LE(2) == 42) { + return 'TIFF'; + } + + if (buff.toString('ascii', 0, 2) == 'MM' && buff.readUInt16BE(2) == 42) { + return 'TIFF'; + } + + if (buff.toString('ascii', 0, 4) == 'RIFF' && buff.toString('ascii', 8, 4) == 'AVI ') { + return 'AVI'; + } + + if (buff.toString('ascii', 4, 4) == 'ftyp') { + return 'MP4'; + } + + if (buff.toString('ascii', 4, 4) == 'moov') { + return 'MOV'; + } + + if (buff.slice(0, 4).compare(Buffer.from([0x1a, 0x45, 0xdf, 0xa3]))) { + // TODO detect MKV + + return 'EBML'; + } + + if (buff.toString('ascii', 0, 4) == 'OggS') { + // TODO detect OGV + + return 'OGG' + } + + if (buff.toString('ascii', 0, 4) == 'RIFF', buff.toString('ascii', 8, 4) == 'WAVE') { + return 'WAV'; + } + + if (buff.toString('ascii', 0, 3) == 'ID3' || (buf[0] == 0xff && (buff[1] & 0xe0))) { + return 'MP3'; + } + + if (buff.toString('ascii', 0, 4) == 'fLaC') { + return 'FLAC'; + } + + return null +} + +module.exports = { + + detect: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.detect: path is required.'); + + return detect(path); + }, + + isImage: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.isImage: path is required.'); + let type = await detect(path); + let cond = imageTypes.includes(type); + + if (cond) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + + return cond; + }, + + isVideo: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.isVideo: path is required.'); + let type = await detect(path); + let cond = videoTypes.includes(type); + + if (cond) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + + return cond; + }, + + isSound: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.isSound: path is required.'); + let type = await detect(path); + let cond = soundTypes.includes(type); + + if (cond) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + + return cond; + }, + + fileinfo: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.fileinfo: path is required.'); + let type = await detect(path); + let result = { type, width: null, height: null, duration: null }; + + if (parser[type]) { + await parser[type](path, result); + } + + return result; + }, + +}; \ No newline at end of file diff --git a/lib/modules/oauth.js b/lib/modules/oauth.js new file mode 100644 index 0000000..e9e94ee --- /dev/null +++ b/lib/modules/oauth.js @@ -0,0 +1,24 @@ +module.exports = { + + provider: async function(options, name) { + const oauth = await this.setOAuthProvider(name, options) + + return { + access_token: oauth.access_token, + refresh_token: oauth.refresh_token + }; + }, + + authorize: async function(options) { + const oauth = await this.getOAuthProvider(options.provider); + + return oauth.authorize(this.parse(options.scopes), this.parse(options.params)); + }, + + refresh: async function(options) { + const oauth = await this.getOAuthProvider(options.provider); + + return oauth.refreshToken(this.parse(options.refresh_token)); + }, + +}; \ No newline at end of file diff --git a/lib/modules/otp.js b/lib/modules/otp.js new file mode 100644 index 0000000..dcb53f3 --- /dev/null +++ b/lib/modules/otp.js @@ -0,0 +1,174 @@ +const crypto = require('crypto'); +const base32 = require('../core/base32'); + +const secretLength = { + 'sha1': 20, + 'sha224': 28, + 'sha256': 32, + 'sha384': 48, + 'sha512': 64, + 'sha3-224': 28, + 'sha3-256': 32, + 'sha3-384': 48, + 'sha3-512': 64, +}; + +const hotp = {}; // HOTP: An HMAC-Based One-Time Password Algorithm +const totp = {}; // TOTP: Time-Based One-Time Password Algorithm + +hotp.counter = 0n; +hotp.defaults = { + algorithm: 'sha1', + digits: 6, +}; + +hotp.generate = function (secret, opts) { + opts = { ...hotp.defaults, ...opts }; + const counter = opts.counter == null ? ++hotp.counter : opts.counter; + const buffer = Buffer.alloc(8); + buffer.writeBigUInt64BE(BigInt(counter)); + const hash = crypto.createHmac(opts.algorithm, secret).update(buffer.slice(0, secretLength[opts.algorithm])).digest(); + return truncate(hash, opts.digits); +}; + +hotp.validate = function (code, secret, opts) { + opts = { ...hotp.defaults, ...opts }; + if (opts.counter == null) opts.counter = hotp.counter; + if (code === hotp.generate(secret, opts)) return true; + if (typeof opts.window == 'number' && opts.window > 0) { + for (let n = 1; n < opts.window + 1; n++) { + if (code === hotp.generate(secret, { ...opts, counter: opts.counter - n })) { + return -n; + } + } + } + return false; +}; + +totp.defaults = { + ...hotp.defaults, + time: null, + period: 30, +}; + +totp.generate = function(secret, opts) { + opts = { ...totp.defaults, ...opts }; + const counter = ~~((opts.time || Date.now()) / 1000 / opts.period); + return hotp.generate(secret, { ...opts, counter }); +}; + +totp.validate = function(code, secret, opts) { + opts = { ...totp.defaults, ...opts }; + const counter = ~~((opts.time || Date.now()) / 1000 / opts.period); + return code === hotp.generate(secret, { ...opts, counter }); +}; + +function truncate(hash, digits) { + const offset = hash[hash.length-1] & 0xf; + const binary = (hash[offset] & 0x7f) << 24 + | (hash[offset+1] & 0xff) << 16 + | (hash[offset+2] & 0xff) << 8 + | (hash[offset+3] & 0xff); + const otp = binary % Math.pow(10, digits); + + return String(otp).padStart(digits, '0'); +}; + +module.exports = { + + hotpGenerage (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.hotpGenerate: secret is required.'); + const counter = this.parseOptional(options.counter, 'number', null); + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return hotp.generate(secret, { counter, digits, algorithm }); + }, + + hotpValidate (options) { + const code = this.parseRequired(options.code, 'string', 'otp.hotpValidate: code is required.'); + const secret = this.parseRequired(options.secret, 'string', 'otp.hotpValidate: secret is required.'); + const counter = this.parseOptional(options.counter, 'number', null); + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return hotp.validate(code, secret, { counter, digits, algorithm }); + }, + + totpGenerate (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.totpGenerate: secret is required.'); + const time = this.parseOptional(options.time, 'string', null); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + if (typeof time == 'string') time = +(new Date(time)); + + return totp.generate(secret, { time, period, digits, algorithm }); + }, + + totpValidate (options) { + const code = this.parseRequired(options.code, 'string', 'otp.totpValidate: code is required.'); + const secret = this.parseRequired(options.secret, 'string', 'otp.totpValidate: secret is required.'); + const time = this.parseOptional(options.time, 'string', null); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + if (typeof time == 'string') time = +(new Date(time)); + + return totp.validate(code, secret, { time, period, digits, algorithm }); + }, + + // https://git.coolaj86.com/coolaj86/browser-authenticator.js + // 20 cryptographically random binary bytes (160-bit key) + generateSecret (options) { + const size = this.parseOptional(options.size, 'number', 20); + return base32.encode(crypto.randomBytes(size), { padding: false }).toString(); + }, + + // generates a 6-digit (20-bit) decimal time-based token + generateToken (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.generateToken: secret is required.'); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return totp.generate(base32.decode(secret), { period, digits, algorithm }); + }, + + // validates a time-based token within a +/- 30 second (90 seconds) window + //returns null on failure or an object such as { delta: 0 } on success + verifyToken (options) { + const code = this.parseRequired(options.code, 'string', 'otp.verifyToken: code is required.') + const secret = this.parseRequired(options.secret, 'string', 'otp.verifyToken: secret is required.'); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return code === totp.generate(base32.decode(secret), { period, digits, algorithm, window: 1 }); + }, + + // generates an OTPAUTH:// scheme URI for QR Code generation. + // https://github.com/google/google-authenticator/wiki/Key-Uri-Format + generateOtpUri (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.generateTotpUri: secret is required.'); + const issuer = this.parseRequired(options.issuer, 'string', 'otp.generateTotpUri: issuer is required.'); + const account = this.parseRequired(options.account, 'string', 'otp.generateTotpUri: account is required.'); + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return `otpauth://totp/${encodeURI(issuer)}:${encodeURI(account)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=${encodeURIComponent(algorithm.toUpperCase())}&digits=${digits}&period=${period}`; + }, + + generateQrCodeUrl (options) { + const data = this.parseRequired(options.data, 'string', 'otp.generateQrCodeUrl: data is required.'); + const size = this.parseOptional(options.size, 'number', 200); + const ecl = this.parseOptional(options.ecl, 'string', 'L'); // error_correction_level: L | M | Q | H + const margin = this.parseOptional(options.margin, 'number', 4); + + return `https://www.google.com/chart?cht=qr&chs=${size}x${size}&chl=${encodeURIComponent(data)}&chld=${ecl}|${margin}`; + }, + +}; \ No newline at end of file diff --git a/lib/modules/passkeys.js b/lib/modules/passkeys.js new file mode 100644 index 0000000..5215b99 --- /dev/null +++ b/lib/modules/passkeys.js @@ -0,0 +1,86 @@ +module.exports = { + + // rpName: User-visible, "friendly" website/service name + // rpID: Valid domain name (after `https://`) + // userID: User's website-specific unique ID (must be a string, do not use integer, user GUID instead) + // userName: User's website-specific username (email, etc...) + // userDisplayName: User's actual name + // timeout: How long (in ms) the user can take to complete attestation + // attestationType: Specific attestation statement + // excludeCredentials: Authenticators registered by the user so the user can't register the same credential multiple times + // supportedAlgorithmIDs Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms + async generateRegistrationOptions (options) { + const { generateRegistrationOptions } = require('@simplewebauthn/server'); + const rpName = this.parseRequired(options.rpName, 'string', 'passkeys.generateRegistrationOptions: rpName is required.'); + const rpID = this.parseRequired(options.rpID, 'string', 'passkeys.generateRegistrationOptions: rpID is required.'); + const userID = this.parseRequired(options.userID, 'string', 'passkeys.generateRegistrationOptions: userID is required.'); + const userName = this.parseRequired(options.userName, 'string', 'passkeys.generateRegistrationOptions: userName is required.'); + const userDisplayName = this.parseOptional(options.userDisplayName, 'string', ''); + const timeout = this.parseOptional(options.timeout, 'number', 60000); + const attestationType = this.parseOptional(options.attestationType, 'string', 'none'); // "direct" | "enterprise" | "indirect" | "none" + const excludeCredentials = this.parseOptional(options.excludeCredentials, 'object', []); // array[{id, transports}] + const supportedAlgorithmIDs = this.parseOptional(options.supportedAlgorithmIDs, 'object', [-8, -7, -257]) // array[number] (-8 requires Node 18 LTS) + // authenticator selection criteria (https://simplewebauthn.dev/docs/packages/server#1-generate-registration-options) + const residentKey = 'preferred'; // "discouraged" | "preferred" | "required" + const userVerification = 'preferred'; // "discouraged" | "preferred" | "required" + + return await generateRegistrationOptions({ + rpName, rpID, userID, userName, userDisplayName, + timeout, attestationType, excludeCredentials, + supportedAlgorithmIDs, authenticatorSelection: { + residentKey, userVerification, + }, + }); + }, + + // response: Response returned by **@simplewebauthn/browser**'s `startAuthentication()` + // expectedChallenge: The base64url-encoded `options.challenge` returned by `generateRegistrationOptions()` + // expectedOrigin: Website URL (or array of URLs) that the registration should have occurred on + // expectedRPID: RP ID (or array of IDs) that was specified in the registration options + async verifyRegistrationResponse (options) { + const { verifyRegistrationResponse } = require('@simplewebauthn/server'); + const response = this.parseRequired(options.response, 'object', 'passkeys.verifyRegistrationResponse: response is required.'); + const expectedChallenge = this.parseRequired(options.expectedChallenge, 'string', 'passkeys.verifyRegistrationResponse: expectedChallenge is required.'); + const expectedOrigin = this.parseRequired(options.expectedOrigin, 'string', 'passkeys.verifyRegistrationResponse: expectedOrigin is required.'); + const expectedRPID = this.parseRequired(options.expectedRPID, 'string', 'passkeys.verifyRegistrationResponse: expectedRPID is required.'); + + return await verifyRegistrationResponse({ + response, expectedChallenge, expectedOrigin, expectedRPID, + }); + }, + + // allowCredentials: Authenticators previously registered by the user, if any. If undefined the client will ask the user which credential they want to use + // timeout: How long (in ms) the user can take to complete authentication + // userVerification: Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise set to `'preferred'` or `'required'` as desired. + // rpID: Valid domain name (after `https://`) + async generateAuthenticationOptions (options) { + const { generateAuthenticationOptions } = require('@simplewebauthn/server'); + const allowCredentials = this.parseOptional(options.allowCredentials, 'object', undefined); + const timeout = this.parseOptional(options.timeout, 'number', 60000); + const userVerification = this.parseOptional(options.userVerification, 'string', 'preferred'); // "discouraged" | "preferred" | "required" + const rpID = this.parseOptional(options.rpID, 'string', undefined); + + return await generateAuthenticationOptions({ + allowCredentials, timeout, userVerification, rpID, + }); + }, + + // response: Response returned by **@simplewebauthn/browser**'s `startAssertion()` + // expectedChallenge: The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()` + // expectedOrigin: Website URL (or array of URLs) that the registration should have occurred on + // expectedRPID: RP ID (or array of IDs) that was specified in the registration options + // authenticator: An internal {@link AuthenticatorDevice} matching the credential's ID + async verifyAuthenticationResponse (options) { + const { verifyAuthenticationResponse } = require('@simplewebauthn/server'); + const response = this.parseRequired(options.response, 'object', 'passkeys.verifyAuthenticationResponse: response is required.'); + const expectedChallenge = this.parseRequired(options.expectedChallenge, 'string', 'passkeys.verifyAuthenticationResponse: expectedChallenge is required.'); + const expectedOrigin = this.parseRequired(options.expectedOrigin, 'string', 'passkeys.verifyAuthenticationResponse: expectedOrigin is required.'); + const expectedRPID = this.parseRequired(options.expectedRPID, 'string', 'passkeys.verifyAuthenticationResponse: expectedRPID is required.'); + const authenticator = this.parseRequired(options.authenticator, 'object', 'passkeys.verifyAuthenticationResponse: authenticator is required.') + + return await verifyAuthenticationResponse({ + response, expectedChallenge, expectedOrigin, expectedRPID, authenticator, + }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/ratelimit.js b/lib/modules/ratelimit.js new file mode 100644 index 0000000..273b40a --- /dev/null +++ b/lib/modules/ratelimit.js @@ -0,0 +1,84 @@ +module.exports = { + /** + * Setup a rate limiter instance + * + * @param {Object} options + * @param {number} options.points - Maximum number of points + * @param {number} options.duration - Duration in seconds + * @param {number} options.blockDuration - Duration in seconds to block the user + * @param {string} name - The name of the rate limiter instance + */ + setup: function (options, name) { + if (!name) throw new Error('rateLimit.setup has no name.'); + this.setRateLimiter(name, options); + }, + + /** + * Consume points from a rate limiter instance + * + * @typedef {Object} options + * @param {string} options.instance - The rate limiter instance name + * @param {number} [options.points=1] - The number of points to consume + * @param {number} [options.status=429] - The status code to return + * @param {string} [options.message=Too Many Requests] - The message to return + * @param {boolean} [options.throw=false] - Throw an error instead of sending a response + * + * @return {Promise} - Resolves if the points were consumed, rejects if the rate limit was exceeded and options.throw is true + */ + consume: function (options) { + options = options || {}; + options.points = options.points || 1; + options.status = options.status || 429; + options.message = options.message || 'Too Many Requests'; + + if (options.instance) { + const rateLimiter = this.getRateLimiter(options.instance); + + return rateLimiter.consume(this.req.ip, options.points).catch(() => { + if (options.throw) { + throw new Error(options.message); + } else { + if (this.req.is('json')) { + this.res.status(options.status).json({ error: options.message }); + } else { + this.res.status(options.status).send(options.message); + } + } + }); + } else if (this.req.app.rateLimiter) { + const config = require('../setup/config'); + let isPrivate = false; + let key = this.req.ip; + + if (this.req.app.privateRateLimiter) { + if (this.req.session && this.req.session[config.rateLimit.private.provider + 'Id']) { + isPrivate = true; + key = this.req.session[config.rateLimit.private.provider + 'Id']; + } + } + + const limit = isPrivate ? config.rateLimit.private.points : config.rateLimit.points; + const duration = isPrivate ? config.rateLimit.private.duration : config.rateLimit.duration; + this.req.app[isPrivate ? 'privateRateLimiter' : 'rateLimiter'].consume(key, points).then((rateLimiterRes) => { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + this.res.set('RateLimit-Policy', `${limit};w=${duration}`); + this.res.set('RateLimit', `limit=${limit}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + next(); + }).catch((rateLimiterRes) => { + if (options.throw) { + throw new Error(options.message); + } else { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + this.res.set('RateLimit-Policy', `${limit};w=${duration}`); + this.res.set('RateLimit', `limit=${limit}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + this.res.set('Retry-After', reset); + if (this.req.is('json')) { + this.res.status(options.status).json({ error: options.message }); + } else { + this.res.status(options.status).send(options.message); + } + } + }); + } + }, +}; diff --git a/lib/modules/recaptcha.js b/lib/modules/recaptcha.js new file mode 100644 index 0000000..2f5266f --- /dev/null +++ b/lib/modules/recaptcha.js @@ -0,0 +1,47 @@ +const querystring = require('querystring'); +const https = require('https'); + +exports.validate = function(options) { + const secret = this.parseRequired(options.secret, 'string', 'recaptcha.validate: secret is required.'); + const msg = this.parseOptional(options.msg, 'string', 'Recaptcha check failed.'); + + const response = this.req.body['g-recaptcha-response']; + const remoteip = this.req.ip; + const data = querystring.stringify({ secret, response, remoteip }); + + return new Promise((resolve, reject) => { + const req = https.request('https://www.google.com/recaptcha/api/siteverify', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': data.length + } + }, (res) => { + let body = ''; + + res.setEncoding('utf8'); + res.on('data', (chunk) => body += chunk); + res.on('end', () => { + if (res.statusCode >= 400) return reject(body); + + if (body.charCodeAt(0) === 0xFEFF) { + body = body.slice(1); + } + + body = JSON.parse(body); + + if (!body.success) { + this.res.status(400).json({ + form: { 'g-recaptcha-response': msg } + }); + } + + resolve(body); + }); + }); + + req.on('error', reject); + req.write(data); + req.end(); + }); +}; \ No newline at end of file diff --git a/lib/modules/redis.js b/lib/modules/redis.js new file mode 100644 index 0000000..143f0b7 --- /dev/null +++ b/lib/modules/redis.js @@ -0,0 +1,237 @@ +const client = global.redisClient; + +if (client) { + const { promisify } = require('util'); + const commands = {}; + + [ + 'append', 'decr', 'decrby', 'del', 'exists', 'get', 'getset', 'incr', 'incrby', + 'mget', 'set', 'setex', 'setnx', 'strlen', 'lindex', 'linsert', 'llen', 'lpop', + 'lpos', 'lpush', 'lpushx', 'lrange', 'lrem', 'lset', 'ltrim', 'rpop', 'rpush', + 'rpushx', 'copy', 'del', 'expire', 'expireat', 'keys', 'persist', 'pexpire', + 'pexpireat', 'pttl', 'randomkey', 'rename', 'renamenx', 'touch', 'ttl', 'type', + 'unlink' + ].forEach(command => { + commands[command] = promisify(client[command]).bind(client); + }); + + exports.append = function(options) { + options = this.parse(options); + return commands.append(options.key, options.value); + }; + + exports.decr = function(options) { + options = this.parse(options); + return commands.decr(options.key); + }; + + exports.decrby = function(options) { + options = this.parse(options); + return commands.decrby(options.key, options.decrement); + }; + + exports.del = function(options) { + options = this.parse(options); + return commands.del(options.key); + }; + + exports.exists = function(options) { + options = this.parse(options); + return commands.exists(options.key); + }; + + exports.get = function(options) { + options = this.parse(options); + return commands.get(options.key); + }; + + exports.getset = function(options) { + options = this.parse(options); + return commands.getset(options.key, options.value); + }; + + exports.incr = function(options) { + options = this.parse(options); + return commands.incr(options.key); + }; + + exports.incrby = function(options) { + options = this.parse(options); + return commands.incrby(options.key, options.increment); + }; + + exports.mget = function(options) { + options = this.parse(options); + return commands.mget(options.keys); + }; + + exports.set = function(options) { + options = this.parse(options); + return commands.set(options.key, options.value); + }; + + exports.setex = function(options) { + options = this.parse(options); + return commands.setex(options.key, options.seconds, options.value); + }; + + exports.setnx = function(options) { + options = this.parse(options); + return commands.setnx(options.key, options.value); + }; + + exports.strlen = function(options) { + options = this.parse(options); + return commands.strlen(options.key); + }; + + exports.lindex = function(options) { + options = this.parse(options); + return commands.lindex(options.key, options.index); + }; + + exports.linsert = function(options) { + options = this.parse(options); // position: BEFORE|AFTER + return commands.linsert(options.key, options.position, options.pivot, options.element); + }; + + exports.llen = function(options) { + options = this.parse(options); + return commands.llen(options.key); + }; + + exports.lpop = function(options) { + options = this.parse(options); + return commands.lpop(options.key, options.count); + }; + + exports.lpos = function(options) { + options = this.parse(options); + return commands.lpos(options.key, options.element); + }; + + exports.lpush = function(options) { + options = this.parse(options); + return commands.lpush(options.key, options.element); + }; + + exports.lpushx = function(options) { + options = this.parse(options); + return commands.lpushx(options.key, options.element); + }; + + exports.lrange = function(options) { + options = this.parse(options); + return commands.lrange(options.key, options.start, options.stop); + }; + + exports.lrem = function(options) { + options = this.parse(options); + return commands.lrem(options.key, options.count, options.element); + }; + + exports.lset = function(options) { + options = this.parse(options); + return commands.lset(options.key, options.index, options.element); + }; + + exports.ltrim = function(options) { + options = this.parse(options); + return commands.ltrim(options.key, options.start, options.stop); + }; + + exports.rpop = function(options) { + options = this.parse(options); + return commands.rpop(options.key, options.count); + }; + + exports.rpush = function(options) { + options = this.parse(options); + return commands.rpush(options.key, options.element); + }; + + exports.rpushx = function(options) { + options = this.parse(options); + return commands.rpushx(options.key, options.element); + }; + + exports.copy = function(options) { + options = this.parse(options); + return commands.copy(options.source, options.destination); + }; + + exports.del = function(options) { + options = this.parse(options); + return commands.del(options.key); + }; + + exports.expire = function(options) { + options = this.parse(options); + return commands.expire(options.key, options.seconds); + }; + + exports.expireat = function(options) { + options = this.parse(options); + return commands.expireat(options.key, options.timestamp); + }; + + exports.keys = function(options) { + options = this.parse(options); + return commands.keys(options.pattern); + }; + + exports.persist = function(options) { + options = this.parse(options); + return commands.persist(options.key); + }; + + exports.pexpire = function(options) { + options = this.parse(options); + return commands.pexpire(options.key, options.milliseconds); + }; + + exports.pexpireat = function(options) { + options = this.parse(options); + return commands.pexpireat(options.key, options.mstimestamp) + }; + + exports.pttl = function(options) { + options = this.parse(options); + return commands.pttl(options.key); + }; + + exports.randomkey = function(options) { + options = this.parse(options); + return commands.randomkey(); + }; + + exports.rename = function(options) { + options = this.parse(options); + return commands.rename(options.key, options.newkey); + }; + + exports.renamenx = function(options) { + options = this.parse(options); + return commands.renamenx(options.key, options.newkey); + }; + + exports.touch = function(options) { + options = this.parse(options); + return commands.touch(options.key); + }; + + exports.ttl = function(options) { + options = this.parse(options); + return commands.ttl(options.key); + }; + + exports.type = function(options) { + options = this.parse(options); + return commands.type(options.key); + }; + + exports.unlink = function(options) { + options = this.parse(options); + return commands.unlink(options.key); + }; +} \ No newline at end of file diff --git a/lib/modules/s3.js b/lib/modules/s3.js new file mode 100644 index 0000000..bebda02 --- /dev/null +++ b/lib/modules/s3.js @@ -0,0 +1,174 @@ +const fs = require('fs-extra'); +const mime = require('mime-types'); +const { join, basename, dirname } = require('path'); +const { toSystemPath } = require('../core/path'); + +module.exports = { + + provider: function (options, name) { + this.setS3Provider(name, options); + }, + + createBucket: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.createBucket: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.createBucket: bucket is required.'); + const ACL = this.parseOptional(options.acl, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.createBucket({ Bucket, ACL }); + }, + + listBuckets: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.listBuckets: provider is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.listBuckets({}); + }, + + deleteBucket: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.deleteBucket: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.deleteBucket: bucket is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.deleteBucket({ Bucket }); + }, + + listFiles: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.listFiles: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.listFiles: bucket is required.'); + const MaxKeys = this.parseOptional(options.maxKeys, 'number', undefined); + const Prefix = this.parseOptional(options.prefix, 'string', undefined); + const ContinuationToken = this.parseOptional(options.continuationToken, 'string', undefined); + const StartAfter = this.parseOptional(options.startAfter, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.listObjectsV2({ Bucket, MaxKeys, Prefix, ContinuationToken, StartAfter }); + }, + + putFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.uploadFile: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.uploadFile: bucket is required.'); + const path = toSystemPath(this.parseRequired(options.path, 'string', 's3.uploadFile: path is required.')); + const Key = this.parseRequired(options.key, 'string', 's3.uploadFile: key is required.'); + const ContentType = this.parseOptional(options.contentType, 'string', mime.lookup(Key) || 'application/octet-stream'); + const ContentDisposition = this.parseOptional(options.contentDisposition, 'string', undefined); + const ACL = this.parseOptional(options.acl, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + let Body = fs.createReadStream(path); + + const result = await s3.putObject({ Bucket, ACL, Key, ContentType, ContentDisposition, Body }); + + try { + const endpoint = await s3.config.endpoint(); + result.Location = `https://${Bucket}.${endpoint.hostname}/${Key}`; + } catch (e) {} + + + return result; + }, + + getFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.getFile: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.getFile: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.getFile: key is required.'); + const path = toSystemPath(this.parseRequired(options.path, 'string', 's3.getFile: path is required.')); + const stripKeyPath = this.parseOptional(options.stripKeyPath, 'boolean', false); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const file = Key; + if (stripKeyPath) file = basename(file); + const destination = join(path, file); + + await fs.ensureDir(dirname(destination)); + + const writer = fs.createWriteStream(destination); + + const { Body } = await s3.getObject({ Bucket, Key }); + + Body.pipe(writer); + + return new Promise((resolve, reject) => { + writer.on('finish', resolve); + writer.on('error', reject); + }); + }, + + deleteFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.deleteFile: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.deleteFile: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.deleteFile: key is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.deleteObject({ Bucket, Key }); + }, + + downloadFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.downloadFile: profider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.downloadFile: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.downloadFile: key is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const data = await s3.headObject({ Bucket, Key }); + + this.res.set('Content-Length', data.ContentLength); + this.res.attachment(basename(Key)); + + const { Body } = await s3.getObject({ Bucket, Key }); + + Body.pipe(this.res); + + this.noOutput = true; + }, + + signDownloadUrl: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.signDownloadUrl: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.signDownloadUrl: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.signDownloadUrl: key is required.'); + const expiresIn = this.parseOptional(options.expires, 'number', 300); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); + const { GetObjectCommand } = require('@aws-sdk/client-s3'); + const command = new GetObjectCommand({ Bucket, Key }); + + return getSignedUrl(s3, command, { expiresIn }); + }, + + signUploadUrl: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.signUploadUrl: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.signUploadUrl: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.signUploadUrl: key is required.'); + const ContentType = this.parseOptional(options.contentType, 'string', mime.lookup(Key) || 'application/octet-stream'); + const expiresIn = this.parseOptional(options.expires, 'number', 300); + const ACL = this.parseOptional(options.acl, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); + const { PutObjectCommand } = require('@aws-sdk/client-s3'); + const command = new PutObjectCommand({ Bucket, Key, ContentType, ACL }); + + return getSignedUrl(s3, command, { expiresIn }); + } + +}; \ No newline at end of file diff --git a/lib/modules/sockets.js b/lib/modules/sockets.js new file mode 100644 index 0000000..b03291e --- /dev/null +++ b/lib/modules/sockets.js @@ -0,0 +1,172 @@ +/** + * emits an event to all clients filtered by the given options + * @param {string} namespace - only to the specified namespace + * @param {string} room - emit only to the specified room (can be combined with namespace) + * @param {string} eventName - the name of the event + * @param {object} params - any parameters/data to send with the event + */ +exports.emit = function(options) { + if (this.io) { + options = this.parse(options); + + if (options.namespace) { + if (options.room) { + this.io.of(options.namespace).to(options.room).emit(options.eventName, options.params); + } else { + this.io.of(options.namespace).emit(options.eventName, options.params); + } + } else { + if (options.room) { + this.io.in(options.room).emit(options.eventName, options.params); + } else { + this.io.emit(options.eventName, options.params); + } + } + } +}; + +/** + * emit an event to all clients except the sender + * @param {string} room - broadcast in a specific room + * @param {string} eventName - the name of the event + * @param {object} params - the parameters/data to send with the event + */ +exports.broadcast = function(options) { + if (this.socket) { + options = this.parse(options); + + if (options.room) { + this.socket.to(options.room).emit(options.eventName, options.params); + } else { + this.socket.broadcast.emit(options.eventName, options.params); + } + } +}; + +/** + * emit an event to client and wait for an answer + * @param {string} room - broadcast in a specific room + * @param {string} eventName - the name of the event + * @param {object} params - the parameters/data to send with the event + */ +exports.request = function(options) { + if (this.socket) { + options = this.parse(options); + + return new Promise((resolve) => { + this.socket.emit(options.eventName, options.params, resolve); + }); + } + + return null; +}; + +/** + * send a private meessage to a socket + * @param {string} socketId - the socket id to send the message to + * @param {string} eventName - the name of the event + * @param {object} params - the parameters/data to send with the event + */ +exports.message = function(options) { + if (this.io) { + options = this.parse(options); + + this.io.to(options.socketId).emit(options.eventName, options.params); + } +}; + +/** + * special serverconnect refresh broadcast + * @param {string} action - the action that require refresh + */ +exports.refresh = async function(options) { + if (this.io) { + options = this.parse(options); + + // Do we have a global redis client? + if (global.redisClient) { + try { // ignore any errors here + let wsKeys = await global.redisClient.keys('ws:' + options.action + ':*'); + if (wsKeys.length) await global.redisClient.del(wsKeys); + let scKeys = await global.redisClient.keys('erc:' + '/api/' + options.action + '*'); + if (scKeys.length) await global.redisClient.del(scKeys); + } catch (e) { + console.error(e); + } + } + + this.io.of('/api').emit(options.action, options.params); + } +}; + +/** + * let current client join a room + * @param {string} room - the room to join + */ +exports.join = function(options) { + if (this.socket) { + this.socket.join(this.parse(options.room)); + } +}; + +/** + * let current client leave a room + * @param {string} room - the room to leave + */ +exports.leave = function(options) { + if (this.socket) { + this.socket.leave(this.parse(options.room)); + } +}; + +/** + * get the socket id of the current client + */ +exports.identify = function(options) { + return this.socket ? this.socket.id : null; +}; + +/** + * get the rooms the current client has joined + */ +exports.rooms = function(options) { + return this.socket ? Array.from(this.socket.rooms) : []; +}; + +/** + * get all the rooms + * @param {string} namespace - the namespace + */ +exports.allRooms = async function(options) { + if (this.io) { + let adapter = io.of(options.namespace || '/').adapter; + + if (typeof adapter.allRooms == 'function') { + return Array.from(await adapter.allRooms()); + } else if (adapter.rooms) { + return Array.from(adapter.rooms.keys()); + } + } + + return []; +}; + +/** + * get all the connected sockets + * @param {string} namespace - the namespace + * @param {string} room - return only clients in a specific room + */ +exports.allSockets = async function(options) { + if (this.io) { + options = this.parse(options); + + if (options.room) { + return Array.from(await this.io.of(options.namespace || '/').in(options.room).allSockets()); + } else { + return Array.from(await this.io.of(options.namespace || '/').allSockets()); + } + } + + return []; +}; + diff --git a/lib/modules/stripe.js b/lib/modules/stripe.js new file mode 100644 index 0000000..6de3fa0 --- /dev/null +++ b/lib/modules/stripe.js @@ -0,0 +1,6400 @@ + +const config = require('../setup/config'); +const Stripe = require('stripe'); + +const stripe = Stripe(config.stripe.secretKey); + +function convertExtraOptions(__extra) { + var extraOptions = null; + if (__extra && typeof __extra == "object" && Object.keys(__extra).length) { + extraOptions = {}; + for (var option in __extra) { + extraOptions[toCamelCase(option)] = __extra[option]; + } + } + return extraOptions; +} + +function toCamelCase(str) { + if (str == null) return str; + return String(str) + .toLowerCase() + .replace(/[\s_]+(\S)/g, function(a, b) { + return b.toUpperCase() + }); +} + + +// /v1/account_links - post +exports.createAccountLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.refresh_url && !options.refresh_url.includes('://')) { + options.refresh_url = this.parse('{{$_SERVER.BASE_URL}}') + options.refresh_url; + } + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.accountLinks.create(options,__extra); +}; + +// /v1/account_sessions - post +exports.createAccountSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.accountSessions.create(options,__extra); +}; + +// /v1/accounts - get +exports.listAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.accounts.list(options,__extra); +}; + +// /v1/accounts - post +exports.createAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.accounts.create(options,__extra); +}; + +// /v1/accounts/{account} - delete +exports.deleteAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.deleteAccount: account is required.'); + delete options.account; + return stripe.accounts.del(account, options,__extra); +}; + +// /v1/accounts/{account} - get +exports.retrieveAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccount: account is required.'); + delete options.account; + return stripe.accounts.retrieve(account, options,__extra); +}; + +// /v1/accounts/{account} - post +exports.updateAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccount: account is required.'); + delete options.account; + return stripe.accounts.update(account, options,__extra); +}; + +// /v1/accounts/{account}/capabilities - get +exports.listAccountCapabilities = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listAccountCapabilities: account is required.'); + delete options.account; + return stripe.accounts.listCapabilities(account, options,__extra); +}; + +// /v1/accounts/{account}/capabilities/{capability} - get +exports.retrieveAccountCapabilitie = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccountCapabilitie: account is required.'); + delete options.account; + const capability = this.parseRequired(options.capability, 'string', 'stripe.retrieveAccountCapabilitie: capability is required.'); + delete options.capability; + return stripe.accounts.retrieveCapabilitie(account, capability, options,__extra); +}; + +// /v1/accounts/{account}/capabilities/{capability} - post +exports.updateAccountCapabilitie = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccountCapabilitie: account is required.'); + delete options.account; + const capability = this.parseRequired(options.capability, 'string', 'stripe.updateAccountCapabilitie: capability is required.'); + delete options.capability; + return stripe.accounts.updateCapabilitie(account, capability, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts - get +exports.listAccountExternalAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listAccountExternalAccounts: account is required.'); + delete options.account; + return stripe.accounts.listExternalAccounts(account, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts - post +exports.createAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.createAccountExternalAccount: account is required.'); + delete options.account; + return stripe.accounts.createExternalAccount(account, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts/{id} - delete +exports.deleteAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.deleteAccountExternalAccount: account is required.'); + delete options.account; + const id = this.parseRequired(options.id, 'string', 'stripe.deleteAccountExternalAccount: id is required.'); + delete options.id; + return stripe.accounts.deleteExternalAccount(account, id, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts/{id} - get +exports.retrieveAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccountExternalAccount: account is required.'); + delete options.account; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveAccountExternalAccount: id is required.'); + delete options.id; + return stripe.accounts.retrieveExternalAccount(account, id, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts/{id} - post +exports.updateAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccountExternalAccount: account is required.'); + delete options.account; + const id = this.parseRequired(options.id, 'string', 'stripe.updateAccountExternalAccount: id is required.'); + delete options.id; + return stripe.accounts.updateExternalAccount(account, id, options,__extra); +}; + +// /v1/accounts/{account}/login_links - post +exports.createAccountLoginLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.createAccountLoginLink: account is required.'); + delete options.account; + return stripe.accounts.createLoginLink(account, options,__extra); +}; + +// /v1/accounts/{account}/persons - get +exports.listAccountPersons = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listAccountPersons: account is required.'); + delete options.account; + return stripe.accounts.listPersons(account, options,__extra); +}; + +// /v1/accounts/{account}/persons - post +exports.createAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.createAccountPerson: account is required.'); + delete options.account; + return stripe.accounts.createPerson(account, options,__extra); +}; + +// /v1/accounts/{account}/persons/{person} - delete +exports.deleteAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.deleteAccountPerson: account is required.'); + delete options.account; + const person = this.parseRequired(options.person, 'string', 'stripe.deleteAccountPerson: person is required.'); + delete options.person; + return stripe.accounts.deletePerson(account, person, options,__extra); +}; + +// /v1/accounts/{account}/persons/{person} - get +exports.retrieveAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccountPerson: account is required.'); + delete options.account; + const person = this.parseRequired(options.person, 'string', 'stripe.retrieveAccountPerson: person is required.'); + delete options.person; + return stripe.accounts.retrievePerson(account, person, options,__extra); +}; + +// /v1/accounts/{account}/persons/{person} - post +exports.updateAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccountPerson: account is required.'); + delete options.account; + const person = this.parseRequired(options.person, 'string', 'stripe.updateAccountPerson: person is required.'); + delete options.person; + return stripe.accounts.updatePerson(account, person, options,__extra); +}; + +// /v1/accounts/{account}/reject - post +exports.rejectAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.rejectAccount: account is required.'); + delete options.account; + return stripe.accounts.reject(account, options,__extra); +}; + +// /v1/apple_pay/domains - get +exports.listApplePayDomains = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.applePay.domains.list(options,__extra); +}; + +// /v1/apple_pay/domains - post +exports.createApplePayDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.applePay.domains.create(options,__extra); +}; + +// /v1/apple_pay/domains/{domain} - delete +exports.deleteApplePayDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const domain = this.parseRequired(options.domain, 'string', 'stripe.deleteApplePayDomain: domain is required.'); + delete options.domain; + return stripe.applePay.domains.del(domain, options,__extra); +}; + +// /v1/apple_pay/domains/{domain} - get +exports.retrieveApplePayDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const domain = this.parseRequired(options.domain, 'string', 'stripe.retrieveApplePayDomain: domain is required.'); + delete options.domain; + return stripe.applePay.domains.retrieve(domain, options,__extra); +}; + +// /v1/application_fees - get +exports.listApplicationFees = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.applicationFees.list(options,__extra); +}; + +// /v1/application_fees/{fee}/refunds/{id} - get +exports.retrieveApplicationFeeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const fee = this.parseRequired(options.fee, 'string', 'stripe.retrieveApplicationFeeRefund: fee is required.'); + delete options.fee; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveApplicationFeeRefund: id is required.'); + delete options.id; + return stripe.applicationFees.retrieveRefund(fee, id, options,__extra); +}; + +// /v1/application_fees/{fee}/refunds/{id} - post +exports.updateApplicationFeeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const fee = this.parseRequired(options.fee, 'string', 'stripe.updateApplicationFeeRefund: fee is required.'); + delete options.fee; + const id = this.parseRequired(options.id, 'string', 'stripe.updateApplicationFeeRefund: id is required.'); + delete options.id; + return stripe.applicationFees.updateRefund(fee, id, options,__extra); +}; + +// /v1/application_fees/{id} - get +exports.retrieveApplicationFee = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveApplicationFee: id is required.'); + delete options.id; + return stripe.applicationFees.retrieve(id, options,__extra); +}; + +// /v1/application_fees/{id}/refunds - get +exports.listApplicationFeeRefunds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.listApplicationFeeRefunds: id is required.'); + delete options.id; + return stripe.applicationFees.listRefunds(id, options,__extra); +}; + +// /v1/application_fees/{id}/refunds - post +exports.createApplicationFeeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.createApplicationFeeRefund: id is required.'); + delete options.id; + return stripe.applicationFees.createRefund(id, options,__extra); +}; + +// /v1/apps/secrets - get +exports.listAppsSecrets = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const scope = this.parseRequired(options.scope, 'string', 'stripe.listAppsSecrets: scope is required.'); + return stripe.apps.secrets.list(options,__extra); +}; + +// /v1/apps/secrets - post +exports.createAppsSecret = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.apps.secrets.create(options,__extra); +}; + +// /v1/apps/secrets/delete - post +exports.createAppsSecretsDelete = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.apps.secrets.delete.create(options,__extra); +}; + +// /v1/apps/secrets/find - get +exports.retrieveAppsSecretsFind = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const name = this.parseRequired(options.name, 'string', 'stripe.retrieveAppsSecretsFind: name is required.'); + const scope = this.parseRequired(options.scope, 'string', 'stripe.retrieveAppsSecretsFind: scope is required.'); + return stripe.apps.secrets.find.retrieve(options,__extra); +}; + +// /v1/balance - get +exports.retrieveBalance = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.balance.retrieve(options,__extra); +}; + +// /v1/balance_transactions - get +exports.listBalanceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.balanceTransactions.list(options,__extra); +}; + +// /v1/balance_transactions/{id} - get +exports.retrieveBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveBalanceTransaction: id is required.'); + delete options.id; + return stripe.balanceTransactions.retrieve(id, options,__extra); +}; + +// /v1/billing/meter_event_adjustments - post +exports.createBillingMeterEventAdjustment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meterEventAdjustments.create(options,__extra); +}; + +// /v1/billing/meter_events - post +exports.createBillingMeterEvent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meterEvents.create(options,__extra); +}; + +// /v1/billing/meters - get +exports.listBillingMeters = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meters.list(options,__extra); +}; + +// /v1/billing/meters - post +exports.createBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meters.create(options,__extra); +}; + +// /v1/billing/meters/{id} - get +exports.retrieveBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.retrieve(id, options,__extra); +}; + +// /v1/billing/meters/{id} - post +exports.updateBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.update(id, options,__extra); +}; + +// /v1/billing/meters/{id}/deactivate - post +exports.deactivateBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.deactivateBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.deactivate(id, options,__extra); +}; + +// /v1/billing/meters/{id}/event_summaries - get +exports.listBillingMeterEventSummaries = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listBillingMeterEventSummaries: customer is required.'); + const end_time = this.parseRequired(options.end_time, 'string', 'stripe.listBillingMeterEventSummaries: end_time is required.'); + const id = this.parseRequired(options.id, 'string', 'stripe.listBillingMeterEventSummaries: id is required.'); + delete options.id; + const start_time = this.parseRequired(options.start_time, 'string', 'stripe.listBillingMeterEventSummaries: start_time is required.'); + return stripe.billing.meters.listEventSummaries(id, options,__extra); +}; + +// /v1/billing/meters/{id}/reactivate - post +exports.reactivateBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.reactivateBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.reactivate(id, options,__extra); +}; + +// /v1/billing_portal/configurations - get +exports.listBillingPortalConfigurations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billingPortal.configurations.list(options,__extra); +}; + +// /v1/billing_portal/configurations - post +exports.createBillingPortalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.default_return_url && !options.default_return_url.includes('://')) { + options.default_return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.default_return_url; + } + return stripe.billingPortal.configurations.create(options,__extra); +}; + +// /v1/billing_portal/configurations/{configuration} - get +exports.retrieveBillingPortalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.retrieveBillingPortalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.billingPortal.configurations.retrieve(configuration, options,__extra); +}; + +// /v1/billing_portal/configurations/{configuration} - post +exports.updateBillingPortalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.updateBillingPortalConfiguration: configuration is required.'); + delete options.configuration; + if (options.default_return_url && !options.default_return_url.includes('://')) { + options.default_return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.default_return_url; + } + return stripe.billingPortal.configurations.update(configuration, options,__extra); +}; + +// /v1/billing_portal/sessions - post +exports.createBillingPortalSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.billingPortal.sessions.create(options,__extra); +}; + +// /v1/charges - get +exports.listCharges = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.charges.list(options,__extra); +}; + +// /v1/charges - post +exports.createCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.charges.create(options,__extra); +}; + +// /v1/charges/search - get +exports.searchCharges = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchCharges: query is required.'); + return stripe.charges.search(options,__extra); +}; + +// /v1/charges/{charge} - get +exports.retrieveCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.retrieveCharge: charge is required.'); + delete options.charge; + return stripe.charges.retrieve(charge, options,__extra); +}; + +// /v1/charges/{charge} - post +exports.updateCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.updateCharge: charge is required.'); + delete options.charge; + return stripe.charges.update(charge, options,__extra); +}; + +// /v1/charges/{charge}/capture - post +exports.captureCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.captureCharge: charge is required.'); + delete options.charge; + return stripe.charges.capture(charge, options,__extra); +}; + +// /v1/charges/{charge}/refunds - get +exports.listChargeRefunds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.listChargeRefunds: charge is required.'); + delete options.charge; + return stripe.charges.listRefunds(charge, options,__extra); +}; + +// /v1/charges/{charge}/refunds/{refund} - get +exports.retrieveChargeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.retrieveChargeRefund: charge is required.'); + delete options.charge; + const refund = this.parseRequired(options.refund, 'string', 'stripe.retrieveChargeRefund: refund is required.'); + delete options.refund; + return stripe.charges.retrieveRefund(charge, refund, options,__extra); +}; + +// /v1/checkout/sessions - get +exports.listCheckoutSessions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.checkout.sessions.list(options,__extra); +}; + +// /v1/checkout/sessions - post +exports.createCheckoutSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.cancel_url && !options.cancel_url.includes('://')) { + options.cancel_url = this.parse('{{$_SERVER.BASE_URL}}') + options.cancel_url; + } + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + if (options.success_url && !options.success_url.includes('://')) { + options.success_url = this.parse('{{$_SERVER.BASE_URL}}') + options.success_url; + } + if (options && options.lineItemsType) { + if (options.lineItemsType == 'customList' || options.lineItemsType == 'customRef') { + if (options.line_items) { + if (!Array.isArray(options.line_items)) { + if (typeof options.line_items == "object") { + options.line_items = [options.line_items]; + } else { + throw new Error('createCheckoutSession: line_items is not an array or object for references'); + } + } else if (!options.line_items || options.line_items.length == 0) { + throw new Error('createCheckoutSession: line_items is empty!'); + } + } else { + throw new Error('createCheckoutSession: line_items is not set!'); + } + options.line_items = options.line_items.map(item => { + return { + price_data: { + currency: item.currency ? item.currency : 'USD', + product_data: { + name: item.title, + }, + unit_amount_decimal: item.amount, + }, + quantity: item.quantity ? item.quantity : 1, + } + }) + } + delete options.lineItemsType; + } else { + if (options.line_items) { + if (Array.isArray(options.line_items)) { + if (options.line_items.length == 0) { + throw new Error('createCheckoutSession: line_items is empty!'); + } + options.line_items = options.line_items.map(item => { + return { + price: item.price, + quantity: item.quantity ? item.quantity : 1 + } + }) + } else if (typeof options.line_items == "object") { + options.line_items = [options.line_items] + } else if (typeof options.line_items == "string") { + options.line_items = [{price: options.line_items, quantity: 1}] + } + } + } + + return stripe.checkout.sessions.create(options,__extra); +}; + +// /v1/checkout/sessions/{session} - get +exports.retrieveCheckoutSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.retrieveCheckoutSession: session is required.'); + delete options.session; + return stripe.checkout.sessions.retrieve(session, options,__extra); +}; + +// /v1/checkout/sessions/{session}/expire - post +exports.expireCheckoutSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.expireCheckoutSession: session is required.'); + delete options.session; + return stripe.checkout.sessions.expire(session, options,__extra); +}; + +// /v1/checkout/sessions/{session}/line_items - get +exports.listCheckoutSessionLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.listCheckoutSessionLineItems: session is required.'); + delete options.session; + return stripe.checkout.sessions.listLineItems(session, options,__extra); +}; + +// /v1/climate/orders - get +exports.listClimateOrders = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.orders.list(options,__extra); +}; + +// /v1/climate/orders - post +exports.createClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.orders.create(options,__extra); +}; + +// /v1/climate/orders/{order} - get +exports.retrieveClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const order = this.parseRequired(options.order, 'string', 'stripe.retrieveClimateOrder: order is required.'); + delete options.order; + return stripe.climate.orders.retrieve(order, options,__extra); +}; + +// /v1/climate/orders/{order} - post +exports.updateClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const order = this.parseRequired(options.order, 'string', 'stripe.updateClimateOrder: order is required.'); + delete options.order; + return stripe.climate.orders.update(order, options,__extra); +}; + +// /v1/climate/orders/{order}/cancel - post +exports.cancelClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const order = this.parseRequired(options.order, 'string', 'stripe.cancelClimateOrder: order is required.'); + delete options.order; + return stripe.climate.orders.cancel(order, options,__extra); +}; + +// /v1/climate/products - get +exports.listClimateProducts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.products.list(options,__extra); +}; + +// /v1/climate/products/{product} - get +exports.retrieveClimateProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const product = this.parseRequired(options.product, 'string', 'stripe.retrieveClimateProduct: product is required.'); + delete options.product; + return stripe.climate.products.retrieve(product, options,__extra); +}; + +// /v1/climate/suppliers - get +exports.listClimateSuppliers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.suppliers.list(options,__extra); +}; + +// /v1/climate/suppliers/{supplier} - get +exports.retrieveClimateSupplier = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const supplier = this.parseRequired(options.supplier, 'string', 'stripe.retrieveClimateSupplier: supplier is required.'); + delete options.supplier; + return stripe.climate.suppliers.retrieve(supplier, options,__extra); +}; + +// /v1/confirmation_tokens/{confirmation_token} - get +exports.retrieveConfirmationToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const confirmation_token = this.parseRequired(options.confirmation_token, 'string', 'stripe.retrieveConfirmationToken: confirmation_token is required.'); + delete options.confirmation_token; + return stripe.confirmationTokens.retrieve(confirmation_token, options,__extra); +}; + +// /v1/country_specs - get +exports.listCountrySpecs = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.countrySpecs.list(options,__extra); +}; + +// /v1/country_specs/{country} - get +exports.retrieveCountrySpec = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const country = this.parseRequired(options.country, 'string', 'stripe.retrieveCountrySpec: country is required.'); + delete options.country; + return stripe.countrySpecs.retrieve(country, options,__extra); +}; + +// /v1/coupons - get +exports.listCoupons = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.coupons.list(options,__extra); +}; + +// /v1/coupons - post +exports.createCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.coupons.create(options,__extra); +}; + +// /v1/coupons/{coupon} - delete +exports.deleteCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const coupon = this.parseRequired(options.coupon, 'string', 'stripe.deleteCoupon: coupon is required.'); + delete options.coupon; + return stripe.coupons.del(coupon, options,__extra); +}; + +// /v1/coupons/{coupon} - get +exports.retrieveCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const coupon = this.parseRequired(options.coupon, 'string', 'stripe.retrieveCoupon: coupon is required.'); + delete options.coupon; + return stripe.coupons.retrieve(coupon, options,__extra); +}; + +// /v1/coupons/{coupon} - post +exports.updateCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const coupon = this.parseRequired(options.coupon, 'string', 'stripe.updateCoupon: coupon is required.'); + delete options.coupon; + return stripe.coupons.update(coupon, options,__extra); +}; + +// /v1/credit_notes - get +exports.listCreditNotes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.creditNotes.list(options,__extra); +}; + +// /v1/credit_notes - post +exports.createCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.creditNotes.create(options,__extra); +}; + +// /v1/credit_notes/preview - get +exports.retrieveCreditNotesPreview = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.retrieveCreditNotesPreview: invoice is required.'); + return stripe.creditNotes.preview(options,__extra); +}; + +// /v1/credit_notes/preview/lines - get +exports.listCreditNotesPreviewLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.listCreditNotesPreviewLines: invoice is required.'); + return stripe.creditNotes.listPreviewLineItems(options,__extra); +}; + +// /v1/credit_notes/{credit_note}/lines - get +exports.listCreditNoteLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const credit_note = this.parseRequired(options.credit_note, 'string', 'stripe.listCreditNoteLines: credit_note is required.'); + delete options.credit_note; + return stripe.creditNotes.listLines(credit_note, options,__extra); +}; + +// /v1/credit_notes/{id} - get +exports.retrieveCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveCreditNote: id is required.'); + delete options.id; + return stripe.creditNotes.retrieve(id, options,__extra); +}; + +// /v1/credit_notes/{id} - post +exports.updateCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateCreditNote: id is required.'); + delete options.id; + return stripe.creditNotes.update(id, options,__extra); +}; + +// /v1/credit_notes/{id}/void - post +exports.voidCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.voidCreditNote: id is required.'); + delete options.id; + return stripe.creditNotes.void(id, options,__extra); +}; + +// /v1/customer_sessions - post +exports.createCustomerSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.customerSessions.create(options,__extra); +}; + +// /v1/customers - get +exports.listCustomers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.customers.list(options,__extra); +}; + +// /v1/customers - post +exports.createCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.customers.create(options,__extra); +}; + +// /v1/customers/search - get +exports.searchCustomers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchCustomers: query is required.'); + return stripe.customers.search(options,__extra); +}; + +// /v1/customers/{customer} - delete +exports.deleteCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomer: customer is required.'); + delete options.customer; + return stripe.customers.del(customer, options,__extra); +}; + +// /v1/customers/{customer} - get +exports.retrieveCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomer: customer is required.'); + delete options.customer; + return stripe.customers.retrieve(customer, options,__extra); +}; + +// /v1/customers/{customer} - post +exports.updateCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.updateCustomer: customer is required.'); + delete options.customer; + return stripe.customers.update(customer, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions - get +exports.listCustomerBalanceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerBalanceTransactions: customer is required.'); + delete options.customer; + return stripe.customers.listBalanceTransactions(customer, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions - post +exports.createCustomerBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerBalanceTransaction: customer is required.'); + delete options.customer; + return stripe.customers.createBalanceTransaction(customer, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions/{transaction} - get +exports.retrieveCustomerBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerBalanceTransaction: customer is required.'); + delete options.customer; + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveCustomerBalanceTransaction: transaction is required.'); + delete options.transaction; + return stripe.customers.retrieveBalanceTransaction(customer, transaction, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions/{transaction} - post +exports.updateCustomerBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.updateCustomerBalanceTransaction: customer is required.'); + delete options.customer; + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.updateCustomerBalanceTransaction: transaction is required.'); + delete options.transaction; + return stripe.customers.updateBalanceTransaction(customer, transaction, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance - get +exports.retrieveCustomerCashBalance = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerCashBalance: customer is required.'); + delete options.customer; + return stripe.customers.retrieveCashBalance(customer, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance - post +exports.cashBalanceCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.cashBalanceCustomer: customer is required.'); + delete options.customer; + return stripe.customers.cashBalance(customer, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance_transactions - get +exports.listCustomerCashBalanceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerCashBalanceTransactions: customer is required.'); + delete options.customer; + return stripe.customers.listCashBalanceTransactions(customer, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance_transactions/{transaction} - get +exports.retrieveCustomerCashBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerCashBalanceTransaction: customer is required.'); + delete options.customer; + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveCustomerCashBalanceTransaction: transaction is required.'); + delete options.transaction; + return stripe.customers.retrieveCashBalanceTransaction(customer, transaction, options,__extra); +}; + +// /v1/customers/{customer}/discount - delete +exports.deleteCustomerDiscount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomerDiscount: customer is required.'); + delete options.customer; + return stripe.customers.deleteDiscount(customer, options,__extra); +}; + +// /v1/customers/{customer}/funding_instructions - post +exports.createCustomerFundingInstruction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerFundingInstruction: customer is required.'); + delete options.customer; + return stripe.customers.createFundingInstruction(customer, options,__extra); +}; + +// /v1/customers/{customer}/payment_methods - get +exports.listCustomerPaymentMethods = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerPaymentMethods: customer is required.'); + delete options.customer; + return stripe.customers.listPaymentMethods(customer, options,__extra); +}; + +// /v1/customers/{customer}/payment_methods/{payment_method} - get +exports.retrieveCustomerPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerPaymentMethod: customer is required.'); + delete options.customer; + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.retrieveCustomerPaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.customers.retrievePaymentMethod(customer, payment_method, options,__extra); +}; + +// /v1/customers/{customer}/sources - get +exports.listCustomerSources = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerSources: customer is required.'); + delete options.customer; + return stripe.customers.listSources(customer, options,__extra); +}; + +// /v1/customers/{customer}/sources - post +exports.createCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerSource: customer is required.'); + delete options.customer; + return stripe.customers.createSource(customer, options,__extra); +}; + +// /v1/customers/{customer}/sources/{id} - delete +exports.deleteCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomerSource: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.deleteCustomerSource: id is required.'); + delete options.id; + return stripe.customers.deleteSource(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/sources/{id} - get +exports.retrieveCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerSource: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveCustomerSource: id is required.'); + delete options.id; + return stripe.customers.retrieveSource(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/sources/{id} - post +exports.updateCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.updateCustomerSource: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.updateCustomerSource: id is required.'); + delete options.id; + return stripe.customers.updateSource(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids - get +exports.listCustomerTaxIds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerTaxIds: customer is required.'); + delete options.customer; + return stripe.customers.listTaxIds(customer, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids - post +exports.createCustomerTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerTaxId: customer is required.'); + delete options.customer; + return stripe.customers.createTaxId(customer, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids/{id} - delete +exports.deleteCustomerTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomerTaxId: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.deleteCustomerTaxId: id is required.'); + delete options.id; + return stripe.customers.deleteTaxId(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids/{id} - get +exports.retrieveCustomerTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerTaxId: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveCustomerTaxId: id is required.'); + delete options.id; + return stripe.customers.retrieveTaxId(customer, id, options,__extra); +}; + +// /v1/disputes - get +exports.listDisputes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.disputes.list(options,__extra); +}; + +// /v1/disputes/{dispute} - get +exports.retrieveDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.retrieveDispute: dispute is required.'); + delete options.dispute; + return stripe.disputes.retrieve(dispute, options,__extra); +}; + +// /v1/disputes/{dispute} - post +exports.updateDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.updateDispute: dispute is required.'); + delete options.dispute; + return stripe.disputes.update(dispute, options,__extra); +}; + +// /v1/disputes/{dispute}/close - post +exports.closeDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.closeDispute: dispute is required.'); + delete options.dispute; + return stripe.disputes.close(dispute, options,__extra); +}; + +// /v1/ephemeral_keys - post +exports.createEphemeralKey = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.ephemeralKeys.create(options,__extra); +}; + +// /v1/ephemeral_keys/{key} - delete +exports.deleteEphemeralKey = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const key = this.parseRequired(options.key, 'string', 'stripe.deleteEphemeralKey: key is required.'); + delete options.key; + return stripe.ephemeralKeys.del(key, options,__extra); +}; + +// /v1/events - get +exports.listEvents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.events.list(options,__extra); +}; + +// /v1/events/{id} - get +exports.retrieveEvent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveEvent: id is required.'); + delete options.id; + return stripe.events.retrieve(id, options,__extra); +}; + +// /v1/exchange_rates - get +exports.listExchangeRates = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.exchangeRates.list(options,__extra); +}; + +// /v1/exchange_rates/{rate_id} - get +exports.retrieveExchangeRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const rate_id = this.parseRequired(options.rate_id, 'string', 'stripe.retrieveExchangeRate: rate_id is required.'); + delete options.rate_id; + return stripe.exchangeRates.retrieve(rate_id, options,__extra); +}; + +// /v1/file_links - get +exports.listFileLinks = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.fileLinks.list(options,__extra); +}; + +// /v1/file_links - post +exports.createFileLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.fileLinks.create(options,__extra); +}; + +// /v1/file_links/{link} - get +exports.retrieveFileLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const link = this.parseRequired(options.link, 'string', 'stripe.retrieveFileLink: link is required.'); + delete options.link; + return stripe.fileLinks.retrieve(link, options,__extra); +}; + +// /v1/file_links/{link} - post +exports.updateFileLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const link = this.parseRequired(options.link, 'string', 'stripe.updateFileLink: link is required.'); + delete options.link; + return stripe.fileLinks.update(link, options,__extra); +}; + +// /v1/files - get +exports.listFiles = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.files.list(options,__extra); +}; + +// /v1/files - post +exports.createFile = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.filePath) { + const fs = require('fs'); + if (fs.existsSync(options.filePath)) { + const fp = fs.readFileSync(options.filePath); + options.file = { + data: fp, + name: options.filePath.split('/').pop() + }; + } else { + throw new Error('createFile: File not found: ' + options.filePath); + } + delete options.filePath; + } else { + throw new Error('createFile: filePath is required!'); + } + + return stripe.files.create(options,__extra); +}; + +// /v1/files/{file} - get +exports.retrieveFile = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const file = this.parseRequired(options.file, 'string', 'stripe.retrieveFile: file is required.'); + delete options.file; + return stripe.files.retrieve(file, options,__extra); +}; + +// /v1/financial_connections/accounts - get +exports.listFinancialConnectionsAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.financialConnections.accounts.list(options,__extra); +}; + +// /v1/financial_connections/accounts/{account} - get +exports.retrieveFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.retrieve(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/disconnect - post +exports.disconnectFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.disconnectFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.disconnect(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/owners - get +exports.listFinancialConnectionsAccountOwners = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listFinancialConnectionsAccountOwners: account is required.'); + delete options.account; + const ownership = this.parseRequired(options.ownership, 'string', 'stripe.listFinancialConnectionsAccountOwners: ownership is required.'); + return stripe.financialConnections.accounts.listOwners(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/refresh - post +exports.refreshFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.refreshFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.refresh(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/subscribe - post +exports.subscribeFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.subscribeFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.subscribe(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/unsubscribe - post +exports.unsubscribeFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.unsubscribeFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.unsubscribe(account, options,__extra); +}; + +// /v1/financial_connections/sessions - post +exports.createFinancialConnectionsSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.financialConnections.sessions.create(options,__extra); +}; + +// /v1/financial_connections/sessions/{session} - get +exports.retrieveFinancialConnectionsSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.retrieveFinancialConnectionsSession: session is required.'); + delete options.session; + return stripe.financialConnections.sessions.retrieve(session, options,__extra); +}; + +// /v1/financial_connections/transactions - get +exports.listFinancialConnectionsTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listFinancialConnectionsTransactions: account is required.'); + return stripe.financialConnections.transactions.list(options,__extra); +}; + +// /v1/financial_connections/transactions/{transaction} - get +exports.retrieveFinancialConnectionsTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveFinancialConnectionsTransaction: transaction is required.'); + delete options.transaction; + return stripe.financialConnections.transactions.retrieve(transaction, options,__extra); +}; + +// /v1/forwarding/requests - get +exports.listForwardingRequests = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.forwarding.requests.list(options,__extra); +}; + +// /v1/forwarding/requests - post +exports.createForwardingRequest = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.forwarding.requests.create(options,__extra); +}; + +// /v1/forwarding/requests/{id} - get +exports.retrieveForwardingRequest = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveForwardingRequest: id is required.'); + delete options.id; + return stripe.forwarding.requests.retrieve(id, options,__extra); +}; + +// /v1/identity/verification_reports - get +exports.listIdentityVerificationReports = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.identity.verificationReports.list(options,__extra); +}; + +// /v1/identity/verification_reports/{report} - get +exports.retrieveIdentityVerificationReport = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const report = this.parseRequired(options.report, 'string', 'stripe.retrieveIdentityVerificationReport: report is required.'); + delete options.report; + return stripe.identity.verificationReports.retrieve(report, options,__extra); +}; + +// /v1/identity/verification_sessions - get +exports.listIdentityVerificationSessions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.identity.verificationSessions.list(options,__extra); +}; + +// /v1/identity/verification_sessions - post +exports.createIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.identity.verificationSessions.create(options,__extra); +}; + +// /v1/identity/verification_sessions/{session} - get +exports.retrieveIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.retrieveIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.retrieve(session, options,__extra); +}; + +// /v1/identity/verification_sessions/{session} - post +exports.updateIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.updateIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.update(session, options,__extra); +}; + +// /v1/identity/verification_sessions/{session}/cancel - post +exports.cancelIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.cancelIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.cancel(session, options,__extra); +}; + +// /v1/identity/verification_sessions/{session}/redact - post +exports.redactIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.redactIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.redact(session, options,__extra); +}; + +// /v1/invoiceitems - get +exports.listInvoiceItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoiceItems.list(options,__extra); +}; + +// /v1/invoiceitems - post +exports.createInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoiceItems.create(options,__extra); +}; + +// /v1/invoiceitems/{invoiceitem} - delete +exports.deleteInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoiceitem = this.parseRequired(options.invoiceitem, 'string', 'stripe.deleteInvoiceItem: invoiceitem is required.'); + delete options.invoiceitem; + return stripe.invoiceItems.del(invoiceitem, options,__extra); +}; + +// /v1/invoiceitems/{invoiceitem} - get +exports.retrieveInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoiceitem = this.parseRequired(options.invoiceitem, 'string', 'stripe.retrieveInvoiceItem: invoiceitem is required.'); + delete options.invoiceitem; + return stripe.invoiceItems.retrieve(invoiceitem, options,__extra); +}; + +// /v1/invoiceitems/{invoiceitem} - post +exports.updateInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoiceitem = this.parseRequired(options.invoiceitem, 'string', 'stripe.updateInvoiceItem: invoiceitem is required.'); + delete options.invoiceitem; + return stripe.invoiceItems.update(invoiceitem, options,__extra); +}; + +// /v1/invoices - get +exports.listInvoices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.list(options,__extra); +}; + +// /v1/invoices - post +exports.createInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.create(options,__extra); +}; + +// /v1/invoices/search - get +exports.searchInvoices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchInvoices: query is required.'); + return stripe.invoices.search(options,__extra); +}; + +// /v1/invoices/upcoming - get +exports.retrieveInvoicesUpcoming = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.retrieveUpcoming(options,__extra); +}; + +// /v1/invoices/upcoming/lines - get +exports.listInvoicesUpcomingLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.listUpcomingLineItems(options,__extra); +}; + +// /v1/invoices/{invoice} - delete +exports.deleteInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.deleteInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.del(invoice, options,__extra); +}; + +// /v1/invoices/{invoice} - get +exports.retrieveInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.retrieveInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.retrieve(invoice, options,__extra); +}; + +// /v1/invoices/{invoice} - post +exports.updateInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.updateInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.update(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/finalize - post +exports.finalizeInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.finalizeInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.finalize(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/lines - get +exports.listInvoiceLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.listInvoiceLines: invoice is required.'); + delete options.invoice; + return stripe.invoices.listLines(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/lines/{line_item_id} - post +exports.updateInvoiceLine = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.updateInvoiceLine: invoice is required.'); + delete options.invoice; + const line_item_id = this.parseRequired(options.line_item_id, 'string', 'stripe.updateInvoiceLine: line_item_id is required.'); + delete options.line_item_id; + return stripe.invoices.updateLine(invoice, line_item_id, options,__extra); +}; + +// /v1/invoices/{invoice}/mark_uncollectible - post +exports.markUncollectibleInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.markUncollectibleInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.markUncollectible(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/pay - post +exports.payInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.payInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.pay(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/send - post +exports.sendInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.sendInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.send(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/void - post +exports.voidInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.voidInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.void(invoice, options,__extra); +}; + +// /v1/issuing/authorizations - get +exports.listIssuingAuthorizations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.authorizations.list(options,__extra); +}; + +// /v1/issuing/authorizations/{authorization} - get +exports.retrieveIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.retrieveIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.retrieve(authorization, options,__extra); +}; + +// /v1/issuing/authorizations/{authorization} - post +exports.updateIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.updateIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.update(authorization, options,__extra); +}; + +// /v1/issuing/authorizations/{authorization}/approve - post +exports.approveIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.approveIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.approve(authorization, options,__extra); +}; + +// /v1/issuing/authorizations/{authorization}/decline - post +exports.declineIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.declineIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.decline(authorization, options,__extra); +}; + +// /v1/issuing/cardholders - get +exports.listIssuingCardholders = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cardholders.list(options,__extra); +}; + +// /v1/issuing/cardholders - post +exports.createIssuingCardholder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cardholders.create(options,__extra); +}; + +// /v1/issuing/cardholders/{cardholder} - get +exports.retrieveIssuingCardholder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const cardholder = this.parseRequired(options.cardholder, 'string', 'stripe.retrieveIssuingCardholder: cardholder is required.'); + delete options.cardholder; + return stripe.issuing.cardholders.retrieve(cardholder, options,__extra); +}; + +// /v1/issuing/cardholders/{cardholder} - post +exports.updateIssuingCardholder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const cardholder = this.parseRequired(options.cardholder, 'string', 'stripe.updateIssuingCardholder: cardholder is required.'); + delete options.cardholder; + return stripe.issuing.cardholders.update(cardholder, options,__extra); +}; + +// /v1/issuing/cards - get +exports.listIssuingCards = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cards.list(options,__extra); +}; + +// /v1/issuing/cards - post +exports.createIssuingCard = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cards.create(options,__extra); +}; + +// /v1/issuing/cards/{card} - get +exports.retrieveIssuingCard = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.retrieveIssuingCard: card is required.'); + delete options.card; + return stripe.issuing.cards.retrieve(card, options,__extra); +}; + +// /v1/issuing/cards/{card} - post +exports.updateIssuingCard = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.updateIssuingCard: card is required.'); + delete options.card; + return stripe.issuing.cards.update(card, options,__extra); +}; + +// /v1/issuing/disputes - get +exports.listIssuingDisputes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.disputes.list(options,__extra); +}; + +// /v1/issuing/disputes - post +exports.createIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.disputes.create(options,__extra); +}; + +// /v1/issuing/disputes/{dispute} - get +exports.retrieveIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.retrieveIssuingDispute: dispute is required.'); + delete options.dispute; + return stripe.issuing.disputes.retrieve(dispute, options,__extra); +}; + +// /v1/issuing/disputes/{dispute} - post +exports.updateIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.updateIssuingDispute: dispute is required.'); + delete options.dispute; + return stripe.issuing.disputes.update(dispute, options,__extra); +}; + +// /v1/issuing/disputes/{dispute}/submit - post +exports.submitIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.submitIssuingDispute: dispute is required.'); + delete options.dispute; + return stripe.issuing.disputes.submit(dispute, options,__extra); +}; + +// /v1/issuing/personalization_designs - get +exports.listIssuingPersonalizationDesigns = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.personalizationDesigns.list(options,__extra); +}; + +// /v1/issuing/personalization_designs - post +exports.createIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.personalizationDesigns.create(options,__extra); +}; + +// /v1/issuing/personalization_designs/{personalization_design} - get +exports.retrieveIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.retrieveIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.issuing.personalizationDesigns.retrieve(personalization_design, options,__extra); +}; + +// /v1/issuing/personalization_designs/{personalization_design} - post +exports.updateIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.updateIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.issuing.personalizationDesigns.update(personalization_design, options,__extra); +}; + +// /v1/issuing/physical_bundles - get +exports.listIssuingPhysicalBundles = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.physicalBundles.list(options,__extra); +}; + +// /v1/issuing/physical_bundles/{physical_bundle} - get +exports.retrieveIssuingPhysicalBundle = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const physical_bundle = this.parseRequired(options.physical_bundle, 'string', 'stripe.retrieveIssuingPhysicalBundle: physical_bundle is required.'); + delete options.physical_bundle; + return stripe.issuing.physicalBundles.retrieve(physical_bundle, options,__extra); +}; + +// /v1/issuing/tokens - get +exports.listIssuingTokens = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.listIssuingTokens: card is required.'); + return stripe.issuing.tokens.list(options,__extra); +}; + +// /v1/issuing/tokens/{token} - get +exports.retrieveIssuingToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const token = this.parseRequired(options.token, 'string', 'stripe.retrieveIssuingToken: token is required.'); + delete options.token; + return stripe.issuing.tokens.retrieve(token, options,__extra); +}; + +// /v1/issuing/tokens/{token} - post +exports.updateIssuingToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const token = this.parseRequired(options.token, 'string', 'stripe.updateIssuingToken: token is required.'); + delete options.token; + return stripe.issuing.tokens.update(token, options,__extra); +}; + +// /v1/issuing/transactions - get +exports.listIssuingTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.transactions.list(options,__extra); +}; + +// /v1/issuing/transactions/{transaction} - get +exports.retrieveIssuingTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveIssuingTransaction: transaction is required.'); + delete options.transaction; + return stripe.issuing.transactions.retrieve(transaction, options,__extra); +}; + +// /v1/issuing/transactions/{transaction} - post +exports.updateIssuingTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.updateIssuingTransaction: transaction is required.'); + delete options.transaction; + return stripe.issuing.transactions.update(transaction, options,__extra); +}; + +// /v1/mandates/{mandate} - get +exports.retrieveMandate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const mandate = this.parseRequired(options.mandate, 'string', 'stripe.retrieveMandate: mandate is required.'); + delete options.mandate; + return stripe.mandates.retrieve(mandate, options,__extra); +}; + +// /v1/payment_intents - get +exports.listPaymentIntents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentIntents.list(options,__extra); +}; + +// /v1/payment_intents - post +exports.createPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.paymentIntents.create(options,__extra); +}; + +// /v1/payment_intents/search - get +exports.searchPaymentIntents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchPaymentIntents: query is required.'); + return stripe.paymentIntents.search(options,__extra); +}; + +// /v1/payment_intents/{intent} - get +exports.retrievePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.retrievePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.retrieve(intent, options,__extra); +}; + +// /v1/payment_intents/{intent} - post +exports.updatePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.updatePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.update(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/apply_customer_balance - post +exports.applyCustomerBalancePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.applyCustomerBalancePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.applyCustomerBalance(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/cancel - post +exports.cancelPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.cancelPaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.cancel(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/capture - post +exports.capturePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.capturePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.capture(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/confirm - post +exports.confirmPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.confirmPaymentIntent: intent is required.'); + delete options.intent; + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.paymentIntents.confirm(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/increment_authorization - post +exports.incrementAuthorizationPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.incrementAuthorizationPaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.incrementAuthorization(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/verify_microdeposits - post +exports.createPaymentIntentVerifyMicrodeposit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.createPaymentIntentVerifyMicrodeposit: intent is required.'); + delete options.intent; + return stripe.paymentIntents.createVerifyMicrodeposit(intent, options,__extra); +}; + +// /v1/payment_links - get +exports.listPaymentLinks = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentLinks.list(options,__extra); +}; + +// /v1/payment_links - post +exports.createPaymentLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentLinks.create(options,__extra); +}; + +// /v1/payment_links/{payment_link} - get +exports.retrievePaymentLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_link = this.parseRequired(options.payment_link, 'string', 'stripe.retrievePaymentLink: payment_link is required.'); + delete options.payment_link; + return stripe.paymentLinks.retrieve(payment_link, options,__extra); +}; + +// /v1/payment_links/{payment_link} - post +exports.updatePaymentLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_link = this.parseRequired(options.payment_link, 'string', 'stripe.updatePaymentLink: payment_link is required.'); + delete options.payment_link; + return stripe.paymentLinks.update(payment_link, options,__extra); +}; + +// /v1/payment_links/{payment_link}/line_items - get +exports.listPaymentLinkLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_link = this.parseRequired(options.payment_link, 'string', 'stripe.listPaymentLinkLineItems: payment_link is required.'); + delete options.payment_link; + return stripe.paymentLinks.listLineItems(payment_link, options,__extra); +}; + +// /v1/payment_method_configurations - get +exports.listPaymentMethodConfigurations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodConfigurations.list(options,__extra); +}; + +// /v1/payment_method_configurations - post +exports.createPaymentMethodConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodConfigurations.create(options,__extra); +}; + +// /v1/payment_method_configurations/{configuration} - get +exports.retrievePaymentMethodConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.retrievePaymentMethodConfiguration: configuration is required.'); + delete options.configuration; + return stripe.paymentMethodConfigurations.retrieve(configuration, options,__extra); +}; + +// /v1/payment_method_configurations/{configuration} - post +exports.updatePaymentMethodConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.updatePaymentMethodConfiguration: configuration is required.'); + delete options.configuration; + return stripe.paymentMethodConfigurations.update(configuration, options,__extra); +}; + +// /v1/payment_method_domains - get +exports.listPaymentMethodDomains = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodDomains.list(options,__extra); +}; + +// /v1/payment_method_domains - post +exports.createPaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodDomains.create(options,__extra); +}; + +// /v1/payment_method_domains/{payment_method_domain} - get +exports.retrievePaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method_domain = this.parseRequired(options.payment_method_domain, 'string', 'stripe.retrievePaymentMethodDomain: payment_method_domain is required.'); + delete options.payment_method_domain; + return stripe.paymentMethodDomains.retrieve(payment_method_domain, options,__extra); +}; + +// /v1/payment_method_domains/{payment_method_domain} - post +exports.updatePaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method_domain = this.parseRequired(options.payment_method_domain, 'string', 'stripe.updatePaymentMethodDomain: payment_method_domain is required.'); + delete options.payment_method_domain; + return stripe.paymentMethodDomains.update(payment_method_domain, options,__extra); +}; + +// /v1/payment_method_domains/{payment_method_domain}/validate - post +exports.validatePaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method_domain = this.parseRequired(options.payment_method_domain, 'string', 'stripe.validatePaymentMethodDomain: payment_method_domain is required.'); + delete options.payment_method_domain; + return stripe.paymentMethodDomains.validate(payment_method_domain, options,__extra); +}; + +// /v1/payment_methods - get +exports.listPaymentMethods = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethods.list(options,__extra); +}; + +// /v1/payment_methods - post +exports.createPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethods.create(options,__extra); +}; + +// /v1/payment_methods/{payment_method} - get +exports.retrievePaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.retrievePaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.retrieve(payment_method, options,__extra); +}; + +// /v1/payment_methods/{payment_method} - post +exports.updatePaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.updatePaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.update(payment_method, options,__extra); +}; + +// /v1/payment_methods/{payment_method}/attach - post +exports.attachPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.attachPaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.attach(payment_method, options,__extra); +}; + +// /v1/payment_methods/{payment_method}/detach - post +exports.detachPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.detachPaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.detach(payment_method, options,__extra); +}; + +// /v1/payouts - get +exports.listPayouts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.payouts.list(options,__extra); +}; + +// /v1/payouts - post +exports.createPayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.payouts.create(options,__extra); +}; + +// /v1/payouts/{payout} - get +exports.retrievePayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.retrievePayout: payout is required.'); + delete options.payout; + return stripe.payouts.retrieve(payout, options,__extra); +}; + +// /v1/payouts/{payout} - post +exports.updatePayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.updatePayout: payout is required.'); + delete options.payout; + return stripe.payouts.update(payout, options,__extra); +}; + +// /v1/payouts/{payout}/cancel - post +exports.cancelPayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.cancelPayout: payout is required.'); + delete options.payout; + return stripe.payouts.cancel(payout, options,__extra); +}; + +// /v1/payouts/{payout}/reverse - post +exports.reversePayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.reversePayout: payout is required.'); + delete options.payout; + return stripe.payouts.reverse(payout, options,__extra); +}; + +// /v1/plans - get +exports.listPlans = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.plans.list(options,__extra); +}; + +// /v1/plans - post +exports.createPlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.plans.create(options,__extra); +}; + +// /v1/plans/{plan} - delete +exports.deletePlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const plan = this.parseRequired(options.plan, 'string', 'stripe.deletePlan: plan is required.'); + delete options.plan; + return stripe.plans.del(plan, options,__extra); +}; + +// /v1/plans/{plan} - get +exports.retrievePlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const plan = this.parseRequired(options.plan, 'string', 'stripe.retrievePlan: plan is required.'); + delete options.plan; + return stripe.plans.retrieve(plan, options,__extra); +}; + +// /v1/plans/{plan} - post +exports.updatePlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const plan = this.parseRequired(options.plan, 'string', 'stripe.updatePlan: plan is required.'); + delete options.plan; + return stripe.plans.update(plan, options,__extra); +}; + +// /v1/prices - get +exports.listPrices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.prices.list(options,__extra); +}; + +// /v1/prices - post +exports.createPrice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.prices.create(options,__extra); +}; + +// /v1/prices/search - get +exports.searchPrices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchPrices: query is required.'); + return stripe.prices.search(options,__extra); +}; + +// /v1/prices/{price} - get +exports.retrievePrice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const price = this.parseRequired(options.price, 'string', 'stripe.retrievePrice: price is required.'); + delete options.price; + return stripe.prices.retrieve(price, options,__extra); +}; + +// /v1/prices/{price} - post +exports.updatePrice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const price = this.parseRequired(options.price, 'string', 'stripe.updatePrice: price is required.'); + delete options.price; + return stripe.prices.update(price, options,__extra); +}; + +// /v1/products - get +exports.listProducts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.products.list(options,__extra); +}; + +// /v1/products - post +exports.createProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.products.create(options,__extra); +}; + +// /v1/products/search - get +exports.searchProducts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchProducts: query is required.'); + return stripe.products.search(options,__extra); +}; + +// /v1/products/{id} - delete +exports.deleteProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.deleteProduct: id is required.'); + delete options.id; + return stripe.products.del(id, options,__extra); +}; + +// /v1/products/{id} - get +exports.retrieveProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveProduct: id is required.'); + delete options.id; + return stripe.products.retrieve(id, options,__extra); +}; + +// /v1/products/{id} - post +exports.updateProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateProduct: id is required.'); + delete options.id; + return stripe.products.update(id, options,__extra); +}; + +// /v1/promotion_codes - get +exports.listPromotionCodes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.promotionCodes.list(options,__extra); +}; + +// /v1/promotion_codes - post +exports.createPromotionCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.promotionCodes.create(options,__extra); +}; + +// /v1/promotion_codes/{promotion_code} - get +exports.retrievePromotionCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const promotion_code = this.parseRequired(options.promotion_code, 'string', 'stripe.retrievePromotionCode: promotion_code is required.'); + delete options.promotion_code; + return stripe.promotionCodes.retrieve(promotion_code, options,__extra); +}; + +// /v1/promotion_codes/{promotion_code} - post +exports.updatePromotionCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const promotion_code = this.parseRequired(options.promotion_code, 'string', 'stripe.updatePromotionCode: promotion_code is required.'); + delete options.promotion_code; + return stripe.promotionCodes.update(promotion_code, options,__extra); +}; + +// /v1/quotes - get +exports.listQuotes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.quotes.list(options,__extra); +}; + +// /v1/quotes - post +exports.createQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.quotes.create(options,__extra); +}; + +// /v1/quotes/{quote} - get +exports.retrieveQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.retrieveQuote: quote is required.'); + delete options.quote; + return stripe.quotes.retrieve(quote, options,__extra); +}; + +// /v1/quotes/{quote} - post +exports.updateQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.updateQuote: quote is required.'); + delete options.quote; + return stripe.quotes.update(quote, options,__extra); +}; + +// /v1/quotes/{quote}/accept - post +exports.acceptQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.acceptQuote: quote is required.'); + delete options.quote; + return stripe.quotes.accept(quote, options,__extra); +}; + +// /v1/quotes/{quote}/cancel - post +exports.cancelQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.cancelQuote: quote is required.'); + delete options.quote; + return stripe.quotes.cancel(quote, options,__extra); +}; + +// /v1/quotes/{quote}/computed_upfront_line_items - get +exports.listQuoteComputedUpfrontLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.listQuoteComputedUpfrontLineItems: quote is required.'); + delete options.quote; + return stripe.quotes.listComputedUpfrontLineItems(quote, options,__extra); +}; + +// /v1/quotes/{quote}/finalize - post +exports.finalizeQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.finalizeQuote: quote is required.'); + delete options.quote; + return stripe.quotes.finalize(quote, options,__extra); +}; + +// /v1/quotes/{quote}/line_items - get +exports.listQuoteLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.listQuoteLineItems: quote is required.'); + delete options.quote; + return stripe.quotes.listLineItems(quote, options,__extra); +}; + +// /v1/quotes/{quote}/pdf - get +exports.retrieveQuotePdf = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.retrieveQuotePdf: quote is required.'); + delete options.quote; + return stripe.quotes.retrievePdf(quote, options,__extra); +}; + +// /v1/radar/early_fraud_warnings - get +exports.listRadarEarlyFraudWarnings = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.earlyFraudWarnings.list(options,__extra); +}; + +// /v1/radar/early_fraud_warnings/{early_fraud_warning} - get +exports.retrieveRadarEarlyFraudWarning = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const early_fraud_warning = this.parseRequired(options.early_fraud_warning, 'string', 'stripe.retrieveRadarEarlyFraudWarning: early_fraud_warning is required.'); + delete options.early_fraud_warning; + return stripe.radar.earlyFraudWarnings.retrieve(early_fraud_warning, options,__extra); +}; + +// /v1/radar/value_list_items - get +exports.listRadarValueListItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.listRadarValueListItems: value_list is required.'); + return stripe.radar.valueListItems.list(options,__extra); +}; + +// /v1/radar/value_list_items - post +exports.createRadarValueListItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.valueListItems.create(options,__extra); +}; + +// /v1/radar/value_list_items/{item} - delete +exports.deleteRadarValueListItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.deleteRadarValueListItem: item is required.'); + delete options.item; + return stripe.radar.valueListItems.del(item, options,__extra); +}; + +// /v1/radar/value_list_items/{item} - get +exports.retrieveRadarValueListItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.retrieveRadarValueListItem: item is required.'); + delete options.item; + return stripe.radar.valueListItems.retrieve(item, options,__extra); +}; + +// /v1/radar/value_lists - get +exports.listRadarValueLists = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.valueLists.list(options,__extra); +}; + +// /v1/radar/value_lists - post +exports.createRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.valueLists.create(options,__extra); +}; + +// /v1/radar/value_lists/{value_list} - delete +exports.deleteRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.deleteRadarValueList: value_list is required.'); + delete options.value_list; + return stripe.radar.valueLists.del(value_list, options,__extra); +}; + +// /v1/radar/value_lists/{value_list} - get +exports.retrieveRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.retrieveRadarValueList: value_list is required.'); + delete options.value_list; + return stripe.radar.valueLists.retrieve(value_list, options,__extra); +}; + +// /v1/radar/value_lists/{value_list} - post +exports.updateRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.updateRadarValueList: value_list is required.'); + delete options.value_list; + return stripe.radar.valueLists.update(value_list, options,__extra); +}; + +// /v1/refunds - get +exports.listRefunds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.refunds.list(options,__extra); +}; + +// /v1/refunds - post +exports.createRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.refunds.create(options,__extra); +}; + +// /v1/refunds/{refund} - get +exports.retrieveRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.retrieveRefund: refund is required.'); + delete options.refund; + return stripe.refunds.retrieve(refund, options,__extra); +}; + +// /v1/refunds/{refund} - post +exports.updateRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.updateRefund: refund is required.'); + delete options.refund; + return stripe.refunds.update(refund, options,__extra); +}; + +// /v1/refunds/{refund}/cancel - post +exports.cancelRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.cancelRefund: refund is required.'); + delete options.refund; + return stripe.refunds.cancel(refund, options,__extra); +}; + +// /v1/reporting/report_runs - get +exports.listReportingReportRuns = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reporting.reportRuns.list(options,__extra); +}; + +// /v1/reporting/report_runs - post +exports.createReportingReportRun = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reporting.reportRuns.create(options,__extra); +}; + +// /v1/reporting/report_runs/{report_run} - get +exports.retrieveReportingReportRun = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const report_run = this.parseRequired(options.report_run, 'string', 'stripe.retrieveReportingReportRun: report_run is required.'); + delete options.report_run; + return stripe.reporting.reportRuns.retrieve(report_run, options,__extra); +}; + +// /v1/reporting/report_types - get +exports.listReportingReportTypes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reporting.reportTypes.list(options,__extra); +}; + +// /v1/reporting/report_types/{report_type} - get +exports.retrieveReportingReportType = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const report_type = this.parseRequired(options.report_type, 'string', 'stripe.retrieveReportingReportType: report_type is required.'); + delete options.report_type; + return stripe.reporting.reportTypes.retrieve(report_type, options,__extra); +}; + +// /v1/reviews - get +exports.listReviews = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reviews.list(options,__extra); +}; + +// /v1/reviews/{review} - get +exports.retrieveReview = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const review = this.parseRequired(options.review, 'string', 'stripe.retrieveReview: review is required.'); + delete options.review; + return stripe.reviews.retrieve(review, options,__extra); +}; + +// /v1/reviews/{review}/approve - post +exports.approveReview = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const review = this.parseRequired(options.review, 'string', 'stripe.approveReview: review is required.'); + delete options.review; + return stripe.reviews.approve(review, options,__extra); +}; + +// /v1/setup_attempts - get +exports.listSetupAttempts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const setup_intent = this.parseRequired(options.setup_intent, 'string', 'stripe.listSetupAttempts: setup_intent is required.'); + return stripe.setupAttempts.list(options,__extra); +}; + +// /v1/setup_intents - get +exports.listSetupIntents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.setupIntents.list(options,__extra); +}; + +// /v1/setup_intents - post +exports.createSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.setupIntents.create(options,__extra); +}; + +// /v1/setup_intents/{intent} - get +exports.retrieveSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.retrieveSetupIntent: intent is required.'); + delete options.intent; + return stripe.setupIntents.retrieve(intent, options,__extra); +}; + +// /v1/setup_intents/{intent} - post +exports.updateSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.updateSetupIntent: intent is required.'); + delete options.intent; + return stripe.setupIntents.update(intent, options,__extra); +}; + +// /v1/setup_intents/{intent}/cancel - post +exports.cancelSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.cancelSetupIntent: intent is required.'); + delete options.intent; + return stripe.setupIntents.cancel(intent, options,__extra); +}; + +// /v1/setup_intents/{intent}/confirm - post +exports.confirmSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.confirmSetupIntent: intent is required.'); + delete options.intent; + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.setupIntents.confirm(intent, options,__extra); +}; + +// /v1/setup_intents/{intent}/verify_microdeposits - post +exports.createSetupIntentVerifyMicrodeposit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.createSetupIntentVerifyMicrodeposit: intent is required.'); + delete options.intent; + return stripe.setupIntents.createVerifyMicrodeposit(intent, options,__extra); +}; + +// /v1/shipping_rates - get +exports.listShippingRates = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.shippingRates.list(options,__extra); +}; + +// /v1/shipping_rates - post +exports.createShippingRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.shippingRates.create(options,__extra); +}; + +// /v1/shipping_rates/{shipping_rate_token} - get +exports.retrieveShippingRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const shipping_rate_token = this.parseRequired(options.shipping_rate_token, 'string', 'stripe.retrieveShippingRate: shipping_rate_token is required.'); + delete options.shipping_rate_token; + return stripe.shippingRates.retrieve(shipping_rate_token, options,__extra); +}; + +// /v1/shipping_rates/{shipping_rate_token} - post +exports.updateShippingRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const shipping_rate_token = this.parseRequired(options.shipping_rate_token, 'string', 'stripe.updateShippingRate: shipping_rate_token is required.'); + delete options.shipping_rate_token; + return stripe.shippingRates.update(shipping_rate_token, options,__extra); +}; + +// /v1/sigma/scheduled_query_runs - get +exports.listSigmaScheduledQueryRuns = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.sigma.scheduledQueryRuns.list(options,__extra); +}; + +// /v1/sigma/scheduled_query_runs/{scheduled_query_run} - get +exports.retrieveSigmaScheduledQueryRun = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const scheduled_query_run = this.parseRequired(options.scheduled_query_run, 'string', 'stripe.retrieveSigmaScheduledQueryRun: scheduled_query_run is required.'); + delete options.scheduled_query_run; + return stripe.sigma.scheduledQueryRuns.retrieve(scheduled_query_run, options,__extra); +}; + +// /v1/sources - post +exports.createSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.sources.create(options,__extra); +}; + +// /v1/sources/{source} - get +exports.retrieveSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.retrieveSource: source is required.'); + delete options.source; + return stripe.sources.retrieve(source, options,__extra); +}; + +// /v1/sources/{source} - post +exports.updateSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.updateSource: source is required.'); + delete options.source; + return stripe.sources.update(source, options,__extra); +}; + +// /v1/sources/{source}/source_transactions - get +exports.listSourceSourceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.listSourceSourceTransactions: source is required.'); + delete options.source; + return stripe.sources.listSourceTransactions(source, options,__extra); +}; + +// /v1/sources/{source}/verify - post +exports.verifySource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.verifySource: source is required.'); + delete options.source; + return stripe.sources.verify(source, options,__extra); +}; + +// /v1/subscription_items - get +exports.listSubscriptionItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription = this.parseRequired(options.subscription, 'string', 'stripe.listSubscriptionItems: subscription is required.'); + return stripe.subscriptionItems.list(options,__extra); +}; + +// /v1/subscription_items - post +exports.createSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptionItems.create(options,__extra); +}; + +// /v1/subscription_items/{item} - delete +exports.deleteSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.deleteSubscriptionItem: item is required.'); + delete options.item; + return stripe.subscriptionItems.del(item, options,__extra); +}; + +// /v1/subscription_items/{item} - get +exports.retrieveSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.retrieveSubscriptionItem: item is required.'); + delete options.item; + return stripe.subscriptionItems.retrieve(item, options,__extra); +}; + +// /v1/subscription_items/{item} - post +exports.updateSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.updateSubscriptionItem: item is required.'); + delete options.item; + return stripe.subscriptionItems.update(item, options,__extra); +}; + +// /v1/subscription_items/{subscription_item}/usage_record_summaries - get +exports.listSubscriptionItemUsageRecordSummaries = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_item = this.parseRequired(options.subscription_item, 'string', 'stripe.listSubscriptionItemUsageRecordSummaries: subscription_item is required.'); + delete options.subscription_item; + return stripe.subscriptionItems.listUsageRecordSummaries(subscription_item, options,__extra); +}; + +// /v1/subscription_items/{subscription_item}/usage_records - post +exports.createSubscriptionItemUsageRecord = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_item = this.parseRequired(options.subscription_item, 'string', 'stripe.createSubscriptionItemUsageRecord: subscription_item is required.'); + delete options.subscription_item; + return stripe.subscriptionItems.createUsageRecord(subscription_item, options,__extra); +}; + +// /v1/subscription_schedules - get +exports.listSubscriptionSchedules = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptionSchedules.list(options,__extra); +}; + +// /v1/subscription_schedules - post +exports.createSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptionSchedules.create(options,__extra); +}; + +// /v1/subscription_schedules/{schedule} - get +exports.retrieveSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.retrieveSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.retrieve(schedule, options,__extra); +}; + +// /v1/subscription_schedules/{schedule} - post +exports.updateSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.updateSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.update(schedule, options,__extra); +}; + +// /v1/subscription_schedules/{schedule}/cancel - post +exports.cancelSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.cancelSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.cancel(schedule, options,__extra); +}; + +// /v1/subscription_schedules/{schedule}/release - post +exports.releaseSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.releaseSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.release(schedule, options,__extra); +}; + +// /v1/subscriptions - get +exports.listSubscriptions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptions.list(options,__extra); +}; + +// /v1/subscriptions - post +exports.createSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptions.create(options,__extra); +}; + +// /v1/subscriptions/search - get +exports.searchSubscriptions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchSubscriptions: query is required.'); + return stripe.subscriptions.search(options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id} - delete +exports.deleteSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.deleteSubscription: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.del(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id} - get +exports.retrieveSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.retrieveSubscription: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.retrieve(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id} - post +exports.updateSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.updateSubscription: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.update(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id}/discount - delete +exports.deleteSubscriptionDiscount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.deleteSubscriptionDiscount: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.deleteDiscount(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription}/resume - post +exports.resumeSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription = this.parseRequired(options.subscription, 'string', 'stripe.resumeSubscription: subscription is required.'); + delete options.subscription; + return stripe.subscriptions.resume(subscription, options,__extra); +}; + +// /v1/tax/calculations - post +exports.createTaxCalculation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.calculations.create(options,__extra); +}; + +// /v1/tax/calculations/{calculation}/line_items - get +exports.listTaxCalculationLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const calculation = this.parseRequired(options.calculation, 'string', 'stripe.listTaxCalculationLineItems: calculation is required.'); + delete options.calculation; + return stripe.tax.calculations.listLineItems(calculation, options,__extra); +}; + +// /v1/tax/registrations - get +exports.listTaxRegistrations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.registrations.list(options,__extra); +}; + +// /v1/tax/registrations - post +exports.createTaxRegistration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.registrations.create(options,__extra); +}; + +// /v1/tax/registrations/{id} - get +exports.retrieveTaxRegistration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTaxRegistration: id is required.'); + delete options.id; + return stripe.tax.registrations.retrieve(id, options,__extra); +}; + +// /v1/tax/registrations/{id} - post +exports.updateTaxRegistration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateTaxRegistration: id is required.'); + delete options.id; + return stripe.tax.registrations.update(id, options,__extra); +}; + +// /v1/tax/settings - get +exports.retrieveTaxSetting = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.settings.retrieve(options,__extra); +}; + +// /v1/tax/settings - post +exports.createTaxSetting = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.settings.create(options,__extra); +}; + +// /v1/tax/transactions/create_from_calculation - post +exports.createTaxTransactionsCreateFromCalculation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.transactions.createFromCalculation.create(options,__extra); +}; + +// /v1/tax/transactions/create_reversal - post +exports.createTaxTransactionsCreateReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.transactions.createReversal.create(options,__extra); +}; + +// /v1/tax/transactions/{transaction} - get +exports.retrieveTaxTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveTaxTransaction: transaction is required.'); + delete options.transaction; + return stripe.tax.transactions.retrieve(transaction, options,__extra); +}; + +// /v1/tax/transactions/{transaction}/line_items - get +exports.listTaxTransactionLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.listTaxTransactionLineItems: transaction is required.'); + delete options.transaction; + return stripe.tax.transactions.listLineItems(transaction, options,__extra); +}; + +// /v1/tax_codes - get +exports.listTaxCodes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxCodes.list(options,__extra); +}; + +// /v1/tax_codes/{id} - get +exports.retrieveTaxCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTaxCode: id is required.'); + delete options.id; + return stripe.taxCodes.retrieve(id, options,__extra); +}; + +// /v1/tax_ids - get +exports.listTaxIds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxIds.list(options,__extra); +}; + +// /v1/tax_ids - post +exports.createTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxIds.create(options,__extra); +}; + +// /v1/tax_ids/{id} - delete +exports.deleteTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.deleteTaxId: id is required.'); + delete options.id; + return stripe.taxIds.del(id, options,__extra); +}; + +// /v1/tax_ids/{id} - get +exports.retrieveTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTaxId: id is required.'); + delete options.id; + return stripe.taxIds.retrieve(id, options,__extra); +}; + +// /v1/tax_rates - get +exports.listTaxRates = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxRates.list(options,__extra); +}; + +// /v1/tax_rates - post +exports.createTaxRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxRates.create(options,__extra); +}; + +// /v1/tax_rates/{tax_rate} - get +exports.retrieveTaxRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const tax_rate = this.parseRequired(options.tax_rate, 'string', 'stripe.retrieveTaxRate: tax_rate is required.'); + delete options.tax_rate; + return stripe.taxRates.retrieve(tax_rate, options,__extra); +}; + +// /v1/tax_rates/{tax_rate} - post +exports.updateTaxRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const tax_rate = this.parseRequired(options.tax_rate, 'string', 'stripe.updateTaxRate: tax_rate is required.'); + delete options.tax_rate; + return stripe.taxRates.update(tax_rate, options,__extra); +}; + +// /v1/terminal/configurations - get +exports.listTerminalConfigurations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.configurations.list(options,__extra); +}; + +// /v1/terminal/configurations - post +exports.createTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.configurations.create(options,__extra); +}; + +// /v1/terminal/configurations/{configuration} - delete +exports.deleteTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.deleteTerminalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.terminal.configurations.del(configuration, options,__extra); +}; + +// /v1/terminal/configurations/{configuration} - get +exports.retrieveTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.retrieveTerminalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.terminal.configurations.retrieve(configuration, options,__extra); +}; + +// /v1/terminal/configurations/{configuration} - post +exports.updateTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.updateTerminalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.terminal.configurations.update(configuration, options,__extra); +}; + +// /v1/terminal/connection_tokens - post +exports.createTerminalConnectionToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.connectionTokens.create(options,__extra); +}; + +// /v1/terminal/locations - get +exports.listTerminalLocations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.locations.list(options,__extra); +}; + +// /v1/terminal/locations - post +exports.createTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.locations.create(options,__extra); +}; + +// /v1/terminal/locations/{location} - delete +exports.deleteTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const location = this.parseRequired(options.location, 'string', 'stripe.deleteTerminalLocation: location is required.'); + delete options.location; + return stripe.terminal.locations.del(location, options,__extra); +}; + +// /v1/terminal/locations/{location} - get +exports.retrieveTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const location = this.parseRequired(options.location, 'string', 'stripe.retrieveTerminalLocation: location is required.'); + delete options.location; + return stripe.terminal.locations.retrieve(location, options,__extra); +}; + +// /v1/terminal/locations/{location} - post +exports.updateTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const location = this.parseRequired(options.location, 'string', 'stripe.updateTerminalLocation: location is required.'); + delete options.location; + return stripe.terminal.locations.update(location, options,__extra); +}; + +// /v1/terminal/readers - get +exports.listTerminalReaders = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.readers.list(options,__extra); +}; + +// /v1/terminal/readers - post +exports.createTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.readers.create(options,__extra); +}; + +// /v1/terminal/readers/{reader} - delete +exports.deleteTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.deleteTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.del(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader} - get +exports.retrieveTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.retrieveTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.retrieve(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader} - post +exports.updateTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.updateTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.update(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/cancel_action - post +exports.cancelActionTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.cancelActionTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.cancelAction(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/process_payment_intent - post +exports.processPaymentIntentTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.processPaymentIntentTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.processPaymentIntent(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/process_setup_intent - post +exports.processSetupIntentTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.processSetupIntentTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.processSetupIntent(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/refund_payment - post +exports.refundPaymentTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.refundPaymentTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.refundPayment(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/set_reader_display - post +exports.setReaderDisplayTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.setReaderDisplayTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.setReaderDisplay(reader, options,__extra); +}; + +// /v1/test_helpers/confirmation_tokens - post +exports.createTestHelpersConfirmationToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.testHelpers.confirmationTokens.create(options,__extra); +}; + +// /v1/test_helpers/customers/{customer}/fund_cash_balance - post +exports.fundCashBalanceTestHelpersCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.fundCashBalanceTestHelpersCustomer: customer is required.'); + delete options.customer; + return stripe.testHelpers.customers.fundCashBalance(customer, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations - post +exports.createTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.createIssuingAuthorizations(options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/capture - post +exports.captureTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.captureTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.capture(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/expire - post +exports.expireTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.expireTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.expire(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/increment - post +exports.incrementTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.incrementTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.increment(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/reverse - post +exports.reverseTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.reverseTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.reverse(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/cards/{card}/shipping/deliver - post +exports.createTestHelpersIssuingCardShipping = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.createTestHelpersIssuingCardShipping: card is required.'); + delete options.card; + return stripe.testHelpers.issuing.cards.createShipping(card, options,__extra); +}; + +// /v1/test_helpers/issuing/personalization_designs/{personalization_design}/activate - post +exports.activateTestHelpersIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.activateTestHelpersIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.testHelpers.issuing.personalizationDesigns.activate(personalization_design, options,__extra); +}; + +// /v1/test_helpers/issuing/personalization_designs/{personalization_design}/deactivate - post +exports.deactivateTestHelpersIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.deactivateTestHelpersIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.testHelpers.issuing.personalizationDesigns.deactivate(personalization_design, options,__extra); +}; + +// /v1/test_helpers/issuing/personalization_designs/{personalization_design}/reject - post +exports.rejectTestHelpersIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.rejectTestHelpersIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.testHelpers.issuing.personalizationDesigns.reject(personalization_design, options,__extra); +}; + +// /v1/test_helpers/issuing/transactions/create_force_capture - post +exports.createTestHelpersIssuingTransactionsCreateForceCapture = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.issuing.transactions.createForceCapture.create(options,__extra); +}; + +// /v1/test_helpers/issuing/transactions/create_unlinked_refund - post +exports.createTestHelpersIssuingTransactionsCreateUnlinkedRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.issuing.transactions.createUnlinkedRefund.create(options,__extra); +}; + +// /v1/test_helpers/issuing/transactions/{transaction}/refund - post +exports.refundTestHelpersIssuingTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.refundTestHelpersIssuingTransaction: transaction is required.'); + delete options.transaction; + return stripe.testHelpers.issuing.transactions.refund(transaction, options,__extra); +}; + +// /v1/test_helpers/refunds/{refund}/expire - post +exports.expireTestHelpersRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.expireTestHelpersRefund: refund is required.'); + delete options.refund; + return stripe.testHelpers.refunds.expire(refund, options,__extra); +}; + +// /v1/test_helpers/terminal/readers/{reader}/present_payment_method - post +exports.presentPaymentMethodTestHelpersTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.presentPaymentMethodTestHelpersTerminalReader: reader is required.'); + delete options.reader; + return stripe.testHelpers.terminal.readers.presentPaymentMethod(reader, options,__extra); +}; + +// /v1/test_helpers/test_clocks - get +exports.listTestHelpersTestClocks = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.testClocks.list(options,__extra); +}; + +// /v1/test_helpers/test_clocks - post +exports.createTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.testClocks.create(options,__extra); +}; + +// /v1/test_helpers/test_clocks/{test_clock} - delete +exports.deleteTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const test_clock = this.parseRequired(options.test_clock, 'string', 'stripe.deleteTestHelpersTestClock: test_clock is required.'); + delete options.test_clock; + return stripe.testHelpers.testClocks.del(test_clock, options,__extra); +}; + +// /v1/test_helpers/test_clocks/{test_clock} - get +exports.retrieveTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const test_clock = this.parseRequired(options.test_clock, 'string', 'stripe.retrieveTestHelpersTestClock: test_clock is required.'); + delete options.test_clock; + return stripe.testHelpers.testClocks.retrieve(test_clock, options,__extra); +}; + +// /v1/test_helpers/test_clocks/{test_clock}/advance - post +exports.advanceTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const test_clock = this.parseRequired(options.test_clock, 'string', 'stripe.advanceTestHelpersTestClock: test_clock is required.'); + delete options.test_clock; + return stripe.testHelpers.testClocks.advance(test_clock, options,__extra); +}; + +// /v1/test_helpers/treasury/inbound_transfers/{id}/fail - post +exports.failTestHelpersTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.failTestHelpersTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.inboundTransfers.fail(id, options,__extra); +}; + +// /v1/test_helpers/treasury/inbound_transfers/{id}/return - post +exports.returnTestHelpersTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.returnTestHelpersTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.inboundTransfers.return(id, options,__extra); +}; + +// /v1/test_helpers/treasury/inbound_transfers/{id}/succeed - post +exports.succeedTestHelpersTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.succeedTestHelpersTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.inboundTransfers.succeed(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_payments/{id}/fail - post +exports.failTestHelpersTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.failTestHelpersTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.outboundPayments.fail(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_payments/{id}/post - post +exports.postTestHelpersTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.postTestHelpersTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.outboundPayments.post(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_payments/{id}/return - post +exports.returnTestHelpersTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.returnTestHelpersTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.outboundPayments.return(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/fail - post +exports.failTestHelpersTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.failTestHelpersTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.testHelpers.treasury.outboundTransfers.fail(outbound_transfer, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/post - post +exports.postTestHelpersTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.postTestHelpersTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.testHelpers.treasury.outboundTransfers.post(outbound_transfer, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/return - post +exports.returnTestHelpersTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.returnTestHelpersTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.testHelpers.treasury.outboundTransfers.return(outbound_transfer, options,__extra); +}; + +// /v1/test_helpers/treasury/received_credits - post +exports.createTestHelpersTreasuryReceivedCredit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.createTreasuryReceivedCredits(options,__extra); +}; + +// /v1/test_helpers/treasury/received_debits - post +exports.createTestHelpersTreasuryReceivedDebit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.createTreasuryReceivedDebits(options,__extra); +}; + +// /v1/tokens - post +exports.createToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tokens.create(options,__extra); +}; + +// /v1/tokens/{token} - get +exports.retrieveToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const token = this.parseRequired(options.token, 'string', 'stripe.retrieveToken: token is required.'); + delete options.token; + return stripe.tokens.retrieve(token, options,__extra); +}; + +// /v1/topups - get +exports.listTopups = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.topups.list(options,__extra); +}; + +// /v1/topups - post +exports.createTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.topups.create(options,__extra); +}; + +// /v1/topups/{topup} - get +exports.retrieveTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const topup = this.parseRequired(options.topup, 'string', 'stripe.retrieveTopup: topup is required.'); + delete options.topup; + return stripe.topups.retrieve(topup, options,__extra); +}; + +// /v1/topups/{topup} - post +exports.updateTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const topup = this.parseRequired(options.topup, 'string', 'stripe.updateTopup: topup is required.'); + delete options.topup; + return stripe.topups.update(topup, options,__extra); +}; + +// /v1/topups/{topup}/cancel - post +exports.cancelTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const topup = this.parseRequired(options.topup, 'string', 'stripe.cancelTopup: topup is required.'); + delete options.topup; + return stripe.topups.cancel(topup, options,__extra); +}; + +// /v1/transfers - get +exports.listTransfers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.transfers.list(options,__extra); +}; + +// /v1/transfers - post +exports.createTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.transfers.create(options,__extra); +}; + +// /v1/transfers/{id}/reversals - get +exports.listTransferReversals = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.listTransferReversals: id is required.'); + delete options.id; + return stripe.transfers.listReversals(id, options,__extra); +}; + +// /v1/transfers/{id}/reversals - post +exports.createTransferReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.createTransferReversal: id is required.'); + delete options.id; + return stripe.transfers.createReversal(id, options,__extra); +}; + +// /v1/transfers/{transfer} - get +exports.retrieveTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.retrieveTransfer: transfer is required.'); + delete options.transfer; + return stripe.transfers.retrieve(transfer, options,__extra); +}; + +// /v1/transfers/{transfer} - post +exports.updateTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.updateTransfer: transfer is required.'); + delete options.transfer; + return stripe.transfers.update(transfer, options,__extra); +}; + +// /v1/transfers/{transfer}/reversals/{id} - get +exports.retrieveTransferReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTransferReversal: id is required.'); + delete options.id; + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.retrieveTransferReversal: transfer is required.'); + delete options.transfer; + return stripe.transfers.retrieveReversal(id, transfer, options,__extra); +}; + +// /v1/transfers/{transfer}/reversals/{id} - post +exports.updateTransferReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateTransferReversal: id is required.'); + delete options.id; + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.updateTransferReversal: transfer is required.'); + delete options.transfer; + return stripe.transfers.updateReversal(id, transfer, options,__extra); +}; + +// /v1/treasury/credit_reversals - get +exports.listTreasuryCreditReversals = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryCreditReversals: financial_account is required.'); + return stripe.treasury.creditReversals.list(options,__extra); +}; + +// /v1/treasury/credit_reversals - post +exports.createTreasuryCreditReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.creditReversals.create(options,__extra); +}; + +// /v1/treasury/credit_reversals/{credit_reversal} - get +exports.retrieveTreasuryCreditReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const credit_reversal = this.parseRequired(options.credit_reversal, 'string', 'stripe.retrieveTreasuryCreditReversal: credit_reversal is required.'); + delete options.credit_reversal; + return stripe.treasury.creditReversals.retrieve(credit_reversal, options,__extra); +}; + +// /v1/treasury/debit_reversals - get +exports.listTreasuryDebitReversals = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryDebitReversals: financial_account is required.'); + return stripe.treasury.debitReversals.list(options,__extra); +}; + +// /v1/treasury/debit_reversals - post +exports.createTreasuryDebitReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.debitReversals.create(options,__extra); +}; + +// /v1/treasury/debit_reversals/{debit_reversal} - get +exports.retrieveTreasuryDebitReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const debit_reversal = this.parseRequired(options.debit_reversal, 'string', 'stripe.retrieveTreasuryDebitReversal: debit_reversal is required.'); + delete options.debit_reversal; + return stripe.treasury.debitReversals.retrieve(debit_reversal, options,__extra); +}; + +// /v1/treasury/financial_accounts - get +exports.listTreasuryFinancialAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.financialAccounts.list(options,__extra); +}; + +// /v1/treasury/financial_accounts - post +exports.createTreasuryFinancialAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.financialAccounts.create(options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account} - get +exports.retrieveTreasuryFinancialAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.retrieveTreasuryFinancialAccount: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.retrieve(financial_account, options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account} - post +exports.updateTreasuryFinancialAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.updateTreasuryFinancialAccount: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.update(financial_account, options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account}/features - get +exports.retrieveTreasuryFinancialAccountFeature = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.retrieveTreasuryFinancialAccountFeature: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.retrieveFeature(financial_account, options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account}/features - post +exports.createTreasuryFinancialAccountFeature = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.createTreasuryFinancialAccountFeature: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.createFeature(financial_account, options,__extra); +}; + +// /v1/treasury/inbound_transfers - get +exports.listTreasuryInboundTransfers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryInboundTransfers: financial_account is required.'); + return stripe.treasury.inboundTransfers.list(options,__extra); +}; + +// /v1/treasury/inbound_transfers - post +exports.createTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.inboundTransfers.create(options,__extra); +}; + +// /v1/treasury/inbound_transfers/{id} - get +exports.retrieveTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.treasury.inboundTransfers.retrieve(id, options,__extra); +}; + +// /v1/treasury/inbound_transfers/{inbound_transfer}/cancel - post +exports.cancelTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const inbound_transfer = this.parseRequired(options.inbound_transfer, 'string', 'stripe.cancelTreasuryInboundTransfer: inbound_transfer is required.'); + delete options.inbound_transfer; + return stripe.treasury.inboundTransfers.cancel(inbound_transfer, options,__extra); +}; + +// /v1/treasury/outbound_payments - get +exports.listTreasuryOutboundPayments = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryOutboundPayments: financial_account is required.'); + return stripe.treasury.outboundPayments.list(options,__extra); +}; + +// /v1/treasury/outbound_payments - post +exports.createTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.outboundPayments.create(options,__extra); +}; + +// /v1/treasury/outbound_payments/{id} - get +exports.retrieveTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.treasury.outboundPayments.retrieve(id, options,__extra); +}; + +// /v1/treasury/outbound_payments/{id}/cancel - post +exports.cancelTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.cancelTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.treasury.outboundPayments.cancel(id, options,__extra); +}; + +// /v1/treasury/outbound_transfers - get +exports.listTreasuryOutboundTransfers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryOutboundTransfers: financial_account is required.'); + return stripe.treasury.outboundTransfers.list(options,__extra); +}; + +// /v1/treasury/outbound_transfers - post +exports.createTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.outboundTransfers.create(options,__extra); +}; + +// /v1/treasury/outbound_transfers/{outbound_transfer} - get +exports.retrieveTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.retrieveTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.treasury.outboundTransfers.retrieve(outbound_transfer, options,__extra); +}; + +// /v1/treasury/outbound_transfers/{outbound_transfer}/cancel - post +exports.cancelTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.cancelTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.treasury.outboundTransfers.cancel(outbound_transfer, options,__extra); +}; + +// /v1/treasury/received_credits - get +exports.listTreasuryReceivedCredits = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryReceivedCredits: financial_account is required.'); + return stripe.treasury.receivedCredits.list(options,__extra); +}; + +// /v1/treasury/received_credits/{id} - get +exports.retrieveTreasuryReceivedCredit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryReceivedCredit: id is required.'); + delete options.id; + return stripe.treasury.receivedCredits.retrieve(id, options,__extra); +}; + +// /v1/treasury/received_debits - get +exports.listTreasuryReceivedDebits = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryReceivedDebits: financial_account is required.'); + return stripe.treasury.receivedDebits.list(options,__extra); +}; + +// /v1/treasury/received_debits/{id} - get +exports.retrieveTreasuryReceivedDebit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryReceivedDebit: id is required.'); + delete options.id; + return stripe.treasury.receivedDebits.retrieve(id, options,__extra); +}; + +// /v1/treasury/transaction_entries - get +exports.listTreasuryTransactionEntries = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryTransactionEntries: financial_account is required.'); + return stripe.treasury.transactionEntries.list(options,__extra); +}; + +// /v1/treasury/transaction_entries/{id} - get +exports.retrieveTreasuryTransactionEntrie = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryTransactionEntrie: id is required.'); + delete options.id; + return stripe.treasury.transactionEntries.retrieve(id, options,__extra); +}; + +// /v1/treasury/transactions - get +exports.listTreasuryTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryTransactions: financial_account is required.'); + return stripe.treasury.transactions.list(options,__extra); +}; + +// /v1/treasury/transactions/{id} - get +exports.retrieveTreasuryTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryTransaction: id is required.'); + delete options.id; + return stripe.treasury.transactions.retrieve(id, options,__extra); +}; + +// /v1/webhook_endpoints - get +exports.listWebhookEndpoints = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.webhookEndpoints.list(options,__extra); +}; + +// /v1/webhook_endpoints - post +exports.createWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.webhookEndpoints.create(options,__extra); +}; + +// /v1/webhook_endpoints/{webhook_endpoint} - delete +exports.deleteWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const webhook_endpoint = this.parseRequired(options.webhook_endpoint, 'string', 'stripe.deleteWebhookEndpoint: webhook_endpoint is required.'); + delete options.webhook_endpoint; + return stripe.webhookEndpoints.del(webhook_endpoint, options,__extra); +}; + +// /v1/webhook_endpoints/{webhook_endpoint} - get +exports.retrieveWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const webhook_endpoint = this.parseRequired(options.webhook_endpoint, 'string', 'stripe.retrieveWebhookEndpoint: webhook_endpoint is required.'); + delete options.webhook_endpoint; + return stripe.webhookEndpoints.retrieve(webhook_endpoint, options,__extra); +}; + +// /v1/webhook_endpoints/{webhook_endpoint} - post +exports.updateWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const webhook_endpoint = this.parseRequired(options.webhook_endpoint, 'string', 'stripe.updateWebhookEndpoint: webhook_endpoint is required.'); + delete options.webhook_endpoint; + return stripe.webhookEndpoints.update(webhook_endpoint, options,__extra); +}; diff --git a/lib/modules/upload.js b/lib/modules/upload.js new file mode 100644 index 0000000..feb5b79 --- /dev/null +++ b/lib/modules/upload.js @@ -0,0 +1,118 @@ +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:upload'); +const { join, basename, extname } = require('path'); +const { toAppPath, toSystemPath, toSiteUrl, parseTemplate, getUniqFile } = require('../core/path'); +const diacritics = require('../core/diacritics'); + +module.exports = { + + upload: async function(options) { + let self = this; + let fields = this.parse(options.fields || this.parse('{{$_POST}}')); + let path = this.parseOptional(options.path, 'string', '/uploads'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createPath = this.parseOptional(options.createPath, 'boolean', true); + let throwErrors = this.parseOptional(options.throwErrors, 'boolean', false); + let template = typeof options.template == 'string' ? options.template : false; //this.parseOptional(options.template, 'string', ''); + let replaceSpace = this.parseOptional(options.replaceSpace, 'boolean', false); + let asciiOnly = this.parseOptional(options.asciiOnly, 'boolean', false); + let replaceDiacritics = this.parseOptional(options.replaceDiacritics, 'boolean', false); + + if (throwErrors) { + for (let field in this.req.files) { + if (Array.isArray(this.req.files[field])) { + for (let file of this.req.files[field]) { + if (file.truncated) { + throw new Error('Some files failed to upload.'); + } + } + } else { + let file = this.req.files[field]; + if (file.truncated) { + throw new Error('Some files failed to upload.'); + } + } + } + } + + path = toSystemPath(path); + + if (!fs.existsSync(path)) { + if (createPath) { + await fs.ensureDir(path); + } else { + throw new Error(`Upload path doesn't exist.`); + } + } + + let files = this.req.files; + let uploaded = []; + + if (files) { + await processFields(fields); + } + + return typeof fields == 'string' ? (uploaded.length ? uploaded[0] : null) : uploaded; + + async function processFields(fields) { + debug('Process fields: %O', fields); + + if (typeof fields == 'object') { + for (let i in fields) { + await processFields(fields[i]); + } + } else if (typeof fields == 'string' && files[fields]) { + let processing = files[fields]; + + if (!Array.isArray(processing)) processing = [processing]; + + for (let file of processing) { + debug('Processing file: %O', file); + + if (!file.processed) { + let name = file.name.replace(/[\x00-\x1f\x7f!%&#@$*()?:,;"'<>^`|+={}\[\]\\\/]/g, ''); + + if (replaceSpace) name = name.replace(/\s+/g, '_'); + if (replaceDiacritics) name = diacritics.replace(name); + if (asciiOnly) name = name.replace(/[^\x00-\x7e]/g, ''); + + let filepath = join(path, name); + + if (template) { + let _template = self.parse(template, self.scope.create({ + file: basename(filepath), + name: basename(filepath, extname(filepath)), + ext: extname(filepath) + })); + filepath = parseTemplate(filepath, _template); + } + + if (fs.existsSync(filepath)) { + if (overwrite) { + await fs.unlink(filepath); + } else { + filepath = getUniqFile(filepath); + } + } + + await file.mv(filepath); + + uploaded.push({ + name: basename(filepath), + path: toAppPath(filepath), + url: toSiteUrl(filepath), + type: file.mimetype, + size: file.size + }); + + file.processed = true; + } + } + } + + return uploaded; + } + + } + +}; \ No newline at end of file diff --git a/lib/modules/validator.js b/lib/modules/validator.js new file mode 100644 index 0000000..2c865a1 --- /dev/null +++ b/lib/modules/validator.js @@ -0,0 +1,18 @@ +const validator = require('../validator'); + +module.exports = { + + validate: function(options) { + const data = this.parse(options.data); + const noError = this.parseOptional(options.noError, 'boolean', false); + + return validator.validateData(this, data, noError); + }, + + error: function(options, name) { + const message = this.parseRequired(options.message, 'string', 'validator.error: message is required.'); + const error = { [options.fieldName ? 'form' : 'data']: {[options.fieldName || name]: message }}; + this.res.status(400).json(error); + }, + +}; \ No newline at end of file diff --git a/lib/modules/zip.js b/lib/modules/zip.js new file mode 100644 index 0000000..5abd1f7 --- /dev/null +++ b/lib/modules/zip.js @@ -0,0 +1,114 @@ +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:zip'); +const { basename } = require('path'); +const { toSystemPath, toAppPath, getFilesArray, getUniqFile } = require('../core/path'); +const openZip = (zipfile) => require('unzipper').Open.file(zipfile); + +const Zip = function(zipfile, options) { + this.output = fs.createWriteStream(zipfile); + this.archive = require('archiver')('zip', options); + this.archive.on('warning', (err) => { + if (err.code == 'ENOENT') { + debug('error: %O', err); + } else { + throw err; + } + }); + this.archive.on('error', (err) => { + throw err; + }); + this.archive.pipe(this.output); +}; + +Zip.prototype.addFile = function(file) { + this.archive.file(file, { name: basename(file) }); +}; + +Zip.prototype.addDir = function(dir, recursive) { + if (recursive) { + this.archive.directory(dir, false); + } else { + this.archive.glob('*', { cwd: dir }); + } +}; + +Zip.prototype.save = function() { + return new Promise((resolve, reject) => { + this.output.on('close', resolve); + this.archive.on('error', reject); + this.archive.finalize(); + }); +}; + +module.exports = { + + zip: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.zip: zipfile is required.')); + let files = getFilesArray(this.parseRequired(options.files, 'object', 'zip.zip: files is requires.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let comment = this.parseOptional(options.comment, 'string', ''); + + if (!overwrite) zipfile = getUniqFile(zipfile); + + const zip = new Zip(zipfile, { comment }); + + for (let file of files) { + zip.addFile(file); + } + + await zip.save(); + + return toAppPath(zipfile); + }, + + zipdir: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.zipdir: zipfile is required.')); + let path = toSystemPath(this.parseRequired(options.path, 'string', 'zip.zipdir: path is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let recursive = this.parseOptional(options.recursive, 'boolean', false); + let comment = this.parseOptional(options.comment, 'string', ''); + + if (!overwrite) zipfile = getUniqFile(zipfile); + + const zip = new Zip(zipfile, { comment }); + + zip.addDir(path, recursive); + + await zip.save(); + + return toAppPath(zipfile); + }, + + unzip: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.unzip: zipfile is required.')); + let dest = toSystemPath(this.parseRequired(options.destination, 'string', 'zip.unzip: destination is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', true); + + // TODO: overwrite option + await openZip(zipfile).then(d => d.extract({ path: dest, concurrency: 4 })); + + return true; + }, + + dir: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.dir: zipfile is required.')); + + return openZip(zipfile).then(d => { + return d.files.map(file => ({ + type: file.type == 'Directory' ? 'dir': 'file', + path: file.path, + size: file.uncompressedSize, + compressedSize: file.compressedSize, + compressionMethod: file.compressionMethod == 8 ? 'Deflate' : 'None', + lastModified: file.lastModifiedDateTime + })); + }); + }, + + comment: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.comment: zipfile is required.')); + + return openZip(zipfile).then(d => d.comment); + }, + +}; \ No newline at end of file diff --git a/lib/oauth/index.js b/lib/oauth/index.js new file mode 100644 index 0000000..e80a480 --- /dev/null +++ b/lib/oauth/index.js @@ -0,0 +1,170 @@ +const { http, https } = require('follow-redirects'); +const querystring = require('querystring'); +const services = require('./services'); +const crypto = require('crypto'); + +class OAuth2 { + + constructor(app, opts, name) { + this.app = app; + this.name = name; + this.opts = opts; + + if (opts.service) { + Object.assign(this.opts, services[opts.service]); + } + + this.access_token = opts.access_token || app.getSession(`${name}_access_token`); + this.refresh_token = opts.refresh_token || app.getSession(`${name}_refresh_token`); + + if (this.access_token) { + let expires = app.getSession(`${name}_expires`); + + if (expires && expires < expires_in(-10)) { + this.access_token = null; + + app.removeSession(`${name}_access_token`); + app.removeSession(`${name}_expires`); + + if (this.refresh_token) { + this.refreshToken(this.refresh_token); + } + } + } + } + + async init() { + if (this.opts.jwt_bearer && !this.access_token) { + const assertion = this.app.getJSONWebToken(this.opts.jwt_bearer); + let response = await this.grant('urn:ietf:params:oauth:grant-type:jwt-bearer', { assertion }); + this.access_token = response.access_token; + } + + if (this.opts.client_credentials && !this.access_token) { + let response = await this.grant('client_credentials'); + this.access_token = response.access_token; + } + } + + async authorize(scopes = [], params = {}) { + let query = this.app.req.query; + + if (query.state && query.state == this.app.getSession(`${this.name}_state`)) { + this.app.removeSession(`${this.name}_state`); + + if (query.error) { + throw new Error(query.error_message || query.error); + } + + if (query.code) { + let params = { + redirect_uri: redirect_uri(this.app.req), + code: query.code + }; + + if (this.app.getSession(`${this.name}_code_verifier`)) { + params.code_verifier = this.app.getSession(`${this.name}_code_verifier`); + } + + return this.grant('authorization_code', params); + } + } + + if (this.opts.pkce || params.code_verifier) { + const code_verifier = params.code_verifier || crypto.randomBytes(40).toString('hex'); + this.app.setSession(`${this.name}_code_verifier`, code_verifier); + params.code_challenge_method = 'S256'; + params.code_challenge = crypto.createHash('sha256').update(code_verifier).digest('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); + } + + params = Object.assign({}, { + response_type: 'code', + client_id: this.opts.client_id, + scope: scopes.join(this.opts.scope_separator), + redirect_uri: redirect_uri(this.app.req), + state: generate_state() + }, this.opts.params, params); + + this.app.setSession(`${this.name}_state`, params.state); + + this.app.res.redirect(build_url(this.opts.auth_endpoint, params)); + } + + async refreshToken(refresh_token) { + return this.grant('refresh_token', { refresh_token }); + } + + async grant(type, params) { + const endpoint = new URL(this.opts.token_endpoint); + + return new Promise((resolve, reject) => { + const req = (endpoint.protocol == 'https:' ? https : http).request(endpoint, { + method: 'POST' + }, res => { + let body = ''; + res.setEncoding('utf8'); + res.on('data', chunk => body += chunk); + res.on('end', () => { + if (res.statusCode >= 400) { + return reject(new Error(`Http status code ${res.statusCode}. ${body}`)); + } + + try { + body = JSON.parse(body); + } catch (e) { } + + if (!body.access_token) { + return reject(new Error(`Http response has no access_token. ${JSON.stringify(body)}`)) + } + + this.app.setSession(`${this.name}_access_token`, body.access_token); + this.access_token = body.access_token; + + if (body.expires_in) { + this.app.setSession(`${this.name}_expires`, expires_in(body.expires_in)); + } + + if (body.refresh_token) { + this.app.setSession(`${this.name}_refresh_token`, body.refresh_token); + this.refresh_token = body.refresh_token; + } + + resolve(body); + }); + }); + + const body = querystring.stringify(Object.assign({ + grant_type: type, + client_id: this.opts.client_id, + client_secret: this.opts.client_secret + }, params)); + + req.on('error', reject); + req.setHeader('Content-Type', 'application/x-www-form-urlencoded'); + req.setHeader('Content-Length', body.length); // required by azure endpoint + req.write(body); + req.end(); + }); + } + +} + +function build_url(url, params) { + return url + (url.indexOf('?') != -1 ? '&' : '?') + querystring.stringify(params); +} + +function redirect_uri(req) { + let hasProxy = !!req.get('x-forwarded-host'); + return `${req.protocol}://${hasProxy ? req.hostname : req.get('host')}${req.path}`; +} + +function expires_in(s) { + return ~~(Date.now() / 1000) + s; +} + +function generate_state() { + const { v4: uuidv4 } = require('uuid'); + return uuidv4(); +} + +module.exports = OAuth2; \ No newline at end of file diff --git a/lib/oauth/services.js b/lib/oauth/services.js new file mode 100644 index 0000000..f23dda7 --- /dev/null +++ b/lib/oauth/services.js @@ -0,0 +1,98 @@ +module.exports = { + + 'google': { + auth_endpoint: 'https://accounts.google.com/o/oauth2/v2/auth', + token_endpoint: 'https://www.googleapis.com/oauth2/v4/token', + params: { access_type: 'offline' } + }, + + 'facebook': { + auth_endpoint: 'https://www.facebook.com/v3.2/dialog/oauth', + token_endpoint: 'https://graph.facebook.com/v3.2/oauth/access_token' + }, + + 'linkedin': { + auth_endpoint: 'https://www.linkedin.com/oauth/v2/authorization', + token_endpoint: 'https://www.linkedin.com/oauth/v2/accessToken' + }, + + 'github': { + auth_endpoint: 'https://github.com/login/oauth/authorize', + token_endpoint: 'https://github.com/login/oauth/access_token' + }, + + 'instagram': { + auth_endpoint: 'https://api.instagram.com/oauth/authorize/', + token_endpoint: 'https://api.instagram.com/oauth/access_token' + }, + + 'amazon': { + auth_endpoint: 'https://www.amazon.com/ap/oa', + token_endpoint: 'https://api.amazon.com/auth/o2/token' + }, + + 'dropbox': { + auth_endpoint: 'https://www.dropbox.com/oauth2/authorize', + token_endpoint: 'https://api.dropbox.com/oauth2/token', + scope_separator: ',' + }, + + 'foursquare': { + auth_endpoint: 'https://foursquare.com/oauth2/authenticate', + token_endpoint: 'https://foursquare.com/oauth2/access_token' + }, + + 'imgur': { + auth_endpoint: 'https://api.imgur.com/oauth2/authorize', + token_endpoint: 'https://api.imgur.com/oauth2/token' + }, + + 'wordpress': { + auth_endpoint: 'https://public-api.wordpress.com/oauth2/authorize', + token_endpoint: 'https://public-api.wordpress.com/oauth2/token' + }, + + 'spotify': { + auth_endpoint: 'https://accounts.spotify.com/authorize', + token_endpoint: 'https://accounts.spotify.com/api/token' + }, + + 'slack': { + auth_endpoint: 'https://slack.com/oauth/authorize', + token_endpoint: 'https://slack.com/api/oauth.access' + }, + + 'reddit': { + auth_endpoint: 'https://ssl.reddit.com/api/v1/authorize', + token_endpoint: 'https://ssl.reddit.com/api/v1/access_token', + scope_separator: ',' + }, + + 'twitch': { + auth_endpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + token_endpoint: 'https://api.twitch.tv/kraken/oauth2/token' + }, + + 'paypal': { + auth_endpoint: 'https://identity.x.com/xidentity/resources/authorize', + token_endpoint: 'https://identity.x.com/xidentity/oauthtokenservice' + }, + + 'pinterest': { + auth_endpoint: 'https://api.pinterest.com/oauth/', + token_endpoint: 'https://api.pinterest.com/v1/oauth/token', + scope_separator: ',' + }, + + 'stripe': { + auth_endpoint: 'https://connect.stripe.com/oauth/authorize', + token_endpoint: 'https://connect.stripe.com/oauth/token', + scope_separator: ',' + }, + + 'coinbase': { + auth_endpoint: 'https://www.coinbase.com/oauth/authorize', + token_endpoint: 'https://www.coinbase.com/oauth/token' + } + +}; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js new file mode 100644 index 0000000..04d1b2d --- /dev/null +++ b/lib/server.js @@ -0,0 +1,110 @@ +if (process.env.NODE_ENV !== 'production') { + require('dotenv').config(); +} + +process.on('uncaughtException', (e) => { + // prevent errors from killing the server and just log them + console.error(e); +}); + +const config = require('./setup/config'); +const debug = require('debug')('server-connect:server'); +const secure = require('./setup/secure'); +const routes = require('./setup/routes'); +const sockets = require('./setup/sockets'); +const upload = require('./setup/upload'); +const cron = require('./setup/cron'); +const http = require('http'); +const express = require('express'); +const endmw = require('express-end'); +const cookieParser = require('cookie-parser'); +const session = require('./setup/session'); //require('express-session')(Object.assign({ secret: config.secret }, config.session)); +const cors = require('cors'); +const app = express(); + +app.set('trust proxy', true); +app.set('view engine', 'ejs'); +app.set('view options', { root: 'views', async: true }); + +app.disable('x-powered-by') + +if (config.compression) { + const compression = require('compression'); + app.use(compression()); +} + +if (config.abortOnDisconnect) { + app.use((req, res, next) => { + req.isDisconnected = false; + req.on('close', () => { + req.isDisconnected = true; + }); + + next(); + }); +} + +app.use(cors(config.cors)); +app.use(express.static('public', config.static)); +app.use(express.urlencoded({ extended: true })); +app.use(express.json({ + verify: (req, res, buf) => { + req.rawBody = buf.toString() + } +})); +app.use(cookieParser(config.secret)); +app.use(session); +app.use(endmw); + +upload(app); +secure(app); +routes(app); + +const server = http.createServer(app); +const io = sockets(server, session); + +// Make sockets global available +global.io = io; + +module.exports = { + server, app, io, + start: function(port) { + // We add the 404 and 500 routes as last + app.use((req, res) => { + // if user has a custom 404 page, redirect to it + if (req.accepts('html') && req.url != '/404' && app.get('has404')) { + //res.redirect(303, '/404'); + req.url = '/404'; + app.handle(req, res); + } else { + res.status(404).json({ + status: '404', + message: `${req.url} not found.` + }); + } + }); + + app.use((err, req, res, next) => { + debug(`Got error? %O`, err); + // if user has a custom 500 page, redirect to it + if (req.accepts('html') && req.url != '/500' && app.get('has500')) { + //res.redirect(303, '/500'); + req.url = '/500'; + app.handle(req, res); + } else { + res.status(500).json({ + status: '500', + code: config.debug ? err.code : undefined, + message: config.debug ? err.message || err : 'A server error occured, to see the error enable the DEBUG flag.', + stack: config.debug ? err.stack : undefined, + }); + } + }); + + cron.start(); + + server.listen(port || config.port, () => { + console.log(`App listening at http://localhost:${config.port}`); + }); + } +}; diff --git a/lib/setup/config.js b/lib/setup/config.js new file mode 100644 index 0000000..258d8c9 --- /dev/null +++ b/lib/setup/config.js @@ -0,0 +1,105 @@ +const package = require('../../package.json'); +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:setup:config'); +const { toSystemPath } = require('../core/path'); +const { mergeDeep } = require('../core/util'); +const Parser = require('../core/parser'); +const Scope = require('../core/scope'); + +const config = { + port: process.env.PORT || 3000, + debug: false, + secret: 'Need to be set', + tmpFolder: '/tmp', + abortOnDisconnect: false, + createApiRoutes: true, + compression: true, + redis: false, + cron: true, + static: { + index: false, + }, + session: { + name: package.name + '.sid', + resave: false, + saveUninitialized: false, + store: { $type: 'memory', ttl: 86400 }, + }, + cors: { // see https://github.com/expressjs/cors + origin: false, + methods: 'GET,POST', + credentials: true, + }, + csrf: { + enabled: false, + exclude: 'GET,HEAD,OPTIONS', + }, + rateLimit: { + enabled: false, + duration: 60, // duration of 60 second (1 minute) + points: 100, // limit to 100 requests per minute + private: { + provider: '', // security provider name + duration: 60, // duration of 60 second (1 minute) + points: 1000, // limit to 1000 requests per minute + }, + }, + globals: {}, + rateLimiter: {}, + mail: {}, + auth: {}, + oauth: {}, + db: {}, + s3: {}, + jwt: {}, + stripe: {}, + env: {}, +}; + +if (fs.existsSync('app/config/config.json')) { + mergeDeep(config, fs.readJSONSync('app/config/config.json')) +} + +if (fs.existsSync('app/config/user_config.json')) { + mergeDeep(config, fs.readJSONSync('app/config/user_config.json')); +} + +// folders are site relative +config.tmpFolder = toSystemPath(config.tmpFolder); + +if (config.env) { + for (let key in config.env) { + if (!Object.hasOwn(process.env, key)) { + process.env[key] = config.env[key]; + } else if (config.debug) { + debug(`"${key}" is already defined in \`process.env\` and will not be overwritten`); + } + } +} + +Parser.parseValue(config, new Scope({ + $_ENV: process.env +})); + +// we change the cors config a bit, * will become true +// and we split string on comma for multiple origins +if (typeof config.cors?.origin == 'string') { + if (config.cors.origin === '*') { + config.cors.origin = true; + } else if (config.cors.origin.includes(',')) { + config.cors.origin = config.cors.origin.split(/\s*,\s*/); + } +} + +if (config.debug) { + require('debug').enable(typeof config.debug == 'string' ? config.debug : 'server-connect:*'); +} + +if (config.redis) { + const Redis = require('ioredis'); + global.redisClient = new Redis(config.redis === true ? 'redis://redis' : config.redis); +} + +debug(config); + +module.exports = config; \ No newline at end of file diff --git a/lib/setup/cron.js b/lib/setup/cron.js new file mode 100644 index 0000000..30115fc --- /dev/null +++ b/lib/setup/cron.js @@ -0,0 +1,60 @@ +const fs = require('fs-extra'); +const { isEmpty } = require('./util'); +const config = require('./config'); +const debug = require('debug')('server-connect:cron'); + +exports.start = () => { + if (!config.cron || isEmpty('app/schedule')) return; + + debug('Start schedule'); + + processEntries('app/schedule'); +}; + +function processEntries(path) { + const schedule = require('node-schedule'); + const entries = fs.readdirSync(path, { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && entry.name.endsWith('.json')) { + try { + const job = fs.readJSONSync(`${path}/${entry.name}`); + const rule = job.settings.options.rule; + + debug(`Adding schedule ${entry.name}`); + + if (rule == '@reboot') { + setImmediate(exec(job.exec)); + } else { + schedule.scheduleJob(rule, exec(job.exec)) + } + } catch (e) { + console.error(e); + } + } else if (entry.isDirectory()) { + processEntries(`${path}/${entry.name}`); + } + } +} + +function exec(action) { + return async () => { + const App = require('../core/app'); + const app = new App({ + params: {}, + session: {}, + cookies: {}, + signedCookies: {}, + query: {}, + headers: {} + }, { + headersSent: false, + set() {}, + status() { return this; }, + send() { this.headersSent = true; }, + json() { this.headersSent = true; }, + redirect() { this.headersSent = true; } + }); + return app.define(action, true); + } +} \ No newline at end of file diff --git a/lib/setup/database.js b/lib/setup/database.js new file mode 100644 index 0000000..b41f008 --- /dev/null +++ b/lib/setup/database.js @@ -0,0 +1,36 @@ +// allow Wappler to run queries on the server + +const crypto = require('crypto'); +const App = require("../core/app"); +const config = require('./config'); + +module.exports = function (app) { + app.post('/_db_', async (req, res) => { + const time = req.get('auth-time'); + const hash = req.get('auth-hash'); + if (!time || !hash || isNaN(time)) return res.status(400).json({ error: 'Auth headers missing' }); + + const diff = Math.abs(Date.now() - time) / 1000; + if (diff > 60) return res.status(400).json({ error: 'Auth time diff to high' }); + if (hash !== crypto.createHmac('sha256', config.secret).update(req.rawBody).digest('hex')) return res.status(400).json({ error: 'Auth hash invalid' }); + + const sc = new App(req, res); + const db = sc.getDbConnection(req.body.name); + + let results = []; + + try { + results = await db.raw(req.body.query); + } catch (error) { + return res.json({ error: error.sqlMessage }); + } + + if (db.client.config.client == 'mysql' || db.client.config.client == 'mysql2') { + results = results[0]; + } else if (db.client.config.client == 'postgres' || db.client.config.client == 'redshift') { + results = results.rows; + } + + res.json({ results }); + }); +} diff --git a/lib/setup/redis.js b/lib/setup/redis.js new file mode 100644 index 0000000..4bfa262 --- /dev/null +++ b/lib/setup/redis.js @@ -0,0 +1,9 @@ +const config = require('./config'); + +if (config.redis) { + const Redis = require('ioredis'); + //global.redisClient = redis.createClient(config.redis === true ? 'redis://redis' : config.redis); + global.redisClient = new Redis(config.redis === true ? 'redis://redis' : config.redis); +} + +module.exports = global.redisClient; \ No newline at end of file diff --git a/lib/setup/routes.js b/lib/setup/routes.js new file mode 100644 index 0000000..9602b34 --- /dev/null +++ b/lib/setup/routes.js @@ -0,0 +1,298 @@ +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:setup:routes'); +const config = require('./config'); +const { map } = require('../core/async'); +const { posix, extname } = require('path'); +const { cache, serverConnect, templateView } = require('../core/middleware'); +const database = require('./database'); +const webhooks = require('./webhooks'); + +module.exports = async function (app) { + app.use((req, res, next) => { + req.fragment = (req.headers['accept'] || '*/*').includes('fragment'); + next(); + }); + + if (fs.existsSync('extensions/server_connect/routes')) { + const entries = fs.readdirSync('extensions/server_connect/routes', { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && extname(entry.name) == '.js') { + let hook = require(`../../extensions/server_connect/routes/${entry.name}`); + if (hook.before) hook.before(app); + if (hook.handler) hook.handler(app); + debug(`Custom router ${entry.name} loaded`); + } + } + } + + database(app); + webhooks(app); + + if (config.createApiRoutes) { + fs.ensureDirSync('app/api'); + createApiRoutes('app/api'); + } + + if (fs.existsSync('app/config/routes.json')) { + const { routes, layouts } = fs.readJSONSync('app/config/routes.json'); + + parseRoutes(routes, null); + + function parseRoutes(routes, parent) { + for (let route of routes) { + if (!route.path) continue; + + createRoute(route, parent); + + if (Array.isArray(route.routes)) { + parseRoutes(route.routes, route); + } + } + } + + function createRoute({ auth, path, method, redirect, url, page, layout, exec, data, ttl, status, proxy }, parent) { + method = method || 'all'; + data = data || {}; + if (page) page = page.replace(/^\//, ''); + if (layout) layout = layout.replace(/^\//, ''); + if (parent && parent.path) path = parent.path + path; + + if (auth) { + app.use(path, (req, res, next) => { + if (typeof auth == 'string' && req.session && req.session[auth + 'Id']) { + next(); + } else if (typeof auth == 'object' && auth.user) { + const b64auth = (req.headers.authorization || '').split(' ')[1] || ''; + const [user, password] = Buffer.from(b64auth, 'base64').toString().split(':'); + if (user && password && user === auth.user && password === auth.password) { + next(); + } else { + res.set('WWW-Authenticate', 'Basic realm="401"'); + res.status(401).json({ error: 'Unauthorized' }); + } + } else { + res.status(401).json({ error: 'Unauthorized' }); + } + }); + } + + if (proxy) { + const httpProxy = require('http-proxy'); + const proxyServer = httpProxy.createProxyServer(proxy); + app.use(path, (req, res) => { + proxyServer.web(req, res); + }); + } else if (redirect) { + app.get(path, (req, res) => res.redirect(status == 302 ? 302 : 301, redirect)); + } else if (url) { + app[method](path, (req, res, next) => { + next(parent && !req.fragment ? 'route' : null); + }, (req, res) => { + res.sendFile(url, { root: 'public' }) + }); + + if (parent) { + createRoute({ + path, + method: parent.method, + redirect: parent.redirect, + url: parent.url, + page: parent.page, + layout: parent.layout, + exec: parent.exec, + data: parent.data + }); + } + } else if (page) { + if (path == '/404') { + app.set('has404', true); + } + + if (path == '/500') { + app.set('has500', true); + } + + if (exec) { + if (fs.existsSync(`app/${exec}.json`)) { + let json = fs.readJSONSync(`app/${exec}.json`); + + if (json.exec && json.exec.steps) { + json = json.exec.steps; + } else if (json.steps) { + json = json.steps; + } + + if (!Array.isArray(json)) { + json = [json]; + } + + + if (layout && layouts && layouts[layout]) { + if (layouts[layout].data) { + data = Object.assign({}, layouts[layout].data, data); + } + + if (layouts[layout].exec) { + if (fs.existsSync(`app/${layouts[layout].exec}.json`)) { + let _json = fs.readJSONSync(`app/${layouts[layout].exec}.json`); + + if (_json.exec && _json.exec.steps) { + _json = _json.exec.steps; + } else if (_json.steps) { + _json = _json.steps; + } + + if (!Array.isArray(_json)) { + _json = [_json]; + } + + json = _json.concat(json); + } else { + debug(`Route ${path} skipped, "app/${exec}.json" not found`); + return; + } + } + } + + app[method](path, (req, res, next) => { + next(parent && !req.fragment ? 'route' : null); + }, cache({ttl}), templateView(layout, page, data, json)); + } else { + debug(`Route ${path} skipped, "app/${exec}.json" not found`); + return; + } + } else { + let json = []; + + if (layout && layouts && layouts[layout]) { + if (layouts[layout].data) { + data = Object.assign({}, layouts[layout].data, data); + } + + if (layouts[layout].exec) { + if (fs.existsSync(`app/${layouts[layout].exec}.json`)) { + let _json = fs.readJSONSync(`app/${layouts[layout].exec}.json`); + + if (_json.exec && _json.exec.steps) { + _json = _json.exec.steps; + } else if (_json.steps) { + _json = _json.steps; + } + + if (!Array.isArray(_json)) { + _json = [_json]; + } + + json = _json.concat(json); + } else { + debug(`Route ${path} skipped, "app/${exec}.json" not found`); + return; + } + } + } + + app[method](path, (req, res, next) => { + next(parent && !req.fragment ? 'route' : null); + }, cache({ttl}), templateView(layout, page, data, json)); + } + + if (parent) { + createRoute({ + path, + method: parent.method, + redirect: parent.redirect, + url: parent.url, + page: parent.page, + layout: parent.layout, + exec: parent.exec, + data: parent.data + }); + } + } else if (exec) { + if (fs.existsSync(`app/${exec}.json`)) { + let json = fs.readJSONSync(`app/${exec}.json`); + + app[method](path, cache({ttl}), serverConnect(json)); + + return; + } + } + } + } + + if (fs.existsSync('extensions/server_connect/routes')) { + const entries = fs.readdirSync('extensions/server_connect/routes', { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && extname(entry.name) == '.js') { + let hook = require(`../../extensions/server_connect/routes/${entry.name}`); + if (hook.after) hook.after(app); + debug(`Custom router ${entry.name} loaded`); + } + } + } + + function createApiRoutes(dir) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + return map(entries, async (entry) => { + if (entry.name.startsWith('_')) return; + + let path = posix.join(dir, entry.name); + + if (entry.isFile() && extname(path) == '.json') { + let json = fs.readJSONSync(path); + let routePath = (json.settings?.options?.path) ? json.settings.options.path : path.replace(/^app/i, '').replace(/.json$/, '(.json)?'); + let routeMethod = (json.settings?.options?.method) ? json.settings.options.method : 'all'; + let ttl = (json.settings?.options?.ttl) ? json.settings.options.ttl : 0; + let csrf = (json.settings?.options?.nocsrf) ? false : config.csrf?.enabled; + let points = (json.settings?.options?.points) ? json.settings.options.points : 1; + + app[routeMethod](routePath.replace(/\/\(.*?\)\//gi, '/'), (req, res, next) => { + if (app.rateLimiter && points > 0) { + let isPrivate = false; + let key = req.ip; + + if (app.privateRateLimiter) { + if (req.session && req.session[config.rateLimit.private.provider + 'Id']) { + isPrivate = true; + key = req.session[config.rateLimit.private.provider + 'Id']; + } + } + + app[isPrivate ? 'privateRateLimiter' : 'rateLimiter'].consume(key, points).then(rateLimiterRes => { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + res.set('RateLimit-Policy', `${config.rateLimit.points};w=${config.rateLimit.duration}`); + res.set('RateLimit', `limit=${config.rateLimit.points}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + next(); + }).catch(rateLimiterRes => { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + res.set('RateLimit-Policy', `${config.rateLimit.points};w=${config.rateLimit.duration}`); + res.set('RateLimit', `limit=${config.rateLimit.points}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + res.set('Retry-After', reset); + if (req.is('json')) { + res.status(429).json({ error: 'Too Many Requests' }); + } else { + res.status(429).send('Too Many Requests'); + } + }); + } else { + next(); + } + }, (req, res, next) => { + if (!csrf) return next(); + if (config.csrf.exclude.split(',').includes(req.method)) return next(); + if (!req.validateCSRF()) return res.status(403).send('Invalid CSRF token'); + next(); + }, cache({ttl}), serverConnect(json)); + + debug(`Api route ${routePath} created`); + } + + if (entry.isDirectory()) { + return createApiRoutes(path); + } + }); + } +}; diff --git a/lib/setup/secure.js b/lib/setup/secure.js new file mode 100644 index 0000000..85ecf2e --- /dev/null +++ b/lib/setup/secure.js @@ -0,0 +1,112 @@ +const debug = require('debug')('server-connect:secure'); +const config = require('./config'); + +module.exports = function (app) { + const { randomBytes } = require('crypto'); + const generateToken = (req, overwrite) => { + const sessionToken = req.session.csrfToken; + + if (!overwrite && typeof sessionToken === 'string') { + return sessionToken; + } + + const token = randomBytes(32).toString('hex'); + req.session.csrfToken = token; + + return token; + } + const getTokenFromRequest = (req) => req.headers['x-csrf-token'] || req.body.CSRFToken; + const isValidToken = (req) => { + const token = getTokenFromRequest(req); + const sessionToken = req.session.csrfToken; + return typeof token === 'string' && typeof sessionToken === 'string' && token === sessionToken; + } + + app.get('/__csrf', (req, res) => { + res.json({ csrfToken: generateToken(req, true) }); + }); + + app.use((req, res, next) => { + req.csrfToken = (overwrite) => generateToken(req, overwrite); + req.validateCSRF = () => isValidToken(req); + next(); + }); + + debug('CSRF Protection initialized'); + + if (config.rateLimit?.enabled) { + const options = config.rateLimit; + const { RateLimiterMemory, RateLimiterRedis } = require('rate-limiter-flexible'); + + if (global.redisClient) { + app.rateLimiter = new RateLimiterRedis({ + duration: options.duration, + points: options.points, + storeClient: global.redisClient, + }); + if (options.private?.provider) { + app.privateRateLimiter = new RateLimiterRedis({ + duration: options.private.duration, + points: options.private.points, + storeClient: global.redisClient, + }); + } + } else { + app.rateLimiter = new RateLimiterMemory({ + duration: config.rateLimit.duration, + points: config.rateLimit.points, + }); + if (options.private?.provider) { + app.privateRateLimiter = new RateLimiterMemory({ + duration: options.private.duration, + points: options.private.points, + }); + } + } + + debug('Ratelimit initialized', options); + } + + if (config.passport) { + const passport = require('passport'); + const ServerConnectStrategy = require('../auth/passport'); + + passport.use(new ServerConnectStrategy({ provider: 'security' })); + + app.use(passport.initialize()); + app.use(passport.session()); + app.use(passport.authenticate('server-connect')); + + debug('Passport initialized', passport.strategies); + + app.use((req, res, next) => { + debug('auth', req.isAuthenticated()); + debug('Session', req.session); + if (req.user) { + debug('User', req.user); + } + next(); + }); + + app.use('/api/secure', restrict()); + } +}; + +// restrict middleware +function restrict (options = {}) { + return async function (req, res, next) { + if (req.isAuthenticated()) { + return next(); + } + + if (req.is('json')) { + return res.status(401).json({ error: 'Unauthorized' }); + } + + if (options.redirect) { + return res.redirect(options.redirect); + } + + res.status(401).send('Unauthorized'); + }; +} \ No newline at end of file diff --git a/lib/setup/session.js b/lib/setup/session.js new file mode 100644 index 0000000..efb7ed2 --- /dev/null +++ b/lib/setup/session.js @@ -0,0 +1,94 @@ +const session = require('express-session'); //(Object.assign({ secret: config.secret }, config.session)); +const debug = require('debug')('server-connect:setup:session'); +const Parser = require('../core/parser'); +const Scope = require('../core/scope'); +const db = require('../core/db'); +const { toSystemPath } = require('../core/path'); +const config = require('./config'); +const options = config.session; + +if (!options.secret) { + options.secret = config.secret; +} + +debug('init session store %o', options.store); + +if (options.store.$type == 'redis') { // https://www.npmjs.com/package/connect-redis + const RedisStore = require('connect-redis').default; + options.store = new RedisStore(Object.assign({ + client: global.redisClient + }, options.store)); +} else if (options.store.$type == 'file') { // https://www.npmjs.com/package/session-file-store + const FileStore = require('session-file-store')(session); + options.store = new FileStore(options.store); +} else if (options.store.$type == 'database') { // https://www.npmjs.com/package/connect-session-knex + const KnexStore = require('connect-session-knex')(session); + + if (typeof options.store.knex == 'string') { + if (!global.db) global.db = {}; + + if (!global.db[options.store.knex]) { + const fs = require('fs-extra'); + const action = fs.readJSONSync(`app/modules/connections/${options.store.knex}.json`); + const knex_options = Parser.parseValue(action.options, new Scope({ $_ENV: process.env })); + + if (knex_options.connection && knex_options.connection.filename) { + knex_options.connection.filename = toSystemPath(knex_options.connection.filename); + } + + if (knex_options.connection && knex_options.connection.ssl) { + if (knex_options.connection.ssl.key) { + knex_options.connection.ssl.key = fs.readFileSync(toSystemPath(knex_options.connection.ssl.key)); + } + + if (knex_options.connection.ssl.ca) { + knex_options.connection.ssl.ca = fs.readFileSync(toSystemPath(knex_options.connection.ssl.ca)); + } + + if (knex_options.connection.ssl.cert) { + knex_options.connection.ssl.cert = fs.readFileSync(toSystemPath(knex_options.connection.ssl.cert)); + } + } + + knex_options.useNullAsDefault = true; + + knex_options.postProcessResponse = function(result) { + if (Array.isArray(result)) { + return result.map(row => { + for (column in row) { + if (row[column] && row[column].toJSON) { + row[column] = row[column].toJSON(); + } + } + return row; + }); + } else { + for (column in result) { + if (result[column] && result[column].toJSON) { + result[column] = result[column].toJSON(); + } + } + return result; + } + }; + + global.db[options.store.knex] = db(knex_options); + } + + options.store.knex = global.db[options.store.knex]; + } + + if (options.store.ttl) { + options.cookie = { ...options.cookie, maxAge: options.store.ttl * 1000 }; + } + + options.store = new KnexStore(options.store); +} else { + const MemoryStore = require('../core/memoryStore')(session); + if (options.store.ttl) { + options.store.ttl = options.store.ttl * 1000; + } + options.store = new MemoryStore(options.store); +} + +module.exports = session(options); \ No newline at end of file diff --git a/lib/setup/sockets.js b/lib/setup/sockets.js new file mode 100644 index 0000000..0f7b9fa --- /dev/null +++ b/lib/setup/sockets.js @@ -0,0 +1,195 @@ +const fs = require('fs-extra'); +const { basename, extname } = require('path'); +const debug = require('debug')('server-connect:sockets'); +const { isEmpty } = require('./util'); +const config = require('./config'); +const cookieParser = require('cookie-parser'); +const { promisify } = require('util'); + +module.exports = function (server, appSession) { + //if (isEmpty('app/sockets')) return null; + + const io = require('socket.io')(); + + if (global.redisClient) { + const { createAdapter } = require('@socket.io/redis-streams-adapter'); + io.adapter(createAdapter(global.redisClient)); + } + + // user hooks + if (fs.existsSync('extensions/server_connect/sockets')) { + const entries = fs.readdirSync('extensions/server_connect/sockets', { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && extname(entry.name) == '.js') { + const hook = require(`../../extensions/server_connect/sockets/${entry.name}`); + if (hook.handler) hook.handler(io); + debug(`Custom sockets hook ${entry.name} loaded`); + } + } + } + + // create socket connections for api endpoints + if (fs.existsSync('app/api')) { + + io.of('/api').on('connection', async (socket) => { + socket.onAny(async (event, params, cb) => { + try { + if (typeof cb == 'function' && global.redisClient && global.redisClient.isReady) { + const cached = await global.redisClient.get('ws:' + event + ':' + JSON.stringify(params)); + if (cached) return cb(JSON.parse(cached)); + } + + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/api/${event}.json`); + app.set('$_PARAM', params); + app.set('$_GET', params); // fake query params + app.socket = socket; + await app.define(action, true); + if (typeof cb == 'function') { + cb({ + status: res.statusCode, + data: res.statusCode == 200 ? app.data : null + }); + + if (global.redisClient && global.redisClient.isReady) { + let ttl = (action.settings && action.settings.options && action.settings.options.ttl) ? action.settings.options.ttl : null; + + if (ttl && res.statusCode < 400) { // only cache valid response, not error response + global.redisClient.setEx('ws:' + event + ':' + JSON.stringify(params), ttl, JSON.stringify({ + status: res.statusCode, + data: res.statusCode == 200 ? app.data : null + })); + } + } + } + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + }); + }); + } + + if (fs.existsSync('app/sockets')) { + parseSockets(); + } + + function parseSockets(namespace = '') { + const entries = fs.readdirSync('app/sockets' + namespace, { withFileTypes: true }); + + io.of(namespace || '/').on('connection', async (socket) => { + if (fs.existsSync(`app/sockets${namespace}/connect.json`)) { + try { + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/sockets${namespace}/connect.json`); + app.socket = socket; + await app.define(action, true); + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + } + + if (fs.existsSync(`app/sockets${namespace}/disconnect.json`)) { + socket.on('disconnect', async (event) => { + try { + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/sockets${namespace}/disconnect.json`); + app.socket = socket; + await app.define(action, true); + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + }); + } + + socket.onAny(async (event, params, cb) => { + try { + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/sockets${namespace}/${event}.json`); + app.set('$_PARAM', params); + app.socket = socket; + await app.define(action, true); + if (typeof cb == 'function') cb(app.data); + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + }); + }); + + for (let entry of entries) { + if (entry.isDirectory()) { + parseSockets(namespace + '/' + entry.name); + } + } + } + + io.attach(server, { + cors: config.cors + }); + + return io; +}; \ No newline at end of file diff --git a/lib/setup/upload.js b/lib/setup/upload.js new file mode 100644 index 0000000..afe939a --- /dev/null +++ b/lib/setup/upload.js @@ -0,0 +1,63 @@ +const config = require('./config'); +const debug = require('debug')('server-connect:setup:upload'); +const fs = require('fs-extra'); +const qs = require('qs'); + +module.exports = function(app) { + const fileupload = require('express-fileupload'); + const isEligibleRequest = require('express-fileupload/lib/isEligibleRequest'); + + // Make sure tmp folder exists and make it empty + fs.ensureDirSync(config.tmpFolder); + fs.emptyDirSync(config.tmpFolder); + + // Always use tmp folder + app.use(fileupload(Object.assign({}, config.fileupload, { + useTempFiles: true, + tempFileDir: config.tmpFolder, + defParamCharset: 'utf8' + }))); + + app.use((req, res, next) => { + if (!isEligibleRequest(req)) { + return next(); + } + + let encoded = qs.stringify(req.body); + if (req.files) { + for (let field in req.files) { + encoded += '&' + field + '=' + field; + } + } + + req.body = qs.parse(encoded, { + arrayLimit: 10000, + parameterLimit: 10000, + }); + + // Cleanup + res.once('close', () => { + if (req.files) { + for (let field in req.files) { + let file = req.files[field]; + + if (Array.isArray(file)) { + file.forEach((file) => { + if (file.tempFilePath && fs.existsSync(file.tempFilePath)) { + debug('delete %s', file.tempFilePath); + fs.unlink(file.tempFilePath); + } + }); + } else if (file.tempFilePath && fs.existsSync(file.tempFilePath)) { + debug('delete %s', file.tempFilePath); + fs.unlink(file.tempFilePath); + } + } + } + }); + + next(); + }); + + debug('Upload middleware configured.'); +} \ No newline at end of file diff --git a/lib/setup/util.js b/lib/setup/util.js new file mode 100644 index 0000000..c14f51c --- /dev/null +++ b/lib/setup/util.js @@ -0,0 +1,12 @@ +const fs = require('fs-extra'); + +exports.isEmpty = (path) => { + try { + let stat = fs.statSync(path); + if (!stat.isDirectory()) return true; + let items = fs.readdirSync(path); + return !items || !items.length; + } catch (e) { + return true; + } +} \ No newline at end of file diff --git a/lib/setup/webhooks.js b/lib/setup/webhooks.js new file mode 100644 index 0000000..58fab9e --- /dev/null +++ b/lib/setup/webhooks.js @@ -0,0 +1,22 @@ +const fs = require('fs-extra'); + +module.exports = function(app) { + app.all('/webhooks/:name', (req, res, next) => { + const name = req.params.name; + + if (!/^[a-zA-Z0-9-_]+$/.test(name)) { + res.status(400).json({error: `Invalid webhook name.`}); + } else if (fs.existsSync(`lib/webhooks/${name}.js`)) { + const webhook = require(`../webhooks/${name}`); + if (webhook.handler) { + webhook.handler(req, res, next); + } else { + res.status(400).json({error: `Webhook ${name} has no handler.`}); + } + } else { + const webhook = require('../core/webhook'); + const handler = webhook.createHandler(name); + handler(req, res, next); + } + }); +}; \ No newline at end of file diff --git a/lib/validator/core.js b/lib/validator/core.js new file mode 100644 index 0000000..5de27a9 --- /dev/null +++ b/lib/validator/core.js @@ -0,0 +1,243 @@ +const isValidString = (s) => typeof s == 'string' && s.length > 0; +const getLength = (s) => s && s.length || 0;; +const testRegexp = (re) => (v) => !isValidString(v) || re.test(v); +const reEmail = /^(?!\.)((?!.*\.{2})[a-zA-Z0-9\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFFu20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF\.!#$%&'*+-/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFF\u20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF\-\.\d]+)((\.([a-zA-Z\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFF\u20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF]){2,63})+)$/i; +const reUrl = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i; +const reDateTime = /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])[T ]([01][0-9]|2[0-4]):[0-5][0-9](:([0-5][0-9]|60))?Z?$/; +const reDate = /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/; +const reTime = /^([01][0-9]|2[0-4]):[0-5][0-9](:([0-5][0-9]|60))?$/; +const reMonth = /^\d{4}-(0[1-9]|1[012])$/; +const reWeek = /^\d{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$/; +const reColor = /^#[a-fA-F0-9]{6}$/; +const reNumber = /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/; +const reDigits = /^\d+$/; +const reAlphanumeric = /^\w+$/; +const reBic = /^([A-Z]{6}[A-Z2-9][A-NP-Z1-2])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/; +const reVat = /^((AT)?U[0-9]{8}|(BE)?0[0-9]{9}|(BG)?[0-9]{9,10}|(CY)?[0-9]{8}L|(CZ)?[0-9]{8,10}|(DE)?[0-9]{9}|(DK)?[0-9]{8}|(EE)?[0-9]{9}|(EL|GR)?[0-9]{9}|(ES)?[0-9A-Z][0-9]{7}[0-9A-Z]|(FI)?[0-9]{8}|(FR)?[0-9A-Z]{2}[0-9]{9}|(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3})|(HU)?[0-9]{8}|(IE)?[0-9]S[0-9]{5}L|(IT)?[0-9]{11}|(LT)?([0-9]{9}|[0-9]{12})|(LU)?[0-9]{8}|(LV)?[0-9]{11}|(MT)?[0-9]{8}|(NL)?[0-9]{9}B[0-9]{2}|(PL)?[0-9]{10}|(PT)?[0-9]{9}|(RO)?[0-9]{2,10}|(SE)?[0-9]{12}|(SI)?[0-9]{8}|(SK)?[0-9]{10})$/; +const reInteger = /^-?\d+$/; +const reIpv4 = /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/; +const reIpv6 = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/; +const reLettersonly = /^[a-z]+$/i; +const reUnicodelettersonly = /^[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+$/; +const reLetterswithbasicpunc = /^[a-z\-.,()'"\s]+$/; +const reNowhitespace = /^\S+$/; +const ibanCountryPatterns = { + "AL": "\\d{8}[\\dA-Z]{16}", + "AD": "\\d{8}[\\dA-Z]{12}", + "AT": "\\d{16}", + "AZ": "[\\dA-Z]{4}\\d{20}", + "BE": "\\d{12}", + "BH": "[A-Z]{4}[\\dA-Z]{14}", + "BA": "\\d{16}", + "BR": "\\d{23}[A-Z][\\dA-Z]", + "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}", + "CR": "\\d{17}", + "HR": "\\d{17}", + "CY": "\\d{8}[\\dA-Z]{16}", + "CZ": "\\d{20}", + "DK": "\\d{14}", + "DO": "[A-Z]{4}\\d{20}", + "EE": "\\d{16}", + "FO": "\\d{14}", + "FI": "\\d{14}", + "FR": "\\d{10}[\\dA-Z]{11}\\d{2}", + "GE": "[\\dA-Z]{2}\\d{16}", + "DE": "\\d{18}", + "GI": "[A-Z]{4}[\\dA-Z]{15}", + "GR": "\\d{7}[\\dA-Z]{16}", + "GL": "\\d{14}", + "GT": "[\\dA-Z]{4}[\\dA-Z]{20}", + "HU": "\\d{24}", + "IS": "\\d{22}", + "IE": "[\\dA-Z]{4}\\d{14}", + "IL": "\\d{19}", + "IT": "[A-Z]\\d{10}[\\dA-Z]{12}", + "KZ": "\\d{3}[\\dA-Z]{13}", + "KW": "[A-Z]{4}[\\dA-Z]{22}", + "LV": "[A-Z]{4}[\\dA-Z]{13}", + "LB": "\\d{4}[\\dA-Z]{20}", + "LI": "\\d{5}[\\dA-Z]{12}", + "LT": "\\d{16}", + "LU": "\\d{3}[\\dA-Z]{13}", + "MK": "\\d{3}[\\dA-Z]{10}\\d{2}", + "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}", + "MR": "\\d{23}", + "MU": "[A-Z]{4}\\d{19}[A-Z]{3}", + "MC": "\\d{10}[\\dA-Z]{11}\\d{2}", + "MD": "[\\dA-Z]{2}\\d{18}", + "ME": "\\d{18}", + "NL": "[A-Z]{4}\\d{10}", + "NO": "\\d{11}", + "PK": "[\\dA-Z]{4}\\d{16}", + "PS": "[\\dA-Z]{4}\\d{21}", + "PL": "\\d{24}", + "PT": "\\d{21}", + "RO": "[A-Z]{4}[\\dA-Z]{16}", + "SM": "[A-Z]\\d{10}[\\dA-Z]{12}", + "SA": "\\d{2}[\\dA-Z]{18}", + "RS": "\\d{18}", + "SK": "\\d{20}", + "SI": "\\d{15}", + "ES": "\\d{20}", + "SE": "\\d{20}", + "CH": "\\d{5}[\\dA-Z]{12}", + "TN": "\\d{20}", + "TR": "\\d{5}[\\dA-Z]{17}", + "AE": "\\d{3}\\d{16}", + "GB": "[A-Z]{4}\\d{14}", + "VG": "[\\dA-Z]{4}\\d{16}" +}; + +module.exports = { + + required: function(value) { + return value && value.length > 0; + }, + + email: testRegexp(reEmail), + url: testRegexp(reUrl), + datetime: testRegexp(reDateTime), + date: testRegexp(reDate), + time: testRegexp(reTime), + month: testRegexp(reMonth), + week: testRegexp(reWeek), + color: testRegexp(reColor), + number: testRegexp(reNumber), + digits: testRegexp(reDigits), + alphanumeric: testRegexp(reAlphanumeric), + bic: testRegexp(reBic), + vat: testRegexp(reVat), + integer: testRegexp(reInteger), + ipv4: testRegexp(reIpv4), + ipv6: testRegexp(reIpv6), + lettersonly: testRegexp(reLettersonly), + unicodelettersonly: testRegexp(reUnicodelettersonly), + letterswithbasicpunc: testRegexp(reLetterswithbasicpunc), + nowhitespace: testRegexp(reNowhitespace), + + pattern: function(value, param) { + const re = new RegExp(`^(?:${param})$`); + return !isValidString(value) || re.test(value); + }, + + creditcard: function(value) { + if (!isValidString(value)) { + return true; + } + + if (/[^0-9 \-]+/.test(value)) { + return false; + } + + value = value.replace(/\D/g, ''); + + if (value.length < 13 || value.length > 19) { + return false; + } + + let check = 0, digit = 0, even = false; + for (let i = value.length - 1; i >= 0; i--) { + digit = parseInt(value.charAt(i), 10); + if (even && (digit *= 2) > 9) { + digit -= 9; + } + check += digit; + even = !even; + } + + return (check % 10) === 0; + }, + + iban: function(value) { + if (!isValidString(value)) { + return true; + } + + const iban = value.replace(/ /g, '').toUpperCase(); + const country = iban.substr(0, 2); + const pattern = ibanCountryPatterns[country]; + + if (typeof patter !== 'undefined') { + const re = new RegExp(`^[A-Z]{2}\\d{2}${pattern}$`); + if (!re.test(iban)) { + return false; + } + } + + let leadingZeroes = true, digits = '', rest = ''; + + const lookup = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + const check = iban.substr(4) + iban.substr(0, 4); + + for (let i = 0; i < check.length; i++) { + const ch = check.charAt(i); + + if (ch !== '0') { + leadingZeroes = false; + } + + if (!leadingZeroes) { + digits += lookup.indexOf(ch); + } + } + + for (let i = 0; i < digits.length; i++) { + const ch = digits.charAt(i); + const op = rest + ch; + rest = op % 97; + } + + return rest === 1; + }, + + minlength: function(value, param) { + const length = getLength(value); + return length == 0 || length >= param; + }, + + maxlength: function(value, param) { + const length = getLength(value); + return length == 0 || length <= param; + }, + + rangelength: function(value, param) { + const length = getLength(value); + return length == 0 || (length >= param['0'] && length <= param['1']); + }, + + minitems: function(value, param) { + const length = getLength(value); + return length == 0 || (Array.isArray(value) && length >= param); + }, + + maxitems: function(value, param) { + const length = getLength(value); + return length == 0 || (Array.isArray(value) && length <= param); + }, + + rangeitems: function(value, param) { + const length = getLength(value); + return length == 0 || (Array.isArray(value) && length >= param['0'] && length <= param['1']); + }, + + min: function(value, param) { + return value != null && value != '' && Number(value) >= Number(param); + }, + + max: function(value, param) { + return value != null && value != '' && Number(value) <= Number(param); + }, + + range: function(value, param) { + return value != null && value != '' && Number(value) >= Number(param['0']) && Number(value) <= Number(param['1']); + }, + + equalTo: function(value, param) { + return this.parse(`{{ $_POST.${param.replace(/\[([^\]]+)\]/g, '.$1')} }}`) == value; + }, + + notEqualTo: function(value, param) { + return this.parse(`{{ $_POST.${param.replace(/\[([^\]]+)\]/g, '.$1')} }}`) != value; + }, + +}; \ No newline at end of file diff --git a/lib/validator/db.js b/lib/validator/db.js new file mode 100644 index 0000000..b7ef470 --- /dev/null +++ b/lib/validator/db.js @@ -0,0 +1,21 @@ +module.exports = { + + exists: async function(value, options) { + if (!value) return true; + + const db = this.getDbConnection(options.connection); + const results = await db.from(options.table).where(options.column, value).limit(1); + + return results.length > 0; + }, + + notexists: async function(value, options) { + if (!value) return true; + + const db = this.getDbConnection(options.connection); + const results = await db.from(options.table).where(options.column, value).limit(1); + + return results.length == 0; + } + +}; \ No newline at end of file diff --git a/lib/validator/index.js b/lib/validator/index.js new file mode 100644 index 0000000..e1c7b96 --- /dev/null +++ b/lib/validator/index.js @@ -0,0 +1,120 @@ +module.exports = { + + async init(app, meta) { + const errors = {}; + + if (meta['$_GET'] && Array.isArray(meta['$_GET'])) { + await this.validateFields(app, meta['$_GET'], app.scope.data['$_GET'], errors); + } + + if (meta['$_POST'] && Array.isArray(meta['$_POST'])) { + await this.validateFields(app, meta['$_POST'], app.scope.data['$_POST'], errors); + } + + if (Object.keys(errors).length) { + app.res.status(400).json(errors); + } + }, + + async validateData(app, data, noError) { + const errors = {}; + + if (Array.isArray(data)) { + for (let item of data) { + for (let rule in item.rules) { + const options = item.rules[rule]; + + rule = this.getRule(rule); + + if (!await this.validateRule(app, rule, item.value, options)) { + const t = item.fieldName ? 'form' : 'data'; + errors[t] = errors[t] || {}; + errors[t][item.fieldName || item.name] = this.errorMessage(rule, options); + } + } + } + } + + if (Object.keys(errors).length) { + if (noError) return false; + app.res.status(400).json(errors); + } + + return true; + }, + + async validateFields(app, fields, parent, errors, fieldname) { + if (parent == null) return; + + for (let field of fields) { + let value = parent[field.name]; + let curFieldname = fieldname ? `${fieldname}[${field.name}]` : field.name; + + if (field.type == 'array' && value == null) { + value = []; + } + + if (field.options && field.options.rules) { + await this.validateField(app, field, value, errors, curFieldname); + } + + if (field.type == 'object' && field.sub) { + await this.validateFields(app, field.sub, value, errors, curFieldname); + } + + if (field.type == 'array' && field.sub && field.sub[0] && field.sub[0].sub) { + if (Array.isArray(value) && value.length) { + for (let i = 0; i < value.length; i++) { + await this.validateFields(app, field.sub[0].sub, value[i], errors, `${curFieldname}[${i}]`); + } + } else { + await this.validateFields(app, field.sub[0].sub, null, errors, `${curFieldname}[0]`); + } + } + } + }, + + async validateField(app, field, value, errors, fieldname) { + for (let rule in field.options.rules) { + const options = field.options.rules[rule]; + + rule = this.getRule(rule); + + if (!await this.validateRule(app, rule, value, options)) { + const t = field.fieldName ? 'form' : 'data'; + errors[t] = errors[t] || {}; + errors[t][fieldname] = this.errorMessage(rule, options); + } + } + }, + + async validateRule(app, rule, value, options = {}) { + const module = require(`./${rule.module}`); + return module[rule.method].call(app, value, options.param); + }, + + errorMessage(rule, options = {}) { + let message = options.message; + + if (!message) { + const { validator: messages } = require('../locale/en-US'); + message = messages[rule.module][rule.method]; + } + + if (typeof options.param != 'object') { + options.param = { '0': options.param }; + } + + return message.replace(/{([^}]+)}/g, (m, i) => options.param[i]); + }, + + getRule(rule) { + const colon = rule.indexOf(':'); + + return { + module: colon > 0 ? rule.substr(0, colon) : 'core', + method: colon > 0 ? rule.substr(colon + 1) : rule + }; + } + +}; \ No newline at end of file diff --git a/lib/validator/unicode.js b/lib/validator/unicode.js new file mode 100644 index 0000000..61ed95c --- /dev/null +++ b/lib/validator/unicode.js @@ -0,0 +1,466 @@ +const unicode = { + 'L': { + bmp: 'A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC', + astral: '\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD83A[\uDC00-\uDCC4]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD80D[\uDC00-\uDC2E]|\uD87E[\uDC00-\uDE1D]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD809[\uDC80-\uDD43]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD808[\uDC00-\uDF99]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD811[\uDC00-\uDE46]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD82C[\uDC00\uDC01]|\uD873[\uDC00-\uDEA1]' + }, + 'M': { + bmp: '\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F', + astral: '\uD805[\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7\uDF1D-\uDF2B]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD82F[\uDC9D\uDC9E]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD83A[\uDCD0-\uDCD6]|\uDB40[\uDD00-\uDDEF]' + }, + 'N': { + bmp: '0-9\xB2\xB3\xB9\xBC-\xBE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19', + astral: '\uD800[\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDEE1-\uDEFB\uDF20-\uDF23\uDF41\uDF4A\uDFD1-\uDFD5]|\uD801[\uDCA0-\uDCA9]|\uD803[\uDCFA-\uDCFF\uDE60-\uDE7E]|\uD835[\uDFCE-\uDFFF]|\uD83A[\uDCC7-\uDCCF]|\uD81A[\uDE60-\uDE69\uDF50-\uDF59\uDF5B-\uDF61]|\uD806[\uDCE0-\uDCF2]|\uD804[\uDC52-\uDC6F\uDCF0-\uDCF9\uDD36-\uDD3F\uDDD0-\uDDD9\uDDE1-\uDDF4\uDEF0-\uDEF9]|\uD834[\uDF60-\uDF71]|\uD83C[\uDD00-\uDD0C]|\uD809[\uDC00-\uDC6E]|\uD802[\uDC58-\uDC5F\uDC79-\uDC7F\uDCA7-\uDCAF\uDCFB-\uDCFF\uDD16-\uDD1B\uDDBC\uDDBD\uDDC0-\uDDCF\uDDD2-\uDDFF\uDE40-\uDE47\uDE7D\uDE7E\uDE9D-\uDE9F\uDEEB-\uDEEF\uDF58-\uDF5F\uDF78-\uDF7F\uDFA9-\uDFAF]|\uD805[\uDCD0-\uDCD9\uDE50-\uDE59\uDEC0-\uDEC9\uDF30-\uDF3B]' + }, + 'P': { + bmp: '\x21-\x23\x25-\\x2A\x2C-\x2F\x3A\x3B\\x3F\x40\\x5B-\\x5D\x5F\\x7B\x7D\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65', + astral: '\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD809[\uDC70-\uDC74]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD836[\uDE87-\uDE8B]|\uD801\uDD6F|\uD82F\uDC9F|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]' + }, + 'S': { + bmp: '\\x24\\x2B\x3C-\x3E\\x5E\x60\\x7C\x7E\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20BE\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u23FA\u2400-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD1\u2BEC-\u2BEF\u2CE5-\u2CEA\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u32FE\u3300-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uFB29\uFBB2-\uFBC1\uFDFC\uFDFD\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD', + astral: '\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDD10-\uDD18\uDD80-\uDD84\uDDC0]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD10-\uDD2E\uDD30-\uDD6B\uDD70-\uDD9A\uDDE6-\uDE02\uDE10-\uDE3A\uDE40-\uDE48\uDE50\uDE51\uDF00-\uDFFF]|\uD83D[\uDC00-\uDD79\uDD7B-\uDDA3\uDDA5-\uDED0\uDEE0-\uDEEC\uDEF0-\uDEF3\uDF00-\uDF73\uDF80-\uDFD4]|\uD835[\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3]|\uD800[\uDD37-\uDD3F\uDD79-\uDD89\uDD8C\uDD90-\uDD9B\uDDA0\uDDD0-\uDDFC]|\uD82F\uDC9C|\uD805\uDF3F|\uD802[\uDC77\uDC78\uDEC8]|\uD81A[\uDF3C-\uDF3F\uDF45]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85\uDE86]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD64\uDD6A-\uDD6C\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDE8\uDE00-\uDE41\uDE45\uDF00-\uDF56]|\uD83B[\uDEF0\uDEF1]' + }, + 'Z': { + bmp: '\x20\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000' + }, + 'Ahom': { + astral: '\uD805[\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF3F]' + }, + 'Anatolian_Hieroglyphs' :{ + astral: '\uD811[\uDC00-\uDE46]' + }, + 'Arabic': { + bmp: '\u0600-\u0604\u0606-\u060B\u060D-\u061A\u061E\u0620-\u063F\u0641-\u064A\u0656-\u066F\u0671-\u06DC\u06DE-\u06FF\u0750-\u077F\u08A0-\u08B4\u08E3-\u08FF\uFB50-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFD\uFE70-\uFE74\uFE76-\uFEFC', + astral: '\uD803[\uDE60-\uDE7E]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB\uDEF0\uDEF1]' + }, + 'Armenian': { + bmp: '\u0531-\u0556\u0559-\u055F\u0561-\u0587\u058A\u058D-\u058F\uFB13-\uFB17' + }, + 'Avestan': { + astral: '\uD802[\uDF00-\uDF35\uDF39-\uDF3F]' + }, + 'Balinese': { + bmp: '\u1B00-\u1B4B\u1B50-\u1B7C' + }, + 'Bamum': { + bmp: '\uA6A0-\uA6F7', + astral: '\uD81A[\uDC00-\uDE38]' + }, + 'Bassa_Vah': { + astral: '\uD81A[\uDED0-\uDEED\uDEF0-\uDEF5]' + }, + 'Batak': { + bmp: '\u1BC0-\u1BF3\u1BFC-\u1BFF' + }, + 'Bengali': { + bmp: '\u0980-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09FB' + }, + 'Bopomofo': { + bmp: '\u02EA\u02EB\u3105-\u312D\u31A0-\u31BA' + }, + 'Brahmi': { + astral: '\uD804[\uDC00-\uDC4D\uDC52-\uDC6F\uDC7F]' + }, + 'Braille': { + bmp: '\u2800-\u28FF' + }, + 'Buginese': { + bmp: '\u1A00-\u1A1B\u1A1E\u1A1F' + }, + 'Buhid': { + bmp: '\u1740-\u1753' + }, + 'Canadian_Aboriginal': { + bmp: '\u1400-\u167F\u18B0-\u18F5' + }, + 'Carian': { + astral: '\uD800[\uDEA0-\uDED0]' + }, + 'Caucasian_Albanian': { + astral: '\uD801[\uDD30-\uDD63\uDD6F]' + }, + 'Chakma': { + astral: '\uD804[\uDD00-\uDD34\uDD36-\uDD43]' + }, + 'Cham': { + bmp: '\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA5C-\uAA5F' + }, + 'Cherokee': { + bmp: '\u13A0-\u13F5\u13F8-\u13FD\uAB70-\uABBF' + }, + 'Common': { + bmp: '\0-\x40\\x5B-\x60\\x7B-\xA9\xAB-\xB9\xBB-\xBF\xD7\xF7\u02B9-\u02DF\u02E5-\u02E9\u02EC-\u02FF\u0374\u037E\u0385\u0387\u0589\u0605\u060C\u061B\u061C\u061F\u0640\u06DD\u0964\u0965\u0E3F\u0FD5-\u0FD8\u10FB\u16EB-\u16ED\u1735\u1736\u1802\u1803\u1805\u1CD3\u1CE1\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u2000-\u200B\u200E-\u2064\u2066-\u2070\u2074-\u207E\u2080-\u208E\u20A0-\u20BE\u2100-\u2125\u2127-\u2129\u212C-\u2131\u2133-\u214D\u214F-\u215F\u2189-\u218B\u2190-\u23FA\u2400-\u2426\u2440-\u244A\u2460-\u27FF\u2900-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD1\u2BEC-\u2BEF\u2E00-\u2E42\u2FF0-\u2FFB\u3000-\u3004\u3006\u3008-\u3020\u3030-\u3037\u303C-\u303F\u309B\u309C\u30A0\u30FB\u30FC\u3190-\u319F\u31C0-\u31E3\u3220-\u325F\u327F-\u32CF\u3358-\u33FF\u4DC0-\u4DFF\uA700-\uA721\uA788-\uA78A\uA830-\uA839\uA92E\uA9CF\uAB5B\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFEFF\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFF70\uFF9E\uFF9F\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFF9-\uFFFD', + astral: '\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDD10-\uDD18\uDD80-\uDD84\uDDC0]|\uD82F[\uDCA0-\uDCA3]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDFCB\uDFCE-\uDFFF]|\uDB40[\uDC01\uDC20-\uDC7F]|\uD83D[\uDC00-\uDD79\uDD7B-\uDDA3\uDDA5-\uDED0\uDEE0-\uDEEC\uDEF0-\uDEF3\uDF00-\uDF73\uDF80-\uDFD4]|\uD800[\uDD00-\uDD02\uDD07-\uDD33\uDD37-\uDD3F\uDD90-\uDD9B\uDDD0-\uDDFC\uDEE1-\uDEFB]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD66\uDD6A-\uDD7A\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDE8\uDF00-\uDF56\uDF60-\uDF71]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD00-\uDD0C\uDD10-\uDD2E\uDD30-\uDD6B\uDD70-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE10-\uDE3A\uDE40-\uDE48\uDE50\uDE51\uDF00-\uDFFF]' + }, + 'Coptic': { + bmp: '\u03E2-\u03EF\u2C80-\u2CF3\u2CF9-\u2CFF' + }, + 'Cuneiform': { + astral: '\uD809[\uDC00-\uDC6E\uDC70-\uDC74\uDC80-\uDD43]|\uD808[\uDC00-\uDF99]' + }, + 'Cypriot': { + astral: '\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F]' + }, + 'Cyrillic': { + bmp: '\u0400-\u0484\u0487-\u052F\u1D2B\u1D78\u2DE0-\u2DFF\uA640-\uA69F\uFE2E\uFE2F' + }, + 'Deseret': { + astral: '\uD801[\uDC00-\uDC4F]' + }, + 'Devanagari': { + bmp: '\u0900-\u0950\u0953-\u0963\u0966-\u097F\uA8E0-\uA8FD' + }, + 'Duployan': { + astral: '\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9C-\uDC9F]' + }, + 'Egyptian_Hieroglyphs': { + astral: '\uD80C[\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]' + }, + 'Elbasan': { + astral: '\uD801[\uDD00-\uDD27]' + }, + 'Ethiopic': { + bmp: '\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u137C\u1380-\u1399\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E' + }, + 'Georgian': { + bmp: '\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u10FF\u2D00-\u2D25\u2D27\u2D2D' + }, + 'Glagolitic': { + bmp: '\u2C00-\u2C2E\u2C30-\u2C5E' + }, + 'Gothic': { + astral: '\uD800[\uDF30-\uDF4A]' + }, + 'Grantha': { + astral: '\uD804[\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]' + }, + 'Greek': { + bmp: '\u0370-\u0373\u0375-\u0377\u037A-\u037D\u037F\u0384\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03E1\u03F0-\u03FF\u1D26-\u1D2A\u1D5D-\u1D61\u1D66-\u1D6A\u1DBF\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FC4\u1FC6-\u1FD3\u1FD6-\u1FDB\u1FDD-\u1FEF\u1FF2-\u1FF4\u1FF6-\u1FFE\u2126\uAB65', + astral: '\uD800[\uDD40-\uDD8C\uDDA0]|\uD834[\uDE00-\uDE45]' + }, + 'Gujarati': { + bmp: '\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AF1\u0AF9' + }, + 'Gurmukhi': { + bmp: '\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75' + }, + 'Han': { + bmp: '\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DB5\u4E00-\u9FD5\uF900-\uFA6D\uFA70-\uFAD9', + astral: '\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD87E[\uDC00-\uDE1D]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD873[\uDC00-\uDEA1]' + }, + 'Hangul': { + bmp: '\u1100-\u11FF\u302E\u302F\u3131-\u318E\u3200-\u321E\u3260-\u327E\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC' + }, + 'Hanunoo': { + bmp: '\u1720-\u1734' + }, + 'Hatran': { + astral: '\uD802[\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDCFF]' + }, + 'Hebrew': { + bmp: '\u0591-\u05C7\u05D0-\u05EA\u05F0-\u05F4\uFB1D-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFB4F' + }, + 'Hiragana': { + bmp: '\u3041-\u3096\u309D-\u309F', + astral: '\uD82C\uDC01|\uD83C\uDE00' + }, + 'Imperial_Aramaic': { + astral: '\uD802[\uDC40-\uDC55\uDC57-\uDC5F]' + }, + 'Inherited': { + bmp: '\u0300-\u036F\u0485\u0486\u064B-\u0655\u0670\u0951\u0952\u1AB0-\u1ABE\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u200C\u200D\u20D0-\u20F0\u302A-\u302D\u3099\u309A\uFE00-\uFE0F\uFE20-\uFE2D', + astral: '\uD834[\uDD67-\uDD69\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD]|\uD800[\uDDFD\uDEE0]|\uDB40[\uDD00-\uDDEF]' + }, + 'Inscriptional_Pahlavi': { + astral: '\uD802[\uDF60-\uDF72\uDF78-\uDF7F]' + }, + 'Inscriptional_Parthian': { + astral: '\uD802[\uDF40-\uDF55\uDF58-\uDF5F]' + }, + 'Javanese': { + bmp: '\uA980-\uA9CD\uA9D0-\uA9D9\uA9DE\uA9DF' + }, + 'Kaithi': { + astral: '\uD804[\uDC80-\uDCC1]' + }, + 'Kannada': { + bmp: '\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2' + }, + 'Katakana': { + bmp: '\u30A1-\u30FA\u30FD-\u30FF\u31F0-\u31FF\u32D0-\u32FE\u3300-\u3357\uFF66-\uFF6F\uFF71-\uFF9D', + astral: '\uD82C\uDC00' + }, + 'Kayah_Li': { + bmp: '\uA900-\uA92D\uA92F' + }, + 'Kharoshthi': { + astral: '\uD802[\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F-\uDE47\uDE50-\uDE58]' + }, + 'Khmer': { + bmp: '\u1780-\u17DD\u17E0-\u17E9\u17F0-\u17F9\u19E0-\u19FF' + }, + 'Khojki': { + astral: '\uD804[\uDE00-\uDE11\uDE13-\uDE3D]' + }, + 'Khudawadi': { + astral: '\uD804[\uDEB0-\uDEEA\uDEF0-\uDEF9]' + }, + 'Lao': { + bmp: '\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF' + }, + 'Latin': { + bmp: 'A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A' + }, + 'Lepcha': { + bmp: '\u1C00-\u1C37\u1C3B-\u1C49\u1C4D-\u1C4F' + }, + 'Limbu': { + bmp: '\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1940\u1944-\u194F' + }, + 'Linear_A': { + astral: '\uD801[\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]' + }, + 'Linear_B': { + astral: '\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA]' + }, + 'Lisu': { + bmp: '\uA4D0-\uA4FF' + }, + 'Lycian': { + astral: '\uD800[\uDE80-\uDE9C]' + }, + 'Lydian': { + astral: '\uD802[\uDD20-\uDD39\uDD3F]' + }, + 'Mahajani': { + astral: '\uD804[\uDD50-\uDD76]' + }, + 'Malayalam': { + bmp: '\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D75\u0D79-\u0D7F' + }, + 'Mandaic': { + bmp: '\u0840-\u085B\u085E' + }, + 'Manichaean': { + astral: '\uD802[\uDEC0-\uDEE6\uDEEB-\uDEF6]' + }, + 'Meetei_Mayek': { + bmp: '\uAAE0-\uAAF6\uABC0-\uABED\uABF0-\uABF9' + }, + 'Mende_Kikakui': { + astral: '\uD83A[\uDC00-\uDCC4\uDCC7-\uDCD6]' + }, + 'Meroitic_Cursive': { + astral: '\uD802[\uDDA0-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDDFF]' + }, + 'Meroitic_Hieroglyphs': { + astral: '\uD802[\uDD80-\uDD9F]' + }, + 'Miao': { + astral: '\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]' + }, + 'Modi': { + astral: '\uD805[\uDE00-\uDE44\uDE50-\uDE59]' + }, + 'Mongolian': { + bmp: '\u1800\u1801\u1804\u1806-\u180E\u1810-\u1819\u1820-\u1877\u1880-\u18AA' + }, + 'Mro': { + astral: '\uD81A[\uDE40-\uDE5E\uDE60-\uDE69\uDE6E\uDE6F]' + }, + 'Multani': { + astral: '\uD804[\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA9]' + }, + 'Myanmar': { + bmp: '\u1000-\u109F\uA9E0-\uA9FE\uAA60-\uAA7F' + }, + 'Nabataean': { + astral: '\uD802[\uDC80-\uDC9E\uDCA7-\uDCAF]' + }, + 'New_Tai_Lue': { + bmp: '\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u19DE\u19DF' + }, + 'Nko': { + bmp: '\u07C0-\u07FA' + }, + 'Ogham': { + bmp: '\u1680-\u169C' + }, + 'Ol_Chiki': { + bmp: '\u1C50-\u1C7F' + }, + 'Old_Hungarian': { + astral: '\uD803[\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDCFF]' + }, + 'Old_Italic': { + astral: '\uD800[\uDF00-\uDF23]' + }, + 'Old_North_Arabian': { + astral: '\uD802[\uDE80-\uDE9F]' + }, + 'Old_Permic': { + astral: '\uD800[\uDF50-\uDF7A]' + }, + 'Old_Persian': { + astral: '\uD800[\uDFA0-\uDFC3\uDFC8-\uDFD5]' + }, + 'Old_South_Arabian': { + astral: '\uD802[\uDE60-\uDE7F]' + }, + 'Old_Turkic': { + astral: '\uD803[\uDC00-\uDC48]' + }, + 'Oriya': { + bmp: '\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B77' + }, + 'Osmanya': { + astral: '\uD801[\uDC80-\uDC9D\uDCA0-\uDCA9]' + }, + 'Pahawh_Hmong': { + astral: '\uD81A[\uDF00-\uDF45\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]' + }, + 'Palmyrene': { + astral: '\uD802[\uDC60-\uDC7F]' + }, + 'Pau_Cin_Hau': { + astral: '\uD806[\uDEC0-\uDEF8]' + }, + 'Phags_Pa': { + bmp: '\uA840-\uA877' + }, + 'Phoenician': { + astral: '\uD802[\uDD00-\uDD1B\uDD1F]' + }, + 'Psalter_Pahlavi': { + astral: '\uD802[\uDF80-\uDF91\uDF99-\uDF9C\uDFA9-\uDFAF]' + }, + 'Rejang': { + bmp: '\uA930-\uA953\uA95F' + }, + 'Runic': { + bmp: '\u16A0-\u16EA\u16EE-\u16F8' + }, + 'Samaritan': { + bmp: '\u0800-\u082D\u0830-\u083E' + }, + 'Saurashtra': { + bmp: '\uA880-\uA8C4\uA8CE-\uA8D9' + }, + 'Sharada': { + astral: '\uD804[\uDD80-\uDDCD\uDDD0-\uDDDF]' + }, + 'Shavian': { + astral: '\uD801[\uDC50-\uDC7F]' + }, + 'Siddham': { + astral: '\uD805[\uDD80-\uDDB5\uDDB8-\uDDDD]' + }, + 'SignWriting': { + astral: '\uD836[\uDC00-\uDE8B\uDE9B-\uDE9F\uDEA1-\uDEAF]' + }, + 'Sinhala': { + bmp: '\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4', + astral: '\uD804[\uDDE1-\uDDF4]' + }, + 'Sora_Sompeng': { + astral: '\uD804[\uDCD0-\uDCE8\uDCF0-\uDCF9]' + }, + 'Sundanese': { + bmp: '\u1B80-\u1BBF\u1CC0-\u1CC7' + }, + 'Syloti_Nagri': { + bmp: '\uA800-\uA82B' + }, + 'Syriac': { + bmp: '\u0700-\u070D\u070F-\u074A\u074D-\u074F' + }, + 'Tagalog': { + bmp: '\u1700-\u170C\u170E-\u1714' + }, + 'Tagbanwa': { + bmp: '\u1760-\u176C\u176E-\u1770\u1772\u1773' + }, + 'Tai_Le': { + bmp: '\u1950-\u196D\u1970-\u1974' + }, + 'Tai_Tham': { + bmp: '\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD' + }, + 'Tai_Viet': { + bmp: '\uAA80-\uAAC2\uAADB-\uAADF' + }, + 'Takri': { + astral: '\uD805[\uDE80-\uDEB7\uDEC0-\uDEC9]' + }, + 'Tamil': { + bmp: '\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BFA' + }, + 'Telugu': { + bmp: '\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C78-\u0C7F' + }, + 'Thaana': { + bmp: '\u0780-\u07B1' + }, + 'Thai': { + bmp: '\u0E01-\u0E3A\u0E40-\u0E5B' + }, + 'Tibetan': { + bmp: '\u0F00-\u0F47\u0F49-\u0F6C\u0F71-\u0F97\u0F99-\u0FBC\u0FBE-\u0FCC\u0FCE-\u0FD4\u0FD9\u0FDA' + }, + 'Tifinagh': { + bmp: '\u2D30-\u2D67\u2D6F\u2D70\u2D7F' + }, + 'Tirhuta': { + astral: '\uD805[\uDC80-\uDCC7\uDCD0-\uDCD9]' + }, + 'Ugaritic': { + astral: '\uD800[\uDF80-\uDF9D\uDF9F]' + }, + 'Vai': { + bmp: '\uA500-\uA62B' + }, + 'Warang_Citi': { + astral: '\uD806[\uDCA0-\uDCF2\uDCFF]' + }, + 'Yi': { + bmp: '\uA000-\uA48C\uA490-\uA4C6' + } +}; + +const isValidString = (s) => typeof s == 'string' && s.length > 0; +const buildRegexp = (re) => new RegExp(re.replace(/\\p\{([^}]+)\}/g, function(a, b) { + if (!unicode[b]) throw Error('Invalid expression'); + var i = unicode[b], c = ''; + if (i.bmp) { + c = '[' + i.bmp + ']' + (i.astral ? '|' : ''); + } + if (i.astral) { + c += i.astral; + } + return '(?:' + c + ')'; +})); + +module.exports = { + + unicodelettersonly: function(value, param) { + const arr = ['L']; + + for (let key in param) { + arr.push(key); + } + + return !isValidString(value) || (buildRegexp('^(\\p{' + arr.join('}|\\p{') + '})+$')).test(value) + }, + + unicodescript: function(value, param) { + const arr = param.scripts.slice(0); + + for (let key in param) { + if (key != 'scripts') { + arr.push(key); + } + } + + return !isValidString(value) || (buildRegexp('^(\\p{' + arr.join('}|\\p{') + '})+$')).test(value) + }, + +}; \ No newline at end of file diff --git a/lib/validator/upload.js b/lib/validator/upload.js new file mode 100644 index 0000000..0396794 --- /dev/null +++ b/lib/validator/upload.js @@ -0,0 +1,82 @@ +// IMPROVE: Improve this with the actual file field being checked, for now we check all uploaded files +function getFiles(app, value) { + const files = []; + + if (app.req.files) { + for (let field in app.req.files) { + if (Array.isArray(app.req.files[field])) { + for (let file of app.req.files[field]) { + if (!file.truncated) { + files.push(file); + } + } + } else { + let file = app.req.files[field]; + if (!file.truncated) { + files.push(file); + } + } + } + } + + return files; +} + +module.exports = { + + accept: function(value, param) { + const files = getFiles(this, value); + const allowed = param.replace(/\s/g, '').split(','); + + if (!files.length) { + return true; + } + + for (let file of files) { + if (!allowed.some(allow => { + if (allow[0] == '.') { + const re = new RegExp(`\\${allow}$`, 'i'); + if (re.test(file.name)) { + return true; + } + } else if (/(audio|video|image)\/\*/i.test(allow)) { + const re = new RegExp(`^${allow.replace('*', '.*')}$`, 'i'); + if (re.test(file.mimetype)) { + return true; + } + } else if (allow.toLowerCase() == file.mimetype.toLowerCase()) { + return true; + } + })) { + return false; + } + } + + return true; + }, + + minsize: function(value, param) { + return !getFiles(this, value).some(file => file.size < param); + }, + + maxsize: function(value, param) { + return !getFiles(this, value).some(file => file.size > param); + }, + + mintotalsize: function(value, param) { + return getFiles(this, value).reduce((size, file) => size + file.size, 0) >= param; + }, + + maxtotalsize: function(value, param) { + return getFiles(this, value).reduce((size, file) => size + file.size, 0) <= param; + }, + + minfiles: function(value, param) { + return getFiles(this, value).length >= param; + }, + + maxfiles: function(value, param) { + return getFiles(this, value).length <= param; + }, + +}; \ No newline at end of file diff --git a/lib/webhooks/stripe.js b/lib/webhooks/stripe.js new file mode 100644 index 0000000..7c8c3d0 --- /dev/null +++ b/lib/webhooks/stripe.js @@ -0,0 +1,22 @@ +const webhook = require('../core/webhook'); +const config = require('../setup/config'); +const fs = require('fs-extra') + +if (fs.existsSync('app/webhooks/stripe')) { + const stripe = require('stripe')(config.stripe.secretKey); + const endpointSecret = config.stripe.endpointSecret; + + exports.handler = webhook.createHandler('stripe', (req, res, next) => { + const sig = req.headers['stripe-signature']; + + try { + stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret); + } catch (err) { + res.status(400).send(`Webhook Error: ${err.message}`); + return false; + } + + // return the action name to execute + return req.body.type; + }); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c0aa070 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "ertfast_tiller", + "version": "2.0.0", + "private": true, + "description": "", + "main": "index.js", + "engines": { + "node": ">=16.9.0" + }, + "scripts": { + "start": "node ./index.js" + }, + "author": "Wappler", + "license": "ISC", + "dependencies": { + "archiver": "^7.0.1", + "compression": "^1.7.4", + "connect-session-knex": "^4.0.0", + "cookie-parser": "^1.4.6", + "cors": "^2.8.5", + "debug": "^4.3.2", + "dotenv": "^16.0.3", + "ejs": "^3.1.6", + "express": "^4.17.1", + "express-end": "0.0.8", + "express-fileupload": "^1.2.1", + "express-session": "^1.17.2", + "follow-redirects": "^1.14.5", + "fs-extra": "^11.2.0", + "http-proxy": "^1.18.1", + "knex": "^3.0.1", + "mime-types": "^2.1.34", + "node-schedule": "^2.0.0", + "nodemon": "^3.0.1", + "pdf-lib": "^1.17.1", + "qs": "^6.10.1", + "session-file-store": "^1.5.0", + "socket.io": "^4.7.5", + "unzipper": "^0.12.1", + "uuid": "^10.0.0" + }, + "nodemonConfig": { + "watch": [ + "app", + "lib", + "views", + "extensions", + "tmp/**/restart.txt" + ], + "ext": "ejs,js,json" + } +} diff --git a/public/PDF/ERT_Template_1.pdf b/public/PDF/ERT_Template_1.pdf new file mode 100644 index 0000000..e02a7ef Binary files /dev/null and b/public/PDF/ERT_Template_1.pdf differ diff --git a/public/PDF/PDF-Template.pdf b/public/PDF/PDF-Template.pdf new file mode 100644 index 0000000..8589a9b Binary files /dev/null and b/public/PDF/PDF-Template.pdf differ diff --git a/public/PDF/PDFDemo-2.pdf b/public/PDF/PDFDemo-2.pdf new file mode 100644 index 0000000..8589a9b Binary files /dev/null and b/public/PDF/PDFDemo-2.pdf differ diff --git a/public/PDF/PDFdemo.pdf b/public/PDF/PDFdemo.pdf new file mode 100644 index 0000000..28995a5 Binary files /dev/null and b/public/PDF/PDFdemo.pdf differ diff --git a/public/PDF/blahtest.pdf b/public/PDF/blahtest.pdf new file mode 100644 index 0000000..d3af2cd Binary files /dev/null and b/public/PDF/blahtest.pdf differ diff --git a/public/PDF/test.pdf b/public/PDF/test.pdf new file mode 100644 index 0000000..cc3bde6 --- /dev/null +++ b/public/PDF/test.pdf @@ -0,0 +1,10293 @@ +%PDF-1.6 +%öäüß +1 0 obj +<< +/AcroForm 2 0 R +/Lang +/MarkInfo << +/Marked true +>> +/Metadata 3 0 R +/Outlines 4 0 R +/PageLayout /OneColumn +/Pages 5 0 R +/StructTreeRoot 6 0 R +/Type /Catalog +>> +endobj +7 0 obj +<< +/Author (Waller, Becky) +/Company (Amazon) +/Created (D:20210823) +/CreationDate (D:20240204081438-06'00') +/Creator (Acrobat PDFMaker 23 for Word) +/LastSaved (D:20230625) +/ModDate (D:20240804235645-05'00') +/Producer (Adobe PDF Library 23.8.246) +/SourceModified (D:20240204141231) +/Title () +>> +endobj +2 0 obj +<< +/DA (/Helv 0 Tf 0 g ) +/DR << +/Encoding << +/PDFDocEncoding 8 0 R +>> +/Font << +/Helv 9 0 R +/ZaDb 10 0 R +>> +>> +/Fields [11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R] +/SigFlags 0 +>> +endobj +3 0 obj +<< +/Length 4013 +/Subtype /XML +/Type /Metadata +>> +stream + + + + + 2024-08-04T23:56:45-05:00 + 2024-02-04T08:14:38-06:00 + 2024-08-04T23:56:45-05:00 + Acrobat PDFMaker 23 for Word + uuid:02a43b6b-826f-4dc4-996d-8fd130fae006 + uuid:5c097d4d-e21a-479f-b8e6-b8ded29445af + + + 3 + + + application/pdf + + + + + + + + Waller, Becky + + + Adobe PDF Library 23.8.246 + D:20240204141231 + Amazon + D:20210823 + D:20230625 + 1 + 1.1 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream +endobj +4 0 obj +<< +/Count 15 +/First 19 0 R +/Last 20 0 R +/Type /Outlines +>> +endobj +5 0 obj +<< +/Count 6 +/Kids [21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R] +/Type /Pages +>> +endobj +6 0 obj +<< +/K 27 0 R +/Namespaces [28 0 R] +/ParentTree 29 0 R +/ParentTreeNextKey 8 +/RoleMap 30 0 R +/Type /StructTreeRoot +>> +endobj +8 0 obj +<< +/Differences [24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde 39 +/quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash +/florin /fraction /guilsinglleft /guilsinglright /minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft +/quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron /Ydieresis /Zcaron +/dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 +/brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot /.notdef /registered /macron +/degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior +/ordmasculine 188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde +/Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute +/Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply +/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute +/acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis +/igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde +/odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] +/Type /Encoding +>> +endobj +9 0 obj +<< +/BaseFont /Helvetica +/Encoding 8 0 R +/Name /Helv +/Subtype /Type1 +/Type /Font +>> +endobj +10 0 obj +<< +/BaseFont /ZapfDingbats +/Name /ZaDb +/Subtype /Type1 +/Type /Font +>> +endobj +11 0 obj +<< +/DA (/Helv 12 Tf 0 g) +/DV (0) +/FT /Tx +/Kids [31 0 R] +/T (DOT-English) +/TU (English Language Profency) +/V (0) +>> +endobj +12 0 obj +<< +/DA (/Helv 12 Tf 0 g) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [121.205 696.251 271.205 718.251] +/Subtype /Widget +/T (DSP_ShortCode) +/Type /Annot +>> +endobj +13 0 obj +<< +/AP << +/N 32 0 R +>> +/DA (/Helv 12 Tf 0 g) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [387.343 696.251 537.343 718.251] +/Subtype /Widget +/T (CurrentDate) +/Type /Annot +/V (April--- 2024) +>> +endobj +14 0 obj +<< +/DA (/Helv 12 Tf 0 g) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [120.564 665.469 270.564 687.469] +/Subtype /Widget +/T (DA_Name) +/Type /Annot +>> +endobj +15 0 obj +<< +/DA (/Helv 12 Tf 0 g) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [387.984 664.827 537.984 686.827] +/Subtype /Widget +/T (SoftID) +/Type /Annot +>> +endobj +16 0 obj +<< +/DA (/Helv 12 Tf 0 g) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [170.585 574.405 320.585 596.405] +/Subtype /Widget +/T (DriverLicenseNumber) +/Type /Annot +>> +endobj +17 0 obj +<< +/AP << +/N 33 0 R +>> +/DA (/Helv 12 Tf 0 g) +/DV (DDX6) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [487.011 573.763 572.881 595.763] +/Subtype /Widget +/T (LicenseIssueState) +/Type /Annot +/V (DDX6) +>> +endobj +18 0 obj +<< +/DA (/Helv 12 Tf 0 g) +/F 4 +/FT /Tx +/MK << +>> +/P 21 0 R +/Rect [388.626 619.295 538.626 641.295] +/Subtype /Widget +/T (StationCode) +/Type /Annot +>> +endobj +19 0 obj +<< +/Next 34 0 R +/Parent 4 0 R +/Title (Enhanced Road Test Overview) +>> +endobj +20 0 obj +<< +/A 35 0 R +/Count 6 +/First 36 0 R +/Last 37 0 R +/Parent 4 0 R +/Prev 38 0 R +/Title (Enhanced Road Test Evaluation) +>> +endobj +21 0 obj +<< +/Annots [31 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R] +/Contents [39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R] +/CropBox [0.0 0.0 612.0 792.0] +/Group 47 0 R +/MediaBox [0.0 0.0 612.0 792.0] +/Parent 5 0 R +/Resources << +/ExtGState << +/GS0 48 0 R +>> +/Font << +/TT0 49 0 R +/TT1 50 0 R +/TT2 51 0 R +/TT3 52 0 R +/TT4 53 0 R +>> +/ProcSet [/PDF /Text /ImageC] +/XObject << +/Im0 54 0 R +>> +>> +/Rotate 0 +/StructParents 2 +/Tabs /S +/Type /Page +>> +endobj +22 0 obj +<< +/Contents 55 0 R +/CropBox [0.0 0.0 612.0 792.0] +/Group 47 0 R +/MediaBox [0.0 0.0 612.0 792.0] +/Parent 5 0 R +/Resources << +/ExtGState << +/GS0 48 0 R +>> +/Font << +/TT0 49 0 R +/TT1 50 0 R +/TT2 56 0 R +/TT3 53 0 R +/TT4 57 0 R +/TT5 51 0 R +>> +/ProcSet [/PDF /Text /ImageC] +/XObject << +/Im0 54 0 R +>> +>> +/Rotate 0 +/StructParents 3 +/Tabs /S +/Type /Page +>> +endobj +23 0 obj +<< +/Contents 58 0 R +/CropBox [0.0 0.0 612.0 792.0] +/Group 47 0 R +/MediaBox [0.0 0.0 612.0 792.0] +/Parent 5 0 R +/Resources << +/ExtGState << +/GS0 48 0 R +>> +/Font << +/TT0 49 0 R +/TT1 50 0 R +/TT2 56 0 R +/TT3 53 0 R +/TT4 51 0 R +/TT5 52 0 R +/TT6 57 0 R +>> +/ProcSet [/PDF /Text /ImageC] +/XObject << +/Im0 54 0 R +>> +>> +/Rotate 0 +/StructParents 4 +/Tabs /S +/Type /Page +>> +endobj +24 0 obj +<< +/Contents 59 0 R +/CropBox [0.0 0.0 612.0 792.0] +/Group 47 0 R +/MediaBox [0.0 0.0 612.0 792.0] +/Parent 5 0 R +/Resources << +/ExtGState << +/GS0 48 0 R +>> +/Font << +/TT0 49 0 R +/TT1 50 0 R +/TT2 56 0 R +/TT3 53 0 R +/TT4 57 0 R +/TT5 51 0 R +/TT6 52 0 R +>> +/ProcSet [/PDF /Text /ImageC] +/XObject << +/Im0 54 0 R +>> +>> +/Rotate 0 +/StructParents 5 +/Tabs /S +/Type /Page +>> +endobj +25 0 obj +<< +/Contents 60 0 R +/CropBox [0.0 0.0 612.0 792.0] +/Group 47 0 R +/MediaBox [0.0 0.0 612.0 792.0] +/Parent 5 0 R +/Resources << +/ExtGState << +/GS0 48 0 R +>> +/Font << +/TT0 49 0 R +/TT1 50 0 R +/TT2 56 0 R +/TT3 53 0 R +/TT4 51 0 R +>> +/ProcSet [/PDF /Text /ImageC] +/XObject << +/Im0 54 0 R +>> +>> +/Rotate 0 +/StructParents 6 +/Tabs /S +/Type /Page +>> +endobj +26 0 obj +<< +/Contents 61 0 R +/CropBox [0.0 0.0 612.0 792.0] +/Group 47 0 R +/MediaBox [0.0 0.0 612.0 792.0] +/Parent 5 0 R +/Resources << +/ExtGState << +/GS0 48 0 R +>> +/Font << +/TT0 49 0 R +/TT1 50 0 R +/TT2 56 0 R +/TT3 51 0 R +/TT4 53 0 R +/TT5 52 0 R +>> +/ProcSet [/PDF /Text /ImageC] +/XObject << +/Im0 54 0 R +>> +>> +/Rotate 0 +/StructParents 7 +/Tabs /S +/Type /Page +>> +endobj +27 0 obj +<< +/K 62 0 R +/NS 28 0 R +/P 6 0 R +/S /Document +>> +endobj +28 0 obj +<< +/NS (http://iso.org/pdf2/ssn) +/Type /Namespace +>> +endobj +29 0 obj +<< +/Nums [2 [63 0 R 64 0 R 65 0 R 66 0 R 67 0 R 68 0 R 69 0 R 70 0 R null null +null null null 71 0 R 72 0 R null 73 0 R 74 0 R 75 0 R 76 0 R +77 0 R 78 0 R 79 0 R 80 0 R null 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R +86 0 R 87 0 R 88 0 R null 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R +95 0 R 96 0 R 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R null null null +null null 102 0 R 103 0 R null 104 0 R null null 105 0 R 106 0 R +null null null null null 107 0 R null 108 0 R null null +null null 109 0 R null null 110 0 R null null null null +111 0 R null 112 0 R null 113 0 R null null null 114 0 R 115 0 R +null 116 0 R 117 0 R null 118 0 R null null null 119 0 R 120 0 R +null 121 0 R 122 0 R null 123 0 R null null null 124 0 R 125 0 R +null 126 0 R 127 0 R null 128 0 R null null null 129 0 R 130 0 R +131 0 R 132 0 R 133 0 R 134 0 R null 135 0 R null null null 136 0 R +137 0 R null 138 0 R 139 0 R null 140 0 R null null null 141 0 R +142 0 R 143 0 R null 144 0 R 145 0 R 146 0 R null 147 0 R null null +null 148 0 R 149 0 R 150 0 R 151 0 R 152 0 R 153 0 R 154 0 R 155 0 R 155 0 R +156 0 R 157 0 R 158 0 R 159 0 R 160 0 R 161 0 R 162 0 R 163 0 R 163 0 R 164 0 R +164 0 R 164 0 R 165 0 R 165 0 R 166 0 R 167 0 R 168 0 R 169 0 R 170 0 R 171 0 R +172 0 R 173 0 R 174 0 R] + 3 [175 0 R 176 0 R 177 0 R 178 0 R 179 0 R 180 0 R 181 0 R 182 0 R 183 0 R 184 0 R +185 0 R 186 0 R 187 0 R 188 0 R 189 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R +195 0 R 196 0 R 197 0 R 198 0 R 199 0 R null null null null null +200 0 R 201 0 R 202 0 R 203 0 R 204 0 R 205 0 R 206 0 R 207 0 R 208 0 R 209 0 R +210 0 R 211 0 R 212 0 R 213 0 R 214 0 R 215 0 R 216 0 R 217 0 R 218 0 R 219 0 R +220 0 R 221 0 R 222 0 R 223 0 R 224 0 R 225 0 R 226 0 R 227 0 R 228 0 R 229 0 R +null null null null null 230 0 R 231 0 R null 232 0 R null +233 0 R 234 0 R null 235 0 R null 236 0 R 237 0 R null 238 0 R null +239 0 R 240 0 R null 241 0 R null 242 0 R 243 0 R null null null +null null 244 0 R 245 0 R null 246 0 R null 247 0 R 248 0 R null +249 0 R null 250 0 R 251 0 R null 252 0 R null 253 0 R 254 0 R null +null null null null 255 0 R 256 0 R null 257 0 R null 258 0 R +259 0 R null 260 0 R null 261 0 R 262 0 R null 263 0 R null 264 0 R +265 0 R null 266 0 R null 267 0 R 268 0 R null 269 0 R null 270 0 R +271 0 R null 272 0 R null 273 0 R 274 0 R null 275 0 R null 276 0 R +277 0 R null null null null null 278 0 R 279 0 R 280 0 R 281 0 R +282 0 R 283 0 R 284 0 R 285 0 R 286 0 R 287 0 R 288 0 R 289 0 R 290 0 R 291 0 R +292 0 R 293 0 R 294 0 R 295 0 R 296 0 R 297 0 R 298 0 R 299 0 R 300 0 R 301 0 R +302 0 R 303 0 R 304 0 R 305 0 R 306 0 R 307 0 R 308 0 R 309 0 R 310 0 R 311 0 R +312 0 R 313 0 R 314 0 R 315 0 R 316 0 R 317 0 R 318 0 R 319 0 R 320 0 R] + 4 [321 0 R 322 0 R 323 0 R 324 0 R 325 0 R 326 0 R 327 0 R 328 0 R 329 0 R 330 0 R +331 0 R 332 0 R 333 0 R null 334 0 R 335 0 R 336 0 R 337 0 R 338 0 R 339 0 R +340 0 R 341 0 R 342 0 R 343 0 R 344 0 R 345 0 R 346 0 R 347 0 R null null +null null null 348 0 R 349 0 R 350 0 R 351 0 R 352 0 R 353 0 R 354 0 R +355 0 R 356 0 R 357 0 R 358 0 R 359 0 R 360 0 R 361 0 R null null null +null null 362 0 R 363 0 R null 364 0 R null 365 0 R 366 0 R null +367 0 R null 368 0 R 369 0 R null 370 0 R null 371 0 R 372 0 R null +null null null null 373 0 R 374 0 R 375 0 R 376 0 R 377 0 R 378 0 R +379 0 R 380 0 R 381 0 R 382 0 R 383 0 R 384 0 R 385 0 R 386 0 R 387 0 R 388 0 R +389 0 R 390 0 R 391 0 R 392 0 R null null null null null 393 0 R +394 0 R 395 0 R 396 0 R 397 0 R 398 0 R 399 0 R 400 0 R 401 0 R 402 0 R 403 0 R +null null null null null 404 0 R 405 0 R 406 0 R 407 0 R 408 0 R +409 0 R 410 0 R 411 0 R 412 0 R 413 0 R 414 0 R 415 0 R 416 0 R 417 0 R 418 0 R +419 0 R 420 0 R 421 0 R 422 0 R] + 5 [423 0 R 424 0 R 425 0 R 426 0 R 427 0 R 428 0 R 429 0 R 430 0 R 431 0 R 432 0 R +433 0 R 434 0 R 435 0 R null null null null null 436 0 R 437 0 R +438 0 R 439 0 R 440 0 R 441 0 R 442 0 R 443 0 R 444 0 R 445 0 R 446 0 R 447 0 R +448 0 R 449 0 R 450 0 R 451 0 R 452 0 R 453 0 R 454 0 R 455 0 R 456 0 R 457 0 R +458 0 R 459 0 R 460 0 R 461 0 R 462 0 R null null null null null +463 0 R 464 0 R 465 0 R 466 0 R 467 0 R 468 0 R 469 0 R 470 0 R 471 0 R 472 0 R +473 0 R 474 0 R 475 0 R 476 0 R 477 0 R 478 0 R 479 0 R null null null +null null 480 0 R 481 0 R 482 0 R 483 0 R 484 0 R 485 0 R 486 0 R 487 0 R +488 0 R 489 0 R 490 0 R null 491 0 R 492 0 R 493 0 R 494 0 R 495 0 R 496 0 R +497 0 R 498 0 R 499 0 R 500 0 R null null null null null 501 0 R +502 0 R null 503 0 R null 504 0 R 505 0 R null 506 0 R null 507 0 R +508 0 R null 509 0 R null 510 0 R 511 0 R null 512 0 R null 513 0 R +514 0 R null null null null null 515 0 R 516 0 R null 517 0 R +null 518 0 R 519 0 R 520 0 R 521 0 R 522 0 R 523 0 R 524 0 R 525 0 R 526 0 R +527 0 R 527 0 R 528 0 R 529 0 R 530 0 R 531 0 R 532 0 R 533 0 R 534 0 R] + 6 [535 0 R null 536 0 R null 537 0 R 538 0 R null 539 0 R null 540 0 R +541 0 R null 542 0 R null 543 0 R 544 0 R null 545 0 R 546 0 R 547 0 R +548 0 R 549 0 R 550 0 R 551 0 R 552 0 R 553 0 R null 554 0 R 555 0 R 556 0 R +557 0 R 558 0 R 559 0 R 560 0 R 561 0 R 562 0 R 563 0 R 564 0 R 565 0 R 566 0 R +567 0 R 568 0 R 569 0 R 570 0 R 571 0 R 572 0 R 573 0 R 574 0 R 575 0 R 576 0 R +577 0 R 578 0 R 579 0 R 580 0 R 581 0 R 582 0 R 583 0 R 584 0 R 585 0 R 586 0 R +587 0 R 588 0 R 589 0 R 590 0 R 591 0 R 592 0 R 593 0 R 594 0 R 595 0 R 596 0 R +597 0 R 598 0 R 599 0 R 600 0 R 601 0 R 602 0 R 603 0 R 604 0 R 605 0 R 606 0 R +606 0 R 607 0 R 608 0 R 608 0 R 609 0 R 610 0 R] +7 [611 0 R 612 0 R 613 0 R 614 0 R 615 0 R 616 0 R 617 0 R 618 0 R 619 0 R 620 0 R +621 0 R 622 0 R 623 0 R 624 0 R 625 0 R 626 0 R 627 0 R 628 0 R 629 0 R 630 0 R +631 0 R null 632 0 R 633 0 R 634 0 R 635 0 R 636 0 R 637 0 R 638 0 R null +639 0 R 640 0 R null 641 0 R 642 0 R 643 0 R 644 0 R 645 0 R 646 0 R 647 0 R +648 0 R 649 0 R 650 0 R 651 0 R 652 0 R 653 0 R 654 0 R 655 0 R 656 0 R 657 0 R +658 0 R] +] +>> +endobj +30 0 obj +<< +/Annotation /Span +/Artifact /P +/Bibliography /BibEntry +/Chart /Figure +/Diagram /Figure +/DropCap /Figure +/Endnote /Note +/Footnote /Note +/InlineShape /Figure +/Outline /Span +/Strikeout /Span +/Subscript /Span +/Superscript /Span +/TextBox /Art +/Underline /Span +>> +endobj +31 0 obj +<< +/AP << +/N 659 0 R +>> +/F 4 +/MK << +>> +/P 21 0 R +/Parent 11 0 R +/Rect [32.2761 367.169 92.2754 381.721] +/Subtype /Widget +/Type /Annot +>> +endobj +32 0 obj +<< +/Length 104 +/BBox [0.0 0.0 150.0 22.0] +/FormType 1 +/Matrix [1.0 0.0 0.0 1.0 0.0 0.0] +/Resources << +/Font << +/Helv 9 0 R +>> +/ProcSet [/PDF /Text] +>> +/Subtype /Form +/Type /XObject +>> +stream +/Tx BMC + +q +1 1 148 20 re +W +n +BT +/Helv 12 Tf +/DeviceGray cs +0 sc +2 6.692 Td +(April--- 2024) Tj +ET +Q +EMC + + +endstream +endobj +33 0 obj +<< +/Length 82 +/BBox [0.0 0.0 85.8704 22.0] +/FormType 1 +/Matrix [1.0 0.0 0.0 1.0 0.0 0.0] +/Resources << +/Font << +/Helv 9 0 R +>> +/ProcSet [/PDF /Text] +>> +/Subtype /Form +/Type /XObject +>> +stream +/Tx BMC +q +1 1 83.8704 20 re +W +n +BT +/Helv 12 Tf +0 g +2 6.548 Td +(DDX6) Tj +ET +Q +EMC + +endstream +endobj +34 0 obj +<< +/Count 4 +/First 660 0 R +/Last 661 0 R +/Next 38 0 R +/Parent 4 0 R +/Prev 19 0 R +/Title (Evaluation Instructions) +>> +endobj +35 0 obj +<< +/D [21 0 R /XYZ 34 477 0.0] +/S /GoTo +>> +endobj +36 0 obj +<< +/A 662 0 R +/Next 663 0 R +/Parent 20 0 R +/Title (Section I: English Language Proficiency for DOT Vehicles \(Only if applicable\)) +>> +endobj +37 0 obj +<< +/A 664 0 R +/Parent 20 0 R +/Prev 665 0 R +/Title (Notes) +>> +endobj +38 0 obj +<< +/Count 1 +/First 666 0 R +/Last 666 0 R +/Next 20 0 R +/Parent 4 0 R +/Prev 34 0 R +/Title (DT Script) +>> +endobj +39 0 obj +<< +/Length 1348 +/Filter /FlateDecode +>> +stream +H‰¬WÛnÛF}çWÌ# ”+î…\²X¶S§hP§&š%²,Yj-Ò‘é¸.úñ™Ý¥(Ùµ6ÌËîröÌ™3³ãÑѦ[-¦³^½uÝt¶œ_ÁdT··ði4·ÁDka¥±`¥JÚò²™1` -tVâ‹ûËîñv£³ùôj¾QÍoçÓëU3íVm¯_OŽ!úé¬*„)A[+lºVÁf}€&×Ѩ®3P/¢4Y¦ žx© Ã_¼)A.E^@½Ž&ñi’ˆ›å4I ¼Ï ñlÛØx€Lòá°{ô[F€.) +k?" A“÷.(.Øl«‚ FÆâ'ácÚKSÑó•Ï|JË% ùPý”›£ñI¬%CAíOì´?1Ó÷Ä®Qòãç“ø(QäÂzЬ î¿ñ¨³Ž 2²“‘ôðÈUž»Í'ñ1 ´¼WÁ¢—´ãæj/ÜMOU  0t³Ï€ÈµÝ'@÷qyÀ-rã×ü˜ðßg"ÏUúŽóž½¼L*½¹'Mqþ³Ô6‰,XI[çwO㪦³©HfÓL™=è¾TSI=}¹¤;;S–Âæ`±—ʱ†¢_›Ú:õ}M]0Ä_–hMBª(ýió9ýt‘Áõ]Ä“™µUÅþ ÁdY&s ³u4z»Îà¤Åã‡{Îyôîøí ~ïðÜpFîÌÐ s&ûI58Ô·´šJTx9ŸßÖkn£ˆÁ2!v5ˆþ´W_m·*@©¡~ŸP£Þkç=•»OQeÖPÒbçÀà/Â<Ñ\&Mù„`P +oñÔý#£ƒS^BG UãÞQKø…‡§|Å5ìT.ú!÷oçôÙ¦]¸Ž^ÜîüÈKjï„kik·´Ý ¡C`¥«X'¼ò×$ÅRYÃï¾%•ÚiÆäßÌ÷ØUF˜@ïóûÌXrútý³]·WÃûÞøNuµàA:@ðvëKØ­ÛØps—σÁŠæû÷dŒÄ?**,Ò‰S˜k +CY8ï…`ÿ”Qc`ð$Bí`/…­žhm§Y4OšÅ‚´mðéóâ¼]GY!¬éaOzÐ>X˜*5اïI}I︕ômÙÍ1‚L89 +endstream +endobj +40 0 obj +<< +/Length 1147 +/Filter /FlateDecode +>> +stream +H‰”VKoÛF¾ëWÌ‘k@kîƒÜeðëÐ"FÓ‚EIªd)nb:‘ù÷™Ý%—2;0`‘œÇÎ÷Í ”e© ý”†ÿð§©diÁj-µ‡öañ¾¸KÅZ,5Ç{±ôP<Š¥¢Ë + +¸æ—;±´Pð¬ìOïÃé/làDõïö·E í½¢/¢§Í¢Ñþ»¸¹½‚ÅM»8¿Øï·«õ^½:¿½úõjxýúò¥FI ÖXéj¼‡õáßþn±}FTû$«œt:úFÚ\Oe¾W´ÞIk3iåœ4s>( ±ªJ61奷*hª‘4ÙQæ>M K ,ecØQLƒ÷³PÙ +=¢Ášmœ¹¤WSÓѤa*¼@¯.¹7—›ï½Wålï}Fâ­a9PPñ˜…üTY2oKK/HÄBIb+X&>Ö”µ äy_Ü É0â&Òl÷™øè ¸ŠÉYÓËG–Á›([‰eƒÑ'•o¢Ê¿îØŽà-GïÆ€·luÍÿÓ)4¢eCPJ,ŽïÌóy6+3¦3æ1ñê gƒlš³ƒÞgƒp–³s>u‚80«G8êM gH§ì4뼑uÆ:¼Ù³´C*Ý9Ú½Í(WRÎô”³S”KœlêÐû® N„µE8U¸ÊÒûá 7º»H†ÃáAp4ý—NX:p<étÈÀƼ¬Õ©q¯£„›¬¨œQH™¬m.ÎКVÖ …„¤SÍXuF0ÊóÂÃ'áyÛ"pÐnC)›TÊz +‘0ŒS#rFUˆ~ nÕa48ˆ¨ÚV¸‰¨Ûá<"È>Ó¯¢€H† e¸ À^¨’ž©-xÖNÂt8éX€ÞΞ«çf ¦NÍB„CAçM?ÓT'Bß«f%­ž@6íeõÐr8ƒÛ:³8Å…åS.èrÄ…¯±Ü±À4·°L«ýt#M•4ÿ@µxÝ G×5èq¤Š4›!M…œ¨gI³\Ø ü‡PàÔË¿QêLƒŠ·*«cb$Q‡Œkˆ2pþ„pU$zúø Aƒä/Š‚– M2·O‡Ž«n“Hˆltäõ‹0ÄEŽ£ú$xÀ$Õnšò'þ™ðhÛ—'wè\§i¶šBÒŒž¨ý„mj*5e¦x9Æ’¶I-.„å&”.Úßmž¶¤ÒcC ³ +ƒ 0¬þ‰dI€öØg ¯éwqÞ3³î,63µ7º¦ù3Ó4‚p¦i šSM#Hç›Æ¬_€dYbò:ÈT.Lf§Uçra†EÁWª_tvŸX¢JZÆŽó±ÕÙ¯?rÖ×ÃŽF˜ #ìåK…¶4™x¿—Š?wÜ-˜5{$•%¶át2¼‡òa¸Šë?}èhÉÞw ËG4’l¦måÀVuXW¬tÑζÞaà’KcÍ >-í´l ÇCg¨*Є}É ûLÚ}Ó»S5±;q4à`Àî£Û.ïdÏÃÿ 0 £# +endstream +endobj +41 0 obj +<< +/Length 1234 +/Filter /FlateDecode +>> +stream +H‰œWKoÜ6¾ëWÌQ4*Yõ,‚~\ÀˆÓªè¡ÎAõfc×±ìnåɯïÌ#ÉZrôâ•4œ÷7ßÐ|ŠŽ»® Ý6Ê »ÅOÝWÐ:Í +|ÌÜ“) ÿäEj*è£8QÝßÑE½þ‰ŒN”et¤,ÊÍ•iÃîcô Ñi½ö“àIs©  +3+ú¥6ßë9ÑJe Öt)¬E¸.h[rY³&Õ®™G*¡ â3×Ì~@‡*)ÓŸ7pAb ñðI%EÚBü™~ñýJr¬Qü¯JLZC|;¥3zz"x4¬-B9,F ·#‹J›êég#™ØÒé6-C´2'g=O'­0ØÉOe&mö{e]Î2ík³_u¯Íí6›ìp›óV§m¸Í·*§'iףʨ5 +›û¢~¸qˆ„[•4¤Þ‚ Ñ€i©É"Í +"¥ƒzºû™‚Àø¢@6bn'‡Æ~Øl?5y}V†ÐÑbTxËôSJ®ü3DÑ60âfÊA)2 +¡6Žéá+V7ÕX=<´Áú=£ .zÕDiqÿ€I:Ùc"ý,'ýÄÀÐ 6™­p]›ý¼k›wÿ—ëŽTp*ö"Ê~½%¦•æ6ÿh®Œ~=WˆFš++óÏÕ¬ç™++ ÎUÈgŽü•“þjV.2½:«ÕÀº@W„{ÑÚ…Ñà©(óiwUlähmå”^MEs,Èúù¬áZ]OäL joãYsUFYðävnªýc/͇kÅS$aÙa”a‚­*¦Ç;ø E¸bóT£Q`$\08ÁåzZ@¦e…v‰Ç]‡Öe;0ŽÅ«q$þi´"ï0ÎZû³he¡Q ùË×»J@º^b¦<´Ä¦‹LèZj|7C·á + †ÊÓH'P¨¤u\jpM4H÷´âê*}-…Çç–a#1nô»W¿îV%tEþîMZžî±,ؽ€?”ó×EDÒ ÄŽä³½w sž¤PLÉø,]‡–ì±9ÆéºÕ¦¼û3àiNë¿ÉÜuê’ ´uw‰™ [í. +„̾\I9/Hœt'«JKšw*³÷ M°„sÅÿcà½m1F5³ ;ó…èH[JeB|éG4RNF6–óV ˪ôòŽ/0È^i"W WDs*_˜@jí2kaYN[Òãsà\>Y!H,ý­¥î÷ÎW˜‚/}Öœ³öÙ]L— R)&[tëì§ò‘,jõl_]D.’-Íé S0â„´xÇ%Z»-XÒ‡o¸ÉÚMlôà"uõà?Î#Fð +endstream +endobj +42 0 obj +<< +/Length 1283 +/Filter /FlateDecode +>> +stream +H‰¬WYo#E~÷¯¨Gb:Ó×hµR.‰ÀF$0Ë>ÛI ‰'ëqˆ–_OÝsø €xñLOU}ut}Õm¨¿›dPÏÁ–ʺ +ð}1™¦IýÛ$SY¦I”†·WÈ”-sÑù8ý3Iª`ºÜ$™ò0m£ +˜Bò‰0ÑÄ’qFvFSE»mTw*‡¸š=&©VÍŸ“´PNä·JPºF¥^·íu}íyK¤¯úúòú&—õääÞ½;¹>¿º—Áû÷gô„g(üÖôR¥Ú |8 ¦ÊAç•ÊK¨ŸpÃZbd0³ï^Ó¢V•-l7g©M +É`)ƒ”RXcÐ~ãσ¤]§e}ìì+äL¿á_¸a›`÷Àô÷$Í ÑR1Hå^P?lÃï[ÒƒÓ¶]rƒµí}…Ølûúiì+Ç×/í‡YuÓàŒ<Î011§9»ú>BS¯\¥GKÅ÷ò„Ù@o} l$›•æ?盨@Û¬ûÀ™(ÚâîïmþpçMÜÛ“ºÖ€l¾”^<áTª¢L3yÁÍ7 =µïü%,K=j¤ŽÔŸb<†ö…K°ÚÒ´ô½ø‰F ›•œ¬!ª<ÐwaÕ’nÁô”ª“'¢Î…¯lAÛÝt ¨Áçé £x7ж ºLÂÜÇ©dâÚõAÆ5N-Ql¢Å^°h#2§¦îÑqÏysd®ÿ¡YÄOÍ Ú³(¹ƒ¨Ås r”†e + §IËÿÌA ¶m´OFš’£Œn»šðn³Ôô¹uÁàðîÃCÇÆ¾û<Ávò8$Œ!¦9ŠF—ª0°YN~†5O£ºvÒ›ƒÖ6Ü4‹JEæUlÇ›FÈ·l·ñÈÄnÿ¥¡iÖ9 Ī%Í[¶$ês‚ÙÑ Ú£WÔÚ”ª sõ4ŒUN3+ÇÔ‡ ^,ypµápíh?3ÀŽéxNàåG<#N7ÛÕÝl¾íóñ1ghh‡]é0Wêð‹u¼{Kj&Hq ¬®†â²RÅqcîéÈþø~Ä_0¥öÆð—P`#Ï +endstream +endobj +43 0 obj +<< +/Length 1083 +/Filter /FlateDecode +>> +stream +H‰´WKo7¾ëWÌ©XXšäò±4‚¶åCÚˆ‘…{Hz0$+vÒH©¬¢È¿ï ûv9ma@»Ü!çñÍ|34ÀêçÙõ›+˜]lw«»Å.ié ×à´3\¬x ÛûÙ*m /^œ½¹z5Y xùòrŽ_ÿœyÁ•WÓ¯® 7ÒƒrÜÒÑß`=»lf>ÎJÁªn@/4ƒ”dLà_|‹.-4_fï‹+Vî x`eÅ=+7P,>Ã+=(þ`¥ì?á‘I.¡ØÑÚ¤#øý +GO¬´SØÅ:¨]0A[³Š¿È& +— öI…£µíkT +Íj/åz2åµäUJù<¢Ðä8O ñÀî¾)°:‘/€I™ÄìSÄ´¯øÝž-,˜ŒøŽçÝßX©ƒÒô}EÚKµ¯|žø|Ël¢·‹ô~~=÷6„^*©ûlh1ÞëÖ™œ¦Îܼ Ê´"%R^#¦\#wé72[W\ë,ĩСNZ{î&ÆXòÑL'6ØE¦G§"Q–aÈVÇeIë¸p +ŸÁôiOTÜX°Šø`*ªi’’ñ!¨)³i¿êíN›__n–ßzÝQuÝqÚT/ßTø’P¦·C + Ñ*Öæ"P¼Ç´“ŒœÀ3ëFÞ¯iÜÕĈЧ$Ö!’'Öù®-ÿŸè ò.Ò@]~bŠffèbODU|î Ï„Ó]Ö-0õhoÐDҞϷd¯7Aã<}Œ †z;™óÞ¬ÓÌ΃u»É{Ÿžíü¾£J!›û)„ÿ!ƒ +ÏÐøPP–TÛór6`ZØ .Û—ìàNqÜÇCÞY3Öˆœ#÷¡(›hCãSG§šÐ„ÍàOŒ'Ê:^'™¤Ý“§C$QH(åPäÀ£q«Y8®ù¸t¢ùY×5?ç* ˆ&aûqVY‚Âz¼(¨5-”¢éƒhÑ‚\pšbý°Pû×DÓVÀðjBåk‡ýÎÇyÙ‚!§ÒÝO +ªÑwŒÆ`ñ‡m í»XºøÀåf†ð¹8½Á›9–ùýy(cHCv\šeæ¹}=pÛµí{Sg¦1>#Š3 +¿‡( wD1AŠ ¸>ލí#:r“Î8¶z{@ÞÑEÆÆî&é=u½óÔ˜‘èþŸ›@¢›tØoœ¤NøÕ‘lú;½FQ¬½N&•bìœT2tŠ “µãb UÔܨ ‹˜š(ˆ™ÉøG€ì I +endstream +endobj +44 0 obj +<< +/Length 956 +/Filter /FlateDecode +>> +stream +H‰”WÛjÜ0}߯˜G«`E#˲µ„@’M!¥-….}iúÒMº¥IJ”ü}gtñec­76^k4s4çxàvQµD ¶²%M‹þž6‹Úˆ!];=릖õpåXeÁ:Ë›ú{ZKÔJº,Z©–cæÐPÇÅ!Ê,¾øt‹£Ó§çííõÍ3}:¿\AkàäälE˜’MSÑ2gžî¨a­•ÚÐs>ž“Ƈ{·”Ö€mOŒ‘h‰RN¦Ïàl½8úÒíŽuÓmw”‚RÖÿÀ¢?1`«¥ja}¿ø^¬NEYÓðY”•¬ ¸¥‘ ÷¢¤Ï(ÃKñcý‚®o(E,@¬‡.F)´mÊ ÇË3DÙF~_ç ?zNlãdÍÿ¸å¬TÛCP»KåšJ¾Â!U)ö+Š:txÄÀ÷LÑÍ¿‚û¾ø#¡x|Ž‘ÍçXnƱ æ=iÜ&~˜#*nè²ÐŒgü”¿°òÃP¼äÄYÀ‘ -3fM`zm2€æM ³³·ieM _9eý¾Ó&«85IîwË4šªÉ 1ãüÝ2½8{€s&P»†žr²=x +PþÈHEüô\·›·*vú7’:¿ä ßç[a¹í¹ÝÓ,¾4BÓ½0ð`nôÎó9\BWý’¸>Ì]åß—ðÈ‘*¶ï2›+Áa£IÀZ-Œ +endstream +endobj +45 0 obj +<< +/Length 1112 +/Filter /FlateDecode +>> +stream +H‰”WÛnÛF}×WÌ#·¨ÖÜûn¤¨M?¤Hâ! vTGvÓ&v¡¨I?¿3{/ZZé‹DrxvfÎìœ@ÿóêòͬÎ~/ÎÞ\¼ê@/_žwøtÝò¶5ÐßA¾ø +ÂpÕ:h¡¿ºi.ØZq͆­ wÐìØ:@óÄ~Ã…Û$äÎ&H¬ÿ³â2L\êâRÚòÖ +rÔ]Ï\¼{Ö‡lŸIËò T†_v„x×Bá×`[ ý§•cJ¯]Ó[C-·öD¢@nÅ$GÃ-9¾c …f¤B³ekMÿÍ$ýÁ5-]l¡cCk®°Ðô3B,7§‘UB¡ÕÍMsþ„¬ hþ¥P00èc•wÿÄïÐ(¡ù‹©hërôWñyT×WGQ]ö«³Ÿvû÷›»ý(>UâS,ROqíÓÏn»ºG]f›×S#Vœ‡%$Vƒk]ŒJó ÇVcùÔ©˜Yýky¬R¢uÁ«”ŽË!`Á¥gÓznäRªm(ç¹G«¼£ Ñ’õJÀëñªÚ›Q@3—dt¨x3^Ø8óL¸Lpé”ô˜Øbc["®†ÊÄTM)÷ê‚9¹,Ç^3¥>©lG3´¯s¸‰yÐìò64NѶJ"߸ MT|7_ñœnµàV#Äcc'réPjb]‡ ÎûI¿Ú!x˜ e æ*’•×ö’6&ªÖMÓíöÖ_ØZÒ?JKˆªMFxÍÖŽîâ[:õ5æ8¼öH"°Ïôzy·MW· b»Wôõ¦yKâè$ŸØÚCó{vµ®P4v?Ìe]qáê‚1V²Ãñ±T<Õ./öG.^êSÅS!žh©zÇ}ªvv\;¢ÅRŽë|ŒkçH&Å#ÅGæ^ã €dÁ;‰ÜoX,Çž‰Dâœ;ÍeåDšQ§äIêÆ2l‚¥¾©Êp¶áaν?Ó´"¦,§’(P][¿à–Œæ°°qSh‘“2ëp6.èðB®ØkÙ² Ã ‰æ}–Œ :\w™u¸D» ÃK¥)[»èœQ[·–\«›K¼up‰¨n]Üh'•ÕSþ¨{º-‚ý¬¾šXg,”ÃSÄIr¹GÝ‘¾NEl¦¯÷yêÐW(€¨‹IýÃÝÐÜ‚·ò0M¥±îíÑ;L€_阦¦-öüòÊÎéû’FÄÍGfIÆã üþGf +þxÖŠT±óÓšè"î$é°³=§ÎWåmF˜Žm»m²¨Ñ)CÁÚ|‰òèc¼ÞÂÓ#Ã’f{Ë2b.s¸ÕÚç]5ýºP¥¨*M¤Á£|siTbñW<<FŸ*d+³ëØ‘ž~bFüX*\3øÛ§éǃàJ}ëX¬År›6Å&IÒtªI”Ð\ØÒ%Ö!ðT„oœAK›I½§Ùƒæ†î*Ž}üýÊÔ‡ ØÓ@€MÂühÒø_{¸ø×6ç%(EÌ þ`à+%é +endstream +endobj +46 0 obj +<< +/Length 1026 +/Filter /FlateDecode +>> +stream +H‰´WËn1Ýç+îrŒã·gÑ–ˆ‹lPÊ¢jR( + JϽ~Ì£ã BUÊÌøø¾Ží{\€ëG‹Wç§°8Y.ž|€gÏžœŸ¾>é<~r†€€Ï Á…°¼z1°¼‡þµ ¥áÒ5 · +–ß«êœÕ’{¨6¬6ÜAµ†S¦hàrÏÂçÏô¹eŽK¨n˜ Óž2 °OË71£W˜ÑËýáæúòê'4"¸÷sh ûÏ‹]+Þ€óø#5W°ß,®ËUɇU9ªªNõMËÒž+˺¨˜\Cu…õQÖŽŠØ3)óÐ6¼o`wËdƒ/,Y< E‰@ E±Ü +O¯ë¼üs&jtN4ä¦3÷šÌ1'ç@'èbU}DªâðîA $VêãÌ(P·ÈdúnG¶C·ZOÝŽÖ«`s|zÍñ!¸iâOX)íè5aMË.oJeik•M¥êœÍÅr- ÌØ!h{SË¥  +WßLåµaoD´íÝÇbD3Ÿ®mF|3Ž©O31 íCzÊ®Mc¹š«”@ïìz7tlývu>ZaÈ¢l´&E0³×rˆf~Ц™„²i®³hš )‚qÛ6§ïúÁ¤µÄM)o(¯pW‚j)~ÉchO›+Õ 6Aø!|{¤#©aG +gЦ38jHÙwCÐ^³ûÏ5«5õ!8chŒ-à=«[¨–á÷qhKpCØ`w€_Ôšñm‡OD~°[î¾³Ýç™?É«-;µ9 §:ËoØp úž¦moF»Do4÷–ÕŽœ‚1pRÍÎØþññ%MCnÂDj_‡è=æZ­¤GΣ)‡ç¶]…if—÷š7;Œ¨“>!ÍÊl¢ýô¤Iû#-öqñÑ“¥6EMÍ®=I\ëêìíÃþ«ì¨ÿNõY F$‰YU—·k8GqêTÙgUv*‡âû¤òN¼‡Uõ/Ms/‚ÒÅ¥+òylË"ó-œãÎmÜIÑ{¨…¹ÿƒÚ^µ§ŠL#³‘’Pí»¨Ì;ˆÚ¼¹ CüJ²û€W–v(*v %N…ž‹|#ŠKÔ½º‹E?°ß«,î@鯞úFl5·xXUÝæ_O´Eæ(-êèXÄù‡;‚ÕÿùŽ`ÍðŽ€\«™;BÄfïEÓpGH†ÍX9R•ìJBÑ9¡/§KB‘9¡/ÇLBŸBÎ}1dúŽ¢²Ðϰ›;YC3#o‰e‹u±¤›SÒÊÁz°à—È£½S9ôÚ£ÉkïÎñ]ÌvN³Í‹ÁÙÅ(:€SÇGÁpÄðºß ;rán +endstream +endobj +47 0 obj +<< +/CS /DeviceRGB +/S /Transparency +>> +endobj +48 0 obj +<< +/AIS false +/BM /Normal +/CA 1.0 +/OP false +/OPM 1 +/SA true +/SMask /None +/Type /ExtGState +/ca 1.0 +/op false +>> +endobj +49 0 obj +<< +/BaseFont /PSGFJE+AmazonEmber-Bold +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 667 0 R +/LastChar 121 +/Subtype /TrueType +/ToUnicode 668 0 R +/Type /Font +/Widths [262 0 0 0 0 0 0 0 0 0 +0 0 0 402 0 0 586 0 586 586 +0 586 0 0 0 0 0 0 0 0 +0 0 0 665 0 0 711 578 0 0 +0 318 0 0 0 0 0 796 0 0 +637 585 596 0 0 0 0 0 0 0 +0 0 0 0 0 546 0 473 612 541 +0 596 612 294 0 0 325 917 612 594 +615 0 405 445 394 600 534 798 0 536] +>> +endobj +50 0 obj +<< +/BaseFont /PSGFJE+AmazonEmber-Regular +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 669 0 R +/LastChar 124 +/Subtype /TrueType +/ToUnicode 670 0 R +/Type /Font +/Widths [262 0 0 0 0 0 0 0 312 312 +0 0 248 390 248 0 586 586 586 586 +586 586 586 586 586 0 248 0 0 0 +0 0 0 642 613 607 690 561 524 0 +718 278 0 617 524 840 714 777 600 777 +613 570 574 706 640 929 0 0 0 0 +0 0 0 0 0 527 592 461 590 529 +373 580 588 255 0 526 285 893 588 580 +592 590 373 432 383 575 510 769 0 509 +476 0 281] +>> +endobj +51 0 obj +<< +/BaseFont /USPEBU+AmazonEmberDisplay-Bold +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 671 0 R +/LastChar 146 +/Subtype /TrueType +/ToUnicode 672 0 R +/Type /Font +/Widths [262 0 0 0 0 0 744 0 351 351 +404 0 284 0 284 516 586 586 0 0 +0 0 0 0 0 0 284 0 0 0 +0 444 0 665 632 615 711 578 540 0 +0 318 0 0 543 864 734 796 619 0 +637 585 596 724 663 0 0 630 0 0 +0 0 0 500 0 546 615 473 612 541 +388 596 612 294 294 582 325 917 612 594 +615 612 405 445 394 600 535 798 507 537 +493 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 +0 0 0 0 284] +>> +endobj +52 0 obj +<< +/BaseFont /OESUVG+AmazonEmber-Italic +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 673 0 R +/LastChar 121 +/Subtype /TrueType +/ToUnicode 674 0 R +/Type /Font +/Widths [262 0 0 0 0 0 0 0 302 302 +0 0 0 395 0 0 0 0 0 0 +0 0 0 0 0 0 245 0 0 0 +0 0 0 612 0 0 699 557 0 0 +0 295 0 0 518 840 0 748 593 0 +610 541 563 0 606 0 0 0 0 0 +0 0 0 0 0 548 547 447 548 482 +378 548 579 253 0 499 283 886 579 539 +547 0 369 430 386 564 0 0 0 485] +>> +endobj +53 0 obj +<< +/BaseFont /KQRBRO+AmazonEmberDisplay-Regular +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 675 0 R +/LastChar 150 +/Subtype /TrueType +/ToUnicode 676 0 R +/Type /Font +/Widths [262 0 373 0 0 0 734 0 312 312 +0 0 248 390 248 469 586 586 586 586 +586 586 586 586 586 586 248 248 0 0 +0 429 0 642 613 607 690 561 524 692 +718 278 437 617 524 840 714 777 600 777 +613 570 574 706 640 929 0 602 0 0 +0 0 0 0 0 527 592 461 590 529 +373 580 588 255 255 526 285 893 588 580 +592 590 373 432 383 575 512 769 481 509 +476 0 0 0 0 0 0 0 0 0 +0 1000 0 0 0 0 0 0 0 0 +0 0 0 0 248 0 0 0 500] +>> +endobj +54 0 obj +<< +/Length 5612 +/BitsPerComponent 8 +/ColorSpace /DeviceRGB +/Filter /DCTDecode +/Height 79 +/Name /X +/SMask 677 0 R +/Subtype /Image +/Type /XObject +/Width 264 +>> +stream +ÿØÿîAdobedÿÛÅ + + + + + + +     + + + +     + + + +   ÿÝÿÀO"ÿÄ¢ +  +  ƒ!1"Q’ASa‘¡Ñ#$23Bq±ñ +%&'()*456789:CDEFGHIJRTUVWXYZbcdefghijrstuvwxyz‚ƒ„…†‡ˆ‰Š“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÁÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêðòóôõö÷øùú ‰!1QAq"CRab‚Ñ +#$%&'()*23456789:BDEFGHIJSTUVWXYZcdefghijrstuvwxyzƒ„…†‡ˆ‰Š‘’“”•–—˜™š¡¢£¤¥¦§¨©ª±²³´µ¶·¸¹ºÁÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêðñòóôõö÷øùúÿÚ ?¬vmšg§¹¤·Æe˜—N+( ϦSLó”Â[>jI¹F›¡$'4hqç.¾ jZZe¤&U`©Ëñ… "Y‰TÑ„ãDgf(ƃ9PgF‰™i±GS~©BåÓÚaŠ^Óše൸¥¤š©&ûµtÎíÒBˆr¬5 + -CH-ÊÍË.QâÊï¥àçQ…¶$»/-Õ:€¼P…EõÏÝ£f$0ª^B;µ)¼ÀÁü¯ö§³C y5ž¤Yb"$Ò‹9™):nCz´}šÔªµ€§MåYÚá aªrÚS–Y@PA¢‰9sÀ‡Ï 1€”ßJL€”i„)%*‚(AΆ‰»e:™muÆÕ„‘œ*2Ô¤Àš—CàbãjލƒC´¶…8³D¤N¸ELB¨ˆ5D£3k—”—–N+(<êÎî`ÏK³0‚ÛÉ +ýäC^ÿý´Ü¬Åsï¥rä§h‡€j+ C€a¬ƒ Ź›BIRObÖ¨Uè:½ØëãK,®aÔ²ÞâQ ‡L ÊÇkû,i±]–eçúÂRI¸SW®ÎˆÆL*é=tYÐ0Èk9JÙr²À(¤-Áº•Ÿ®, Î +ž”RÃÈ8¢¦Šx†m)×WŽ]Ro¨ ¸ uÙ{L>eä Øǣ÷H¶Ûq%$)'("¢-K-2ÃR%÷.ओ“UQ]T9Ys*š• rõ¤”¨ç‘õŒlžJU$øP®Ù¨ö* P¥P‘·D‚ˆ”aonZ:­B“Ö èÒ9X^›jy) %*¦¬‹öT†9 U°¬‹-@„ïP¤õƒz´¨RzÁ½ÚC}Ÿ=?:ö)ÄMë4Øv‰%pˆÕÌØÄÈç$å(ò’ÊQ$ÕG8Q H$“@RaêÚžÄF¢4sJÜ àgvâXl&ò¹$.õVý”0¨Š °²È"R‰…–HØí4äÈ,ߊr&ì™ÆBRŒÇ?5kÌ­õŠØ4H ¼ ]ãW˜Ä@(B4™ 8³ÌÄ”´ÐÛdFEd;»Ç?="ä‹ +8ÉPªU’´Ët>Y“J›• sq¤â¨ä©½œЖԩU¶7Í'w­"Š&UK(¡<"×5Ja(ÀYŠÌBži$)' ŠÂc Ô)=`Þ€m!†Éã ×ky鎒(ì$†>ë…el ›*\L8òÐ’’sÕ ¯»&]T.Å)mHIQ¦ÓŒâ…oÈ÷ÁìÙýNB±’´R d¿W )ˆS -ADh6‹RÌmÆÕ0Âhâo!;¨jûL3j<Ʋ^„í#«9 k „ܧ„ÕÌA(B‹ÿЫ’rON9ŠÝÉÆQÕ½C³²ÛéЕû8[*Âe¥ÐÊwH¼çY¤é“`ÈÓ‹4Mvf%&YGœaecµý–1ƒù_íOfŒáV;_Ùc?•þÔöho!?Ke•jþ”Ùæ9Y¾.Þë"÷“Tr³|]½ÖEï&*õ@µì%DYöÇâÁ¾Öó㧯“¬jÞ#U‘Æ=®ÖóåFéÞ,ßë÷ƒƒNuÌÃC_×·+zrG!€É :ã탯e«XÌøA•Ž×öXimµº°ÛcJ4C¶ecµý–3`ˤ‡&M渃]p'y„œ’××s@Õ(É$~”X²ö RB¦VRH½ ¥AÝï…›ùìýgãJÚÂÒh+Ôŧ6úÉ -¦·%&”í"„Ç‘‰c‡›d„£7BË K£M²œTå¦÷âÍþ±¯ÏL&±qÌ™[„’¥’ 5®«ØB™Þ,ßëüôÂ1$B´±á™4 °·+mµ¼°ÛcJ¸$?Xòe½IpfÖ.ÃëÄD“ +ÊÖtÆ’ ®NUŒ%”Þr¨ç“vmlUyÈ”ç˜ÞMcœ´W52êžq§ÚnHRHl)ST5VH-ÀÍ”% ì‘ÇêËŽe(Ô“vŠâµeÂ6maŠlƒ.â+x]i® m!‡q¿Ié -L2Îg$qñØ…c…½c݇®j%[°3Æò_µ=š!£Òª>ª\q@:ñ]¬;ÈaÙvm’|4-ÎÙÍé«U V¸ŠZkŸ@¡s¶j˶¢VU©j4×¥F:(£¼$Œ(J¹ª¥®nn×$Ú‚kLZhD,Áü¯ö§³B;_Œƒ½«ç¢`þWûSÙ¡¢b tñºöQ°ï`xJf›ÿÑ€ÅàGjȪq[ÜÆêR¬¢YÖ¸a‰š”Ѝ^@×ß“v‡DÚJHPy9êì ñ0EK4A™ˆ,ßfÙ.¶è~hâ¥óΫv‡ˆl™·BH–jÕ(Ÿ`cEŸlbU¹Å ¨]+JÔšÓeA!ÒN(ì>«TJcN,¶Õ“3Lc#s©N¼jÄ#Áü¯ö§³F›BÖ\Æ3,fZÈNB¬þѲ'”[óŠ É\íÞ' ¬Q +ÈÄk(5dŒè#•›âíî²/y0úm‹>›™ã*ÚG>òï8è¸-ETÝÍ`y1Lž±„À@F Ý ‘Æ=®ÖóåFéÞ,ßë÷ƒ öm¥(Ä¢Yye*I:¢kRN¨ø<å«&¹WexËZJ@¡n9D,J¨PFM;s$J2¨ bŽÀdŽ>:4[ —$^1UvÀCnÆ)eD@(QØYjŒ “2±ÚþËì7R¹BÞ­ +5ÕÁ„VÄܼÙkL+·–”Êt&‘œ\“Úq7¤Ü¤ç¬`q4f£°^Ô’"HV·NEE#ŸßË7§ôÝ›®æTR™ô­aÕ‹VIá\|C« »ëFµ¤™ç"ýžHtàš€cÞÉ,¢ÄD»–e,7¸R5{`êHZJPƒ’†•m‘k%–ÚrÛ›zeभÆÙ!¶H%Mš¥KP­F0)$j‹ÅE£R ŠÉ€žzµRˆ § £<À1­‹cV=T:æ)^ßV$ƒ¹Š%0Î"&Fq §0@(BvTÖ–|qÔ6¶'šJ”qM¶RMê8®©TM:èílùù+VI«BÏu/˾œfÜNB7A¸ƒx7â7ÒªŒ ’²W!(̬µª_mÄ7$ÃP fƒ¥ Im²‡ eV-ÄG[€ÒNØ ͨR™eÇÞ+%´¸µ½Eã„â”…f«ƒg7—¡{QÙs¦¡Hœ¡9M #<Ëcµ"äãfä_SYª°¦ ¨2¥ 9fŒ!4íiKe–0ÞÓm„%´•4²Ëeµ(Ý«$’uæ9ˆYkÚ+µíY»QÀR©§œw«-D„ÔPÃv„q‹=(U^P˜dêÁ\&¶Š'wtwEA‰ÓD„0ÛÊP§Z @ 3ì @ @ @ @ ÿÓª @† @† @†WfÚ¶4™Û.aÉg“LÛj¥@!Xªš¤U& êÄJx;¥šMú1„¬j*ët̸RÚÝG4Š©iÕ ±ªs„D0"SH¼¸ŒQ5¢4HiÊ;ex@ZIT+•J–)Ú`*…´N mq€BÜÖ†FÒ³í6‹ötË3M¥XŠ[.%Ä…))$V„]zγí6ƒŒ«3M¥Xá6—@(Z|Vi+B~Ít¿gL½*ⓈVËŠmE$‚ERA¥@º:û'JîÙ̵-3¦gÛl¤¾•iâA‹Ž•MâP&¦¦±{BÂ7uJ%yLI5e¤Qž„!|ô›{°)ñUŽ+H À7æpØgLœÒmS®­÷,Ä6µ]2·H Q ­(5Bóy†^® ów?ßF~1¥t¶Ø+a*ž›iã\d5¦Ý@¼ÒŠRÚ&î‰ÕX¸iƒXE4¹;oO¼ÛeÕ#M:Š ))&«BFUBÔ;á‚@ c €ÃèL"4ëA¡(k *0‚*쉖aP…ÍF&)By§âÒÑue‹eHl¨”%rÁJ ­À¨u×f[ï­N8âŠÖµ’¥)J5*$ÞI7’`‘k|©÷§ˆ•+ñ Û8hBzÚÉ©A¯µ`‹ƒ‚‹Åå@€Úsj­œ1ôfgŒ²Âœ%•³fQTãÅOº¥rãªÆ!uY 4$òÄ“¥o Q%&p^Îq&be;|RT ¦ZÌŒÍV2‚wQE$¬ÜÔ‹éš’yÉw‘\GZQBÓPA¢’Aˆ#Ͻ2êæ&S®¸J–âÉR”£”’jI†²wE‘LXÁ(÷(WÄFzB5­1î¢ö¤Þ–0 +.¤´…¬Œ¡d ¦˜ @ˆMsaaaaaoÿÙ +endstream +endobj +55 0 obj +<< +/Length 9012 +/Filter /FlateDecode +>> +stream +H‰¬WYoG~ç¯èGާ¯é™EÀ²•M1`ÇÄúAÎMR"×)S”ûã·ªº«çÐ4M+kâÌTWu_]Ó—ûÃær¾8ˆœ¾<æ‹õj).¦³Ý­øczv¶ûS\h;iœpRåJºJØªÌ c„+u®‹ +¾¿ÿtx¼]‰é/«ùrµÓ½½_m¶óÃf·?ýtöú•}éR8Uæ¦ڹܕB—¹Sb¿}ÛÑÙl4Í +!Åìr4)ò¢Pb¶ááAH% +ø?Fæ •¹-Åìft1>Ï&FŒ·ëy6)áw‘I1^eø»Ù³Îg£wÏPÀ$¨k”ÑÖà÷lâÄx +Tpi6Qb,fôzh1¾Ë&µ(UÔôüêO_éžLð_ÐkÏT»ÑQÉ*·ª¥äx’Íþó÷D*zG¤xžÈŽseϹ +…tãûš|è=éݵùêßöôöHn/éå®õw· l‚_=Ïs!Ѩ¨5¤CãÙÿÁøÐv„ÖO`æÍõÖl¶³ðsõ½FÉ'F¹:/«áè&ü`]‘ã]˜U‡åËÈH7=d©NRÂÈ:—ÈUç•é)¡-9]9®B¡þy—t¸iøQöH˜@%r¥ë±«…D¤=¡eÒ"âÛzœ¿Z†|‰òy¶;v7M5¹ÖÖ UåÊ9 .ƒZ:¹®e»€þ¼ÛŽPKÉY£,k\…M±2ËŠ`²Þ@eÁȈ}p­€'¬¨cq”3­ÀŸ+ÀY. o2§Ô“EN „n¡:jŒwáØ"s¹¢J.‡÷ün£´í!Ó9 7ºÄûÅ@.ÊYó üITl +bú ÈÙ¼n¯|ÚowY‰‚‰œËxa´/He=Y,}WÍ9¨õꟋâèëQýÀ~ï½@Rêxºé–|k©VÑ/øäbßFﱜF›ŠŒ‰ç}¨æ/å?£O G½¿p˜·™4¨iÛ£If WýÅ!lMl‚‚äÂázîw¤˜¤0¬‘Ä‘Œ Ö)XLjc™[öjNå­ ð#ð¡HISãó2¤¦ƒ´j¿c²8¯ÚCÑ·ÍêÛ `±jYÑ™wOt“íNwá +£ßB#ZÛWÇÁYóÛ[®×h˜»YÌéSé[õøëŒN¯ «ë!¸j‡®’¡ô Ká_ñ5s ãš–Í‚©üýCƒÍ‡?ü‚¸XùRwËšðÍâw «ÃAfnÔuØÒ-³¿£ü8ø48„îNúÂ#üÝÈ(L ‡}\=5í&žhÊ\Ëö8YÕÉaVo"³+²uöóqªW½ç)Ñw¬Ò )ȤÅ)·©šý7P`ÊÊàs·À´xMg†=¡r @²¾j%|¨1ï5fýz! ;ÿ~ˆs…?»»Æ­¡pËN_ºà“묢Ö+Æ—ÄÂE¯EIXµ»ë`/"‡Xv‡Îâ’2Œ¶@L -ÁÊ åYShKÝë•‚…°V 6ÊεÃD–;L@Uy Ue…õ7‰*×A•dTÉ4ªJÚ“U¿¬Â½É žÔ<Ö?Óô#«s]˜Ó"[õ" ž€6–Ðzj+´²ïä!æÜÀÜ +®|…Á»½f0¬T&Þa" ¦„·>^«ò#Ñ•E'¼ú„¢[¯Bã7›La2SQØï2ê¡û»'qUÒW){è2•²ž˜JÙaÖÕÀšLÙĽ^)µ¬˜^HPYr‚<V©ŽÅUãBޫޫJÇU“~×ß°SAV˜µ’†x<Ü…1ñð ì¿ÎüòWfC§×ý’OÿàO“´­´Âº‡è##'„™”oäs,tyÚCPøÖaXõê9!d‡{¬?2bÔßÞÚ’zƒc…:®^Êl¶$pæ‰)¤%X9]ˆ5‰¶Ô½^)rÀp ¦±ÔAbʱ»CdL-®¸–Ðm•°ZµÊá勾˜3|õ° J=m ÓßÎvËÇV,Ÿßþuž?„»µâv9I®—¦Æ’läʬó€„wßqÑt6@‰Ùå”ÙŠðÐÈÏ?‰†ôÅkGý‹ñ¿Ã¸Â)Òd½É û/2íÇK‰³Ñp®ü%ƒñÐÏŸ”SÛe¦pø çˆu{•Q­í|û.ÍÍÑ PZƒÃJÛP§æA—Æ\ªâlτϛÌÏΤÙÕs4ë«¡¤ID*‘ºÕxP g Âýkx4 +ÄÄh”`åÒæYS£Qê^¯”÷Fr”MÒúŒ>Úg¬Åçä(«c“Œ±>cN`P¶ yK +´÷äšmß ì™û°lîß®¡ßá0ŒÀ§‡²!Ì~Çú„{}Ç+qò¡Uï mT;L¼Ç:܈Ûç5-r>qÄm»hEÓ7ó‰? +Væ0h!”-%›·H|E\ÔdM'Lc"«Æ|TJ…%/Z¹Ü³Ü¯èN›ÖåAᨠ¸ñ]÷Eog€\Õú´­AëîÖ`K¨1&•ž˜JaÖ5™‰{½RìlAr¨}o‚Ê’ä¡´1-äkü¡Òu^UGÈ&H@ »£Ø‹ókª­„ŒÅpK 7½b͇Wm¡@â! HìbÁäU-=.@’{Ä€B  p|öþQéB²6*2¯t<Íùr‰¿c¢r0ü¯3:ÄYáq®YÆ–Ã&Cv¯ùÃVtRÄrÆQQQa 2 U05&[Èô­7&ö=î€â¼ìÐË»CFÛ´W”°Û¦¼&ÁDU†l{A€hÜÿ?Ò«dÉm‰þ +OEG—‚¸Û3Ž˜COôxÖCÕE.ÕÖU–Ê’l‡çë'7,$‘>Id™ 0ßË| É»‰í‰P·µŠD6jHÌo$ÊV‰J\NÊAAÕI…ͽ߼5Ãn ­[Baï½,n^ÖJc¼¦eFB&/q +ÿç3ÂÑa÷ 2–ŸãI§Wl-2¸t *y/¤HcÉÊâ³´ËgFùYãc¬Ex½Jß)ü‹Í¸ì«+ȰÏå&Ïrîx˜p„’ +>W¥Ã¤Já«Båj‹­ªÆR6K¡Êf]ei±93f›Ô[>ÒØšÙ;×왂­«SãV…ô¦–im~bÚªZL5;mד©¡±1’`˜#hk£eS..*Û-0—ó¥×&ÅXx7b¸/[™1É,¾ÙÇ#'Í•ý„‹,îøX[ú<Å%÷ý;ýŸ`¤Ä¤9Üìâû›Þšƒoël¼·É·šc¤Ÿ,¤´(øÈm’üûVGF6gÁÇVžfõnsŽC‡ºn<˜9?Œö7U„•¸©¡V’ïÕN˽ö}Ü©}¼nP•PÝÀ&miTƒªz„2°Œ±C÷Tf$–Òƒ¡–¯=6XªÜ†ywáW­‚‹- rÉ3¤¹¢õÖmÙ#Ñ“^¼c…‰s*:ð9ܽ„­Ÿp×}RÀ­¬/~ñ›¬5È|"ñçh8{¹§‡$gZ‚G¼Þ7¼^¶§±z‡4OÅQÆöb:i¿”2”Û8’?ÊÚ£Ÿ·÷4¤Ïé¼yî}š;“‰ÏÉälöÔ¤ü›AÙôCz€%²†6tsõolUMÔU~z*v^Éíö¾¾'£•W“ŃØw;zUãv•¤õú¨5DB =ßd™»)‡±úƒÛe]²«Æ8Â’„ÚÈCEŒJWQ¶Ê§–­ZßÐârRüÕ•~¥Y½gŬ`¬3Œ5 þ,ǯŒé/ÿÂ:ä„u.ƒ0Éü„ÉŽ€°é›4ý8q.Ñu„´p4œ8¯‰÷ÿ)šký€sTó Âè ‡O†FÉíŸÛ#ˆ¼:.>ÈäZ|‹%oOÏ0›ÖÑßpÏ£-“æ£}t|Æa6I‡žaÅÿJa½yûˆöþ¢È×ü±1Z£[ðò ‘š¬Bþ話ûå/ÝÉjhþ6Ä)Ö¥%Õ‹‰ôIÇçý´á3ºwñèûpÔÏå äBqOäzÇ®7…üyð¼Ûùi(ެÚÊixÇÔeÒ¥ŸŠ¾ˆWˆ’ïu{Ubߥ±xÇùOI¸º*~ß–~bÂü%nËé3Õ?ýpA÷ÓÁà]õ m˜_±5á+3eŽÜæØdqsÂXfF-ÙØœÃ((F[3{çš]a®>7àX·²ý¯Ÿ8#Ä×>ЗaDœž 5Û!ɧbT;Y ˜þËHÀ ÆÙ‘F‡kâáæ±àdÎ8ÏKô‰‘ô:ì±$àìÊV`lñ÷<ÓËð@÷T#á$TnJ„Û8iPe´ˆ7qr8|ªAä­Ñ)M/°š˜¬à¢øþTÖim)óMX˜Ê ´Ï«O>“¸÷PHtÌÅÑ!œÌÐAÉE Áyxä-r¤CáóÞw`N›¸GšFze$ªJúxÁç›ñ‘µ‚åõÙÄúD‡þ@É:1±U%&e³ïé¼Y'&-6gÆW¢ Sy£wœ·j†)è?õi©ÂJlœ]JK¼ÞòêʤA{¿ÛüHðÛÎÈiŒ·Oàk|M¶E °ÙÔ XÍJ +âãò(0›×<›O7=Ñað^\î\xçµ]z$ ^pIÓ×ýý&Åθ3šëq„ŸE©Áfc‘)ýA6M~d7z ªøÈÇä|èbÉW¬)fíÌnÞ¦-hµåêW‹´°Zë¦(˜‡8Û -4NøYò›iƒ¦pHZô†z¬~ÚœC¦öÙÉuMu6ñ]‚ºK}/Q=êOãÝ\ý +ȃFþtzמþ4¤I—ãÇù!逷TxÛË:¯X¸_ÒþMá=¼•{ä=wíh!õŒ=µA’<‹õvSЫµ·=ò_ð²õa‹5%ÔÄÄö˜H' ò¾!±ÃnÖGŸC1oÔ˯{³6í6ú ¡wõj°Ëu—¬Ÿë.øb#Â@¥Óv7¬LúÑ|Ìy榪ÒÔ¥x$õåÒÊïÈI«Iþ}•æ?šZ29ÚäþD7Wÿ€±ñ»A]òµ$IpïÌ lPàò‹P7<Ù ›~ÂIVß?°jeÃz¢#r,À\á‹#*–"Ñ=/ß–DÍH1=¥ œÒvø³à¶Ã´ãk³Â+ÎFŒ +`”­þ¼UŒ—“â™×ŠÑûÍ[ؘ*7Á, mr9nxýYÜ@BçqÓ:|X„›&ƒIýÜÈŽ%¸±iþKÆ +NDÓ†Eàlþ(-Ö6lÇ0JýÀN³âYƒäÁ}Y#”î±£·8:ÀLbh¯÷Žö{’0ôµl:[Üæƒú§GXƒ¡î¦²¥Ÿ®=“Á|éI¼Â+9# o¯0+xó]’ÁdaËsǹè xaˆÉïu8VíýŠNú‚`:@¿L nÁ5vƒñëÀ[AlA´²ææêo4ؽÿ»(«™Ã• 0õ×xÍÌß3œ»¦+±2Æõøé÷_ö»—ˆ&òO‡WÇMžØå8¡‰æà0¦¹îJ|Þ—S ‹qKÚˆýò7‹eÃCŠÉ›Â +)n²¶]˜xZ௠E@v”[_áóác)Wåg@8ÜÝÿÝH#ò ¡ÂÈïãIÏÈ=’J˜[ÀËÕôмƒàÙ0-}ƸðÜ—Òß ŽmÃì\3C T +DË~´¤??5ˆN5¥ßˆËIñ雞Ø6÷ÜfôÝwøØ®ïü½è{åN(dsJáåê×nd‹Â»>£ð’ü +ïù8Xáë«!ÙA’þT¬/™hÜÇÉþ£Fî2qÐ@C¦¨6!J.ö'1û½Þù{BÀ£ÈÊ ŠêˆŠ€¾xð D:P(FJ;Ý´Ùt¶YçkDEë|{^§å¿ z5ŒÎ{`ÍB–9§xóBù>­ÔÅåD^¯;“å°Ÿé’3™³Ûþ+VßE¥DÓµ¿-æÌv/ïÄhÿÌ+Y¼­2õU7HÊuG-XTp_?¸ñ ¥Où©²'™jê“—“â‚WV¥fÒÖµ­q«4¾ÊÜ™:˜hà)ã ¬`‰dÎ¬Û `®ÁK´EÀ¼Ï˜$? ØÉôY8Àæø!R0K¿ú…~EÁøúr´9lécþ%ø ™Ík+xÇ}¡¶sw~é„:QnjS›Êó¦ö•¿;í.‡x±G¤âÛ7jïÓÑK”ÿéõ°Óås¢!å ;mxïéU“”>ÀÃG)ÓpAré$]]í¹l‰éè‹=ïá˜Î×ÑM”oÅQ~ÁqHßÄe߈Ęñtµ› ó­ïâ× áPOs•›|“å&À1Xƒ›xÐâ¦üÔÀ·8Õä&#.'ÅåX˜c‰kŒêÊÆ°Y—Œ³vcC@Ýê¬å{qÖª‡€j;ÌI=BÚrã°UŽ|ŽœE#nwÝnäƒ\å¬qÎYq,‘ÜWR–!öLY‚ýHÁÔLHï‰Y.#p>!›«Øeˆ&ÃûèOß3Vp}·ë‚(}Ø_Ξ[FËb¿ëDµœ3êk6uâãØ­‡2Ç5~Q×Yã$,IՑܱUR³ +9O Mçz[<Éñ»§§©—G3˜G´²µ¼úþ$–Ìå?ÐwÅ`Ö“_ÅrÞZV`ÍMúæA‹¾óS…¾eªIßF\JJª¼aÖ”uQ«(uÆTº~€’n§núü4sÕ‚¹—AÖ‰{ð¥ºö ÞæxgÐöP­söèœâœÀ{¾ãL){¨±Å%¸ÿ*ä¼h ûGüÕE}ä³x¦Ï%¿ÇQ}î2È[KxžëF¢Ü)¿é4ÎX«à”õœÅ¸®÷°§¸@ï—X  8G÷ ”É‘årDSY‚9w›¾~Ì6WôÙãK7y]zi*h;»bGä|4~þö6\;ßöŽ(“£PöG¼Lxûj¶#©ÿž~ áz¤1ÐKù‚‡…:ô½¤~çžЯõ2@–X>FÞÉÅÃG‘©{¢·h ÌXgÔd‘¬áw#òA n+F‚áRêÎNU ¤©‘`¸³çãrRT C0²cºjvЬHÎêwÇ¥U8¥jEÚVĤêb±±®]‡MǼ4ù],'7Å\ÝÕÈA.*gí­^FçO¶­›-~×ãoÕ‹5€½|ìþWð¨ƒÿ/È–Õˆq䘃Z{áô•i'ÓI°Íueƒ9âõÑv..—¼»¥f+dçü¬hlUõ3â4a#!¿2܈uyû>;‡¶/ÚèêEÍ:X×÷,ï`eÐp°ÆT%Xžj9X+.'ÕE¤¢H— ¹!]37f•£Éyצ%$n6¯ü½¸×¶pl r'18A:m•j ®šKÞÇÉKÅ%ù3LjÃg´xÚñDÑDAyökÌ*l|ý€ü|Eæp~䡨R”éóVœÃW!ù‡~æ#åõêêyyü»¬IþA [ì4ÄÈèòj4¼[’a±×ÜÀø$>EÝNbT&þÐ,÷Ç Ul(ý¾x°/]TéUºhòöh§5é‚-ºÈOUúã©&]q9).H°!Uꛬa]Û7K“ó1~ÄÊn§þ~ÙøÎ‰£Û@u“ŠÀ âè3Ä!©ŸA5ÜüfCƒÛ/h#Šõê¦Grhððˆ4Þ!–†ÈVܪQ{BN¤çöcˆŠ|÷¹ŒœÎ¡ ×kü’¢ ç‰<ÑÏ*ÔßèÌ/_Kb}× ½Sþ]d0½Z—‰K©@­ÑóúI%® ô¼ŽxêZøê©$f}6‘®cƒ4?•®#š-¾•x²7÷eK7ò.ÜÅ#ÌÀÍ0Wx†Íyªì‚Ê?z€)سíJ¨Êßåkö½¸òU­wž·¸Îdyã¨Î2˜Œ-&ËO ìÜ4'˜ÌˆËIq9ŒN,?¨ëæG­¢´“ý« šx à Ðnk¿•Èd‚÷‹¨¿ÿ²ß½Dl0hÈèJ»éJ»Ô…à\¯²Õ_Éi9•°ÜŸŸÞ¿‡)ÞßB8$”\„v Š©a€6XpàIŠDGiá¤Ôÿ d¨y'¡ß>— ƒ*í#Ô_Üé?„¥°­á +O~î¤å¹úäÌ©¸ÕU=:þ–g›C}‰£P-±Ó»ë)µ|úúø¨Ÿ<΂ÃU‡Ãåàkhl'A WT0àÁ%@äшn‰Üä@.89‚¢[`&›3ã›: +X#™mçt¼¤pĵñúÐÆP寺™~'“'älVÇÊM +ðžß\»TÝîP" SI+Ž`¦é*xÞ$v¤ƒ²ÊS9Ä‹=âE¦ÿùðâ>ý·lE‡XWwº¢|Nö[G9è®ø‡ïà=½j’òÏVžçÄßÀÉ¥Kd{{hÉ[Ó¢ýÇçy@£š¨>¢8Ê/8aIâ› üFÚqÁ¤©¡2õ˜€Ä¶ií8 \5gƒ„GM“$2Ù‰›2{$á¼`åÇdÙü`:]u:C‡¦Õ†NçÎ…ì!çO êôL›éÂõtÁ/£;¢n¯X½BS7.ÒùÏgvh©]Ì™=ßaã% f¯‰1¦)íŠ,Žô‚Ç”jøAO" Îö +vÑ´{ÚÝm¡1ò‰£Í¢ÛsUJ"ð^G<¡^ü£-å `¬Žê± +³Î§0ƒ;Y †%ä1Ãæ'2Àd¢å˜”ÏàðV_kÊÂÖð"uèÒØSuàþ‡ÐËòLñT öTö¥c4Áƒ«÷—(‘õOmÅk7â>éúÿŠ×¹Ÿ]ØãA^U&¾×I÷§Ï]¦KÛ±ÓŒ]m´h®Gãe  ½Ä\k±q¤À0Ž@”kžVÒ™WËWÒÅ9rº?îH<6›Éö®K/îC¨hGÄq˜‘Ì1&Ë%•Ê€¾ÏDWvâ³ïLd÷3 ++óRмfа¨Ãp÷æš×cƒØXÖܲÇ]€¢$`0üË £ÌΧXD”‹C ð–ƒÍõÉ`se±W~ˆR§!#y#œ æÖ”“¡5­Ý«êhnÙSct|ÿ`1Μ¤ +endstream +endobj +56 0 obj +<< +/BaseFont /ATTTHI+AmazonEmberCdRC-Light +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 678 0 R +/LastChar 32 +/Subtype /TrueType +/ToUnicode 679 0 R +/Type /Font +/Widths [211] +>> +endobj +57 0 obj +<< +/BaseFont /YGQCFM+ArialMT +/Encoding /WinAnsiEncoding +/FirstChar 32 +/FontDescriptor 680 0 R +/LastChar 32 +/Subtype /TrueType +/ToUnicode 681 0 R +/Type /Font +/Widths [278] +>> +endobj +58 0 obj +<< +/Length 8459 +/Filter /FlateDecode +>> +stream +H‰¬W[oã6~÷¯à£µ¨‘EiQH&ÓÝY ÛiÇ@Ò>xbÅö6–3¶“tŠýñ{nÔ-’›NwˆEžÃsãw.¼¸<œ¶wËÛ“úúë‹ËÓiy»©Vêæb±P¿\\]íS7Ö&^g^ym£}¡\‘'i–)ŸÛĦ0~xüxúüP©‹VËUuP Z½_®·õò´Ý×ê›o®®ß¨Ù§™Í•7y’ÊzŸø\Ù<ñFªÙOªž]-f‹Eª´ZÜÍâ4IS£·J>ž•6*…ÿð“é,p:q¹Zìf7ó·Qœ©y½YFq¿·‘Vó*ŠáïJE¿,þ5{»˜ýðd”%ÊèZðc{5߃(b£æjA °Ãªù1ŠK5?)0*Uh/ØùÄÜ÷Dz$x£ö…f·6]$ÎtŒœÇÑâ?M¤Kï‰T_&²\=®A!ýû½¦r$9\Û'^hõ™Â¦.iqìüÝß +¶W>ó¥hM´Ò¡ €ùâÿüèÂÚ0cwÙ›mM0“ŸõŸuJ¿pÊ—I^ŒßîDœOÔ… YôŽ|šeÚ¢f±CçæUFdºL4ž*“"aÝ€;¾@¡)þyw¤ ‡4ÜÔæÀˆÄزCì[¡QB_è™vˆø®o¿ƒZ†ç&ÊçÕþtÚïÚ +š%Ö:¯L‘ï5„ +hî=|$¶ÔÝúí~:W@ ,yÈ‹`)‚Å…œ• VQšL6[¨,ø‘㯢 ßÙlÿC”=«›¹‹Œ=£Jâf(ˆ¡¦ä…Wà–Rr”¼[B(ÁÙß¡›{! +é¥1~<+°sÁÄ78# {»&8 «Fê&¾!n!¬÷ð%Îúa”lãÕPá2áù/35çÓÄ9 ý²”Šyù1*ÑèÃ#‘Šáóð9Ò9Á¯u¾ßð2®É,Ó 6©É¦³½P­´N`DgÀQm³Ö#ªÏo§ësw¼ÍŠ"ñŽú—£ffrÀ ζæÏM¶A,@šV±Á*!ÝîÓìâRµ>Έ˜z_–T·)/á_^ª®1išjgÕínvñn—ªë=´?rŒ[ñ{ôé»7ï®A‚ر¸†œdÓGVÀ-øTcWmÛXˆQ+H·Î”‡Låt£KyÑëÜq›v84ÏÒጞ¦Ò> ÖÍ +BšÊ†ª5ÈvGEn'gïá°(!ðã:\AÈr—­ÐÕX®Y€D×þع¡'&…ÖÝq¥7G¿2V âíX¬Œ6ƒ`à"˜:S…9EÑ¢@=‰¹ýRXŽÑ¿œ¦·Žƒ½Q§—G òÃ,3ˆ1 Ѽœ¼Î- ùLÌòÄêî\V”4;ŽeÃQñ²%;ïÎ>Oe“1zLibL%‰ÌQZ3.¶©fCüF25/2üîgjçlÎ6M$nÝh +¢@-¨º„J ø@Yñp€‚a—¡ºMK'=ß§¨àên¸‘À`npzɨi²Œå!24µ,ï›Øè§9D«–™þ*“Î1²$jKTl‘ØgWQÙœfX³Âæé²£ŠŽ©ÆÙ]zˆÁ^Ñm£Í‚íB $¾"ÆQ,Ä O ‰ÊG§P<¥—‚[iZÌå=µãÄ wœ:‚ÖüZs˜ÚŠi´úÞÓå ¦£õ=–·¢SÕïBÝ'€Á¤Ô6á{ˆ:æ h¹DN\JÇ¡SSKZó)ÚK8ùé‘Ó&¬ké6غ²v»³&-]×QÓ‹Pr}*Ñ Îþ<ó<kÖâçŸÈ4ƒ“Ž-¢|‡>«ÔSDlµ‰8qK6K2‘¦® +Ò«D G°–·l• ™(# JšUnþ ’fxQYj±Q´ÖÿQ*ƒTèAÒ´Ö½\bj'—ôÕc‡%›äp'›ô ØêfË`|+²©|'ÁãÔ‘|*Ïå“3É™tÒi/Ÿ4^ÑxÁóˆ»ÈÍüñéTQc+ئé¼^ãó‘ „°5à­ä™Þ¶ìj³äºÿ;nØ–ê-BqiÓóEó‰êk)F´0½QsŠhš?â“U7âk8È 9]/ų{NÄ#wÑTÕ+Î u ÍÄáøÉý-èÆmJÛsø£L^"›ô¢(ªÔC™¾g[NüjŽ?ËdžZW^³J–9Æò78C.ÙŽV$¶%¥éôº g¾z¬ƒçæ5ÜðC¿ ÛÉ6ÜëÀMc•Ž*ýU…×ç_b8'È€p”…Š@þº^ªõ @²Ø|ª™2qª™Ž•ô—£“ÍtB/e ò0Ѧƒä'¹ãÔ‰ñY›v†+³R­ÑqR ¬C‡p Ì´%äoC1W¸äj!Œ9_q´i5‚6x)füR|EWfE€.nó7ób™€ €°ÌÅõ€d[Ê“Ñ|¢´ÀyQÎŽƒú@ïœ*¢©zOtÒ _ÕÜ¼Þ 0x3ÇïGΔ3ÌÄ “=ˆ©“=hâ°€PO÷ )Ýl_êT›§Ž]z3ÀÂ}[¾ï±Ž“‚aïŠÉ{-çü;–{Oš%-VrDÆþ„óÍãq‰¸—ù SÜC)â9ŒKML·z\7ˆ·î•%Ç.\cͨ8D›*8£åªùàd¹×Éö¤X&Ú{LõªÏ#²/LœrfBkK,¾ŠàIg'DwÈ#²ÏSÇ`]´ÅS& ¦v àoGWd‰LØßãX¯e|§jCM¥Ú6B¨Ô5“2ì¨ê(O‘Àº”ùä#4X†³Å%´ršRnýüHi¿P¤„ýiK.áÇÑ CËp0¸²‚—‹õø¿”])!`ƒvˆûI‚ÂFúQž°E'Å”àz¥‚„ ùŽßbq|Éd<–µ¦·æª­@oìÙñ S ’¼úªO,‹†G+fÚŽ'É*èmKL÷rrè +¯Ò²âḹ”008Ç2Xd„¾—%ý‚ÕGu |xÉÇ*â™ö¸ãq_Öõ ¯$'ühíB%í(G¬©”; 2ºcæA†ZÖAªm²ý$—f|ÚQ8pçCaÏÁwY‡Ù{–”Äm.ðþÝŸ„$XŽ÷Vò½u^1\- ‚¬³|Çœy”L Ñ\1PàeD³œÄH‰]rHÔãYò|nùΈ•5e•Òø1™`7ó3D÷„Ñ!È z 1ÚC–t1íÓ4–ª±*µ_!3×MŽyàçÔ>äs“?É‹6Iƒ5O’AÞ=ÎB÷?Ö«¦·‰Þó+ú4pãF7›ýµ·x'À `wàÌì!¹h,ÛÒÆ–YžÀÿ~ë“dw“’¼ÎŲDV±X|õê•]#Í•æc˜±ÆƒGiç¦BWc á6„ ¸òÒ2¡ôLF\½ðÕêÐò •+GFüJœÛЃ +».•£w³W¢~ð@Ÿð,­c`–ú5KA©È +¿=ä”!M úî{[øµs[c“%·hGߨ³X±›l±‘f¤ÁN³à”Ægx>š‚žöøD-ÝA'xúÊAe_Î8€s|]øáK>M…KȦÞÖ¡¾ æ3|;ÉPvëï›-vÒªäæ™\gœžlÉlœQÙ#’_±Øqë\샌âÍÂLÞ YÝ efbtCäfœËgݠѵ¼c6¾˜^CÏÕ¨2ù.ÁJ8Îüt'¹_ˆcÅÀø/`ªm»&( xÖ罤â%×bOޝs*^(Mò´]º!ö–ož-@¼µð{ø°$ÞhZl¨‹lƒ÷²º\c‚^ŸGú*aÈ7²û[Ø(Ipºü'&r«±;W²ª¡WBýÙØ*3RÎô{ëßMóœ…U5 ¥Òª[MÍOy +±”SÉF.óU2þ3g<Š——œžúy¯JÂ=§õéuŠ¢˜Ô÷d’Œ…æÈ?¹  äè%Ñ®>cOnX Ãoµ hSPù—ipÍtq²N‹jÔðÔ}¦FÛ¤à U{T&8ªÇð@ÔWö ¾a5 ÚiòܬU®yA‘v¦$Jhñ«ñ&&h_M²}‘-ñƒæCÛÃÛ¶< ^åTZT3¬jkÛÍôpËAn𛦱qGu’GÍâoyU‚öú¼KÉ…Kä ×pJHª{*gî2ˆ +Qé–+\ýš2Ý»?¨ˆ‘ŒQÈ–xYÃovaå rÞ\Ü~Ϻ¢ÔíÍ>Ôë n|ÿm}xs I‡F^"”1ëÈá;vË¿®$4Éþ¤Ÿø /¬¯Éå½l ðùI½£§[÷—ÊözZP3ÍAðÕ’*FR¤²‡bh Kô£©­‹²g4}ÀI®F~6¾bµ‚_ð2ªþJ3aý6™þ‚RWÈ7&ªÐ-J*kGþ­š©žu˜¾å#eÇ>¹û¨p%Î +‚Öï4ÐØ‘~Åê¼zµlijV¬N’Ê~Žõ‘^“æ‚ jBÖʳÓ÷p™rD,)Û>è`ò¨\­¶£'¢– aê©ÎWôrkÍ×QQÿáÓVÑù퀮Aè5€<ð SJg²ÝÍ»ÿd›w—Ÿ°^—±Ö8œþ{»¦zÙç +08ö÷ÿï àÔ¶‘»|šWZ£wÁ“Š–Oê`³áÀaúñçØ”¬Å ÌþžØBX®Û#^õüB_nˆCž¤Åñîï~dZ›IzMè>Î +Å'q‘Nõ~·_ß.®÷>_­Ïdi§©ñh}%áÌÛc«Ð½¬®Â³Öp°ÜE—6–»Š±\7Xoºæù‘e 2É?º<º°bKê4¶–Êe§¹®€^~§HÓ>kjƒ`o XÐÉùÔË%~åZâý†w—UxèÇËíòÅXùÆ€(áÄ`Œ(•ª(-C…þƒ„R±à‘R•o‰¸9¾v6mNü·`Ù;ÿŠÄßÑüÂ…·„FgÔòóÙŸžd‰›W$¡®A’€®¤ðˆö+(£‚[wK# +êÊí#ÓÍ'GÐ#uïdµÔ.Ý +*àër£bàÅ Æ ‹šjÙ³iP +TÆÏå èÙ´Í ÐñUºðå¨-Ô®-Lt a´ìЛ× ×M xlŒaMìÝ™g˶ ¿ü¹LGWøº½rSРÓHM ½Dy±ÃGïüúX¹´~a±Ïò +ÐðŠ)úæ{QŽ{hÓÔœ·ˆt¼ÃµœˆlO¤þÂhÊÅÐzOc–Úe{ýש§ýb‡„OúÈ7‚^~!¡S$«µtq½¢†¦7øbŒÁ¬´Á-—;õû7&ÂÈn.»XtÃÏUçT}ø¹`¨gò5ļ@ÚIÚêdðbª@â¦R bš,ĹTi|£p„¬ÇÆÕo|5V=Ýꩇ +õLºzz7¬žX35䥷\3às 4ˆ¿›Ü""è+‚eE:dËï¾ÃùŒpøˆxCäÊÊ£ŒD¢;ø¸–±ú2Ñ–}Ñ4öDˆ ˆ@JáESŠ‚W“Š"a,(ã´¢HM‘Õ=\Ý&p’XljÕNly'àÍÀ‰­F8¡~‡Gg1‚Ç?‘):€ÁgÞìóŠG8ì‹Ï¸Xùæ)MS>^puPRéi ´£^« L²~ø«˜kuܱŒ)6döü]LV“ÍA6êâ®çË]nÙ£ v‰çÒœM3Qb‰,عõ‹6,4ôÌIb²…+][™/[&hW'f(š®=­F¬× ”ZœFe1A£ S.5MÑhê\ª+ñø™”s«ê9±3ê):{Eäo¨jÌ Ýô>«š÷4ñ­‘{„Q0}EpÂWqî^19›OrQe‡QHTç?À9ZšÐ{öï'ä·jºÂŒüü$$G•ŒUÿ–ìЄ«®ÃòcPùw‹ŸµR¢ª:Us÷9mR‘Ç›TT®ÔÇF·?bVúÃ&)¾f$ 3ÕX?f*zéºyS’¦©­ao@&ÞºiÖÆž nÌvð{ÈnGÌM\¿y3 +±î†"ÁÙù[?óÞw±¢J̪¶ñ¬&„Ý2=Æ»/¦ºHÜTn$¦É.’8—ƒâtx¥TŽ^rY}§ÖSYq +h\;dwÚUê;]Sn+ÒΧ~.ñ+÷±0fÞ¿>^n—/îÌÊØY‹'(!*'çTØÍÁÖÔ”H£HDS`;ñ@[G[kоÝg¯÷NÁ7Z2Mì-Q·;åóÙÕ^è陲õŸ;i–ë òÖ€Â.¨æ#šñv9eôš 5Æ—:Tט­Diñbª´â¦Jlš,­Ä¹'#)ÁË©‹;%Vùª1³ç´ØÑšáýP°“™(Pc•ifÕ9æÀ›‚¶Á>``ûiO0¥¤]Ä´½«;é>—kýåægI?˜×èéBD¢éDhbÍ€6¸B %³_¼ðÜâzÿH Øë;Ô­_YÆäÁ+‚ŸK¦ž—b…=ë”?63U_asÖÔl2F‹˜,xÃÅLÙa1éÅnÞt±™sWà<¢/~lÖLÕ’°Õ´ÍTí~·ø;¿ðHi¼â<ÿÁymmÑÛÙÕSUÝE‰ºa$j^LuÜTî%¦I¢NœËAqR¼ˆ_T¿ñÕTNú‡—í+¼l#ìª5ä8—ùìL3‹Fto9¹-Ã%Ùeû*n†z<…šû5—í!¤úê¡ÿ†4Ãe#ò&G:C–m¥fès÷„ÅÓãxÛ{¢húá9*m›—Háò±_L¢R£Ò{‚‡ù­Ö¹q›bÄ}ÂÕœp‹Ž[¦dýÜí›JÍ + ÆZ¿/èJ<ŠÒHš¹_–àŸ1 ݹ£ôÈûw¡d¿ºøôêk˜­Ê~|›côÒÆu ©ŠpÒ©FôB‹»Œ'¸©Ò&™ì2ƒçrPe›â–ÈŠzŒ,¥RQ†ðŠíø4Åû™ZFBtÊ-Ãqn1tŸÓÈ¥™“‹Dîèå`ìTÞf¨p—'˜ñ D_—SŠ NcÈÆg(£Ü‚—…¦Hõw™×L†"GLœ­²Õ« ÄÞf‰¤@Ò‰f—ëœeqãC^Ñ|µFZç(³m^ã‡zÓÝ™º‘…›ßÚ9ßìãŽBn"ѹÂ͵lîH“f§¸Ì  ìl` +û)·t=/úã9½³ )*9qééƒ÷º_{\¦ÃiìÜ©óËO€ÎÅ ðF:*ë>Õ–7%]ð§’tdš*,I*î÷",÷ðÑŽžÒ ?ðéa$X£ä¤6ÝÎwâzAÈ+U†ÑM='VBТL¨vGÙjrÔp–³ˆ³ÍQ+7WƯÂy£Î[Üiì¬éI~±iåèéÔ\'ùÙfø)Y¬•hÙ)A]° +2•g؇‹?°É¯{; +G e;þÉTÓZ å¾~Ý—“Ñ“X¦ß´Ø¢ÏCtˆ^O , ¸F·' é¯%|OŸl~ ¹§t–R/ y†ŒDz€&¿®G¡á–OT¦ƒnŠ#©E•Ž*™Ó/ð§/¢·HJùøÄ3˜ìDMÞ±©ÐSášÒÞª:-îÅs]âŽvñêÔQ…ûÍGûÇò¿ý™1~/ÏvnÑ=|ƒ ÈP—l´ÔeÞUÛ »šêÒÈËEñkHÓÙOòVl˜33ô'¦`$ú™À|;õ©ýð±ÝP¯ª0éþ¼ˆÉ þruW·goOn€)* ¿Dh–ÞËf³¾E2Ìiz>¢Á!¿ÝªQÔß òjD9o.»#¬RMz6šš8ò#rŠ£ÝÃÔÄ0@¶<•QÓ +¤Ù¾Ëò„Ù9*‡~7ÈêQjÞÝ¡=¼(ó+JýÑ{ï¢U/Z›úã~ý <ÓóÅWþ_ãoÖóÃ_.Éxþ]Î/®„Nººgy9,xY¤¶ÁK6Z¼Ì»j‡aW“—F^*JÞHšÊÕDL˜f‰mÚ3ÔªSÔ _³¹û Q´ŸÍ²€Ä¥L«öë[íõ:3ž„|w‚O5?ðæ¨m_æÓZá¼N‹þÜàG?÷šþˆº³‘¡ôCFKœj×øT=þ‰Ta~<À<éW,$ŒD.ûO†fUñ¨ŸATt)ï‘§ Œ¼M!_nõ‹†]ž†òÈS:bSIHAº±Aol¢jD¦L>žÕäÚp¶;š¸o¥Ûü†Ô†¸ƒ´{ñ¾5x ;ì\]ø$ø¢¼9”4Cƒ\Ž g3aÁè!nuJ\5òtf›Áæ¼£v(r´¸lääzúÞæ9‹̘ŒŽ6Ä=;Ñç®ÃÐÖ~¬Ïo/ça^ãOæ:;x~Ž9Êúûë/›§˜ÒÕñ£‡”ˆ §:¿åî éZõÍJ:õ7£`® €ŽG€œ6à†1JQD…¬ê·U¸7¹]?)ñÃÅ¢×áj +bê¨÷ÜÊô‰KÀMñþáÐþðª!²õHŠ_ð{n±‘E:L‰ï î¾nžjÃ0ã@ß­Zomªbµ6UËY˜ Îæ¦jææÊè’“dT˜©³aÎÌ-W\ Å\®Š“ë?(EH1ø©R|#MµßçRô[=RNGµë¼büŒÛf ¾ÞuÊ)RíHnJÄì’c±>šöH"ô«ÿåw'£g«\ï6üPŸj1JRœ“(÷Ç©ÎKpö°†)žÍžîªyS\Ô6 ÙjÚp@‹³ h+7WÖ4Ø[Ÿ7jà¼5‹õz„֚њC}M“ûê›%ê§½ûê¡3‡FQï ïJºêk¹ò­tÃ'^[ …èÿÇÇâXœöðë|´Ø=ƒ4QÏZÊyÙER”x-‹Ö™]IÿæÐˆ].´Ú´à†``£¥ò®‚+q55ƒ‘—‹òðfš4fúß2klËžÅVw¶\EžÀV?ÙRy ¹«# ý%9ˆ„Ü–¨xú ¸!ów„ bÞg }ࡤæã©ƒ­ãAêš[ìOéd÷[„.ÅMéH÷ê°“M†5?„µÕ“9 êö\à…ðà’œ©TÙh/ï*ÀWxF^. +)8BŽÈFÍkX5²a6T‹«†¼tàÔ]R‚ÝsÊUž÷~¡–çº5¶Ño'R‰ä|‘®­*Œ†ähWQuž™$i[CѦàÅKc'*š`Õsê­‘lcÒ!/”ÿ—Ô0OgdÈËŠ‹=ûOЍXuÔ¦òb£E ¼«H\My©(~ÆÄÏÔus˜(,àa/8Ó©¡îÚgÞë8CŸ^è79¶äSÏÒœ#êí¡‘!ñáâ×_F]uSj[ï±30ô¢ì ,Õnšõ†¥Å »×"_ã\ùYÒb7 +8iºèËã»Ö<¯Ô{XC*õyЇtG‚­#"˜–6\ùâj*`+/Å/!¯ó6‰š7æŠNlzݰr~&KÞNý}•øÄ'îì@ÆÍ½|!ìe²¾ÆÏ&mn´ ‘`yÅ&r×-}j·‰¿‹êݧ͌MŒPÌ·Çý~ËÊ› ¸Æ5m½Ó—KŸù6ˆ†³ƒ_<ü³ˆönŽè~X£“ÉhßY7> »Y­;ŸJá—°Ð.bÄ÷oøeMt2G0Ò‰1SîŠbZç3¢ª5ö”.ò_)&d +endstream +endobj +59 0 obj +<< +/Length 8341 +/Filter /FlateDecode +>> +stream +H‰¬WmÛ¸þî_ÁVQkER%àp@6É]¯h€¤çâPlîƒc{m7Y+±½ÙnÑßy#õbÑÙì5V–†œŸyfæêÅá´»],Oꇮ^œN‹åv½R7Wóæ³úýêúºù·º±6u:wÊi“í*UTešå¹r¥MmVÁÂ_ï?œ?¯ÕÕ_Ö‹Õú ®æôöv±Ùí§]³W?þxý꥚|™ØR9S¦y¥¬s©+•-SgÔa=ùMí'×óÉÕ|ž)­æ·“Y–f™Qó¥’J•Áxä: +¥šßMn¦¯“Y®¦ûí"™•ð\&ZM×É þ®Tòûü¯“×óÉ»g8G¨kÔÑõàïÉÌ©iT`4™5Usz?¬š“Y­¦'Ne +ý?¿òêO$º§#ðŒÚ3Ýn}4ºJ Óqr:Kæÿúc* \zO¥zžÊ^põ ¸•ôï÷Å#ÉáÚ}å·½=RØÔ z9vþ6KBÁNâÊ{ž ‰ÖEk!º˜ÎÿÀÝ@X{3>.Ÿf·'˜Écó½‡Òg‡ruZVã·‰Cá²maBV½-_&¹¶hYüÐ¥y’¹®S»ê´ÊNØ‚‚nà8®B¥þ}·d ‡2ü¨"Ì©±uGØ÷B£r„~áÉtˆïúñú pî‹Ðçus:5w-ƒæ©µ…S¦JsBZ:?R[ë.þÔ4§KJ`)}ÖXKå=®Äá¼X™'Y +0Ùî€YðdD‰OE\çà uª>¡¤ÄLË𱜥šð¦SJ=¥”@â9pcÚȲeâRCL!‡÷;ü^mûSbS@o +röÕo€\Ô³õ+¼â‡D£ctÓWNpÂíë~Ãi¿o’“8ÕÁ`8Ÿhõ~zµôÝ´ë€+ú5\ÔÑ×?÷eû=G´ÔaõÐèžb[WÑÓ/øÀ‡ò± ÑózZo*:LXÏWµH`yÙ:ÿcšû[Æ(…/þš÷‰ÎÑS¹>Žp›t Œ*–kkïF¤ð¹7¿#àH¯5ˆüM+ Î#ÎëÜû¨¦Do$¥ó¯$5¤U÷“ÅáÒ<Í¡q!ÉÍTùÓ~Øþ1ö n¦eb´¯¡)™…¦`œRVNÁ1ZÈȋĠ滄û¨æŽCˆJziŒ?¸(¼‹/±GÀå:·!8 «Fö!¾>n>¬Ÿ†aK ë†Q²áDÄ)`¢ÈeÍyQØŸ¥Ea¼£?A–YÐ)?$5:}¸G i>‰. ~íáû/gNf± @ÈL>pý¶6R:a!ªmÞžˆøùuœŸ»ím^U©+¨~TÌL ˜ÁÞÖ|_gëÑÎ +´i53ÈRí¾L®~þ5S›ã„„™suM¼ ‹Êþ•µê:“e™.¬ZÞM®~¹ËÔ«ÊŒKñ[<Ó›—¿¼ âÆr`àJÒM?ò +nÁe«j[Æ|ŒZEº=L qÈUI›0ºV½Ê= °i›ãAñ¬ ìѳLÊ'õèT|»BÇN†+&Æ_#Ò™º b»öÅÄ6¼OyÉ.iµì<:Ê€={@~TjV_13:‹÷ò3¢Ô;\ {‹0í‚q²Pb†’ý‡¤@ƒÓ·òýÐwŸì»}Ú·#}Ö“äÔϵí÷%=ÐÓ½¯ï&¹A@8¬^æ¼Mº(´Ô‘³0/S«»MTU“Kã[ùˆ~«œ²®¸°ù²”]Æè±$Äλ4*£²ÐÛµya}üFÒª¬rüÝO«ÎÞ¼×¹=!_P¡–|y)T¾%ìy\"L ¬òÔµirÄ-`oo û²Z q<̪ ‡Pßø¬bÄz°Sã‚UìWŸy&y&&ý¦5T+~9,ó}R‡5+‹nA¥oŠfá£éï½À¾~ª"Œ@5²Õg#oA5f—‚ª6-°ÊžÙq¡×;.dy ’%ôQU’î»!YÒhу¤äNÆ‚Ïx¹U÷ÎÔêŒû~? +n~¾Š)QÐç‚õùϤ/dŒ¦½ƒ¢N'Æm«¥¤¦~,OVM¬.ùäð:±ÙíÇÄpƒÆûV¡®œ‘mwâÙP ²Ð¸oQ©{éÀÒN:è!0Ç6KBÈæNBè3äŽÚfÏ 'ªòXJŒ ½âqéHJÔ—R¢0é…ŒÐY/%Bojã)ÃÓ>¥„çBK×íð2—Ãìy'Ó⃇&B6‡°»Z°æaI›W$@Ž¥f¥!圵d î®Ä¹ýã j„è†Æ1ÜÎwœò+¼KhÎkmšE·Á7õ©éõ[\[©h¼'[Ùë= û¿¢ sz(Ù[rmn`‚JWïàyØúÞdQÎ0Ël™f°ïIõGëAÊ:m+@,Œ ñ­’o²5Z€"vÙ)èËNÊ ¢ˆÔkŽˆ#­¥½%p`^« ž½5V㣰úý¶ª¹ÆWÎPÙ`xy¦»fÿvݬ[›6€‰Š&X&X=–µ…¥ÓY:$A™JÓü%æ2û M8ÌV9ÏVÎe`£4Xw;6|_þî|`¥§ûNÞL0s5:ùŒQtk‹L.'ª&mïfü¬Ñú ªkÛ{tlÐÃúÐÐQ°³0öñ­vÙ{Ä.;Å·ÖÖ‡lP˜bò‘ +¢/–“aÏ­!¦_CŽã¥CCGSñõ¾’¶¨I¸räÒ#Ù¶ß7 ±åIz„]½_&߉‘‰5G,B—åôYÉûgaËuR¦¦¯m|SO†·^²Þ×\¹àφÙ¢'ÔÆðóY[_öÁýǤjˆ"yÈ™Zp€>ÎwOaa3da¸ã¬Š“…1`Žo`ÊÖ(0#vÙ)[^5.bv\èõŽKÏhÐSXQש¡³¶¤+ü5†è æ2Ó#ª3Ö›xF8*ÞˆDe`'œï¤b TfñŒ”ÿ4˜klîq¤Àj-ˆ¥Ž`Oæ¹JŸIDÍÖ=wÞÇmB#í‘×ʉcDÂO;w×b–ò‹†}X!‚ÏfšÒsNè©g¤vBò~ŠycB¦ì1g-öH>¡¶”“Â’V!áïûMË!¸õ}"ÚÕ÷'Îm¦Î±Qïb„OˆÒúþcA®þýÓ'%ŸvHéãø"BHÅþDÕÔ*à˜­Y‚mFyL;rG«þ9y=.ºi›MShqZj-u'âÆ@Kól´Ô V*Ì?ð*áîWÿEyü(÷í‡R?JÈh¹ójÞ-‚ùc•ÁøZ2¢è{ ´Q.ºû„G7I÷•ª{zB}©d˜ÙòãüÛžhÆI©yH¨þû Wo¾ùIÀ¡‹*ã ·aZü^ÆÐ{òÅš½¦$2?ÈGï^óŒõ§h2cj +Æ–QÎj¯ ¤¼ÚNLà¾\-bêòìï<ïUpáWñ ÀTW–¨«%ª,ga™8›¢ÊÌÍ+ƒŠÞUÕÒF œ¶¦ÈÖM\Ub#¶)Ö?›bU #нSpÌÐ>ÆoCç%¡a™7•tô;øAw‹sB7™÷É£\á­s¦Œ@ì‘ÛÊoGa&ª‹¹æÏ3&ž¯s¤ †}®¸?lCRÔ@f`£ÕÒ®‚Pq5‘—U¶‹&™ +ÉkX5²aN@´-ŽA´hpY&DÛrQ§­mˆ úCô +‡×X¸¾Áž0(hN„+ðvŸ´l7Ñ=7‹º“¶Ž«ÃÌ­—œ~R,ê¡Ô¶ÏX’?ç0o€v/sHŸ]çWfÂý˜—¥5˜8Ü>B5…OvÙ˜÷‚l7yGËÇõsx³ d—Ô‰–O9ýB‹â»«¼¬¹‰Ñd»·rJ&QüJÅü˜—DÑå=¤wÈ"úÑàË g…¯uÁp¹–H’.r¤¥ÓjôêfJ½^´½<¸j 0÷‡-¯Ç,Ÿñ"±==ù>‰Äúúo´œžáØè³k,J%ž¼ç›ïèæŸÐaðëƒÌÝÁ·5boòA¾„Ó亠¿+©sÙÿ _¸ûêRnÉÞˆ½ o5vÖ ðàü¶¸øãìxíÐ # ½ªªŽ™L×fÌ¶ŠŠP¸0‹­¢”v•¢$®fQ2òÒ¢jôÒ‰¦ŽóZV‰l™SE©>R”êÎaq³‹’›¥R+ЪJuGH'Tÿ[ºÊ&HŽon±L\çTžn¾1×–òÙ–-„ËŠOxñ=/¹§9ª%²€gó.Þ-š¾óåI›ÜgÄ"uà +3¡Ø“B¿S -„5T¼h^…†@Ë¢Zf­‹¾òñFÙc£UöÒ®RöÄÕ,{F^^Ÿ†¯\¾†¨³eOtüîhÇw4(ª¨À?¨P²5P`uå·æ‡^ÖlXhmiàʧþ}`ÿ¤“²ž&’8IY€œh§4—g·_E«vU-Ë‚¤Ï¼ü‘ïÔ²YHï¼vUé‡R©}ûI +Í=†t2ˆŽ:ºòÿœæ ެaIÿ«»Y"C€Šû,dÑI§vPšÅÙtæJ­á¥K –°\ʱriõ.©ü¾úõ=õqÚÒÕOAk.&r~ÛÙ–TXª0Û_³ÖÓç IŸXµ>¡ EÝøA€íLõß]^VÜvDÿ­‘¸W!:f8 ¶4†&ç½`Ü$©¦øè¿)e~uT¸G‘§ÙR·û ÈÜR¿Jó¥Åuyj±>‹›>Ö xí<]쌾ÒÁ¬Qµ2\zÂ|GÂÀº?é‹RÒSé?J")Ž žShZCbʄÄþ +g»Ä¹Á1Åh d³úGÒQ@ÊŽf÷Hçäõ0¾­QÒ°j`Üj,í±Æ·XõÇK7–j(ñ'`fó¸^dÐàåæzQå܈Ï,jXЩgR‡¢òî¥þÒ7X»U:Ö±¾DÆßAG–PŠfJ¹‡¼¤ª½] CÖ­â¥ÔG­÷×:™n‰Û8bv‡2¶¦ÂCRï8_Ru9×NÃrZCðV¯7ÓRp[d¢§­l +ëPÇuˆa——Rˆ)D%¢i7úéG)þ÷ «`±zGÛL¿Ô(úËGÖ?çȰýt‚¯úÿ©C}T‡*€º³ +­J”vÕ~É®f-2òÒ¢„Ò¾ž´aZÃ(q kª G*QÕÃ.Ž¢¾8(D°‚²õ[>˜GNÐŒ›nXÔ-ãæBêAÐêöÜé¥ý®× ›P¨Ã˜åŒ/U úöÜñ¥¤ÀZ3™¿S&#uwB-´ÙÙ*S_É.]×VkÇdÌñŽ Ý~tŒ‡º +9;Ú¸ð­@è¤9 Fƒ†«v&vµ8`ååEñ1Nk’Ø2klËnŠŸsáJÝ€“n½h N3,Š>kjTŒ‡¢¼ˆÃ¼ÆGfŽ8TÁçòõDž²n&öI¤Ã!,çÕÅE“•ÙÅúñJx†P9Dÿ1‰ÕvyoJêÉõ?±ÜJÞŒX‘dïóFðÜ`—ø„LBrÜ‚>WOˆ½ÉÞ¾ áüÜ}Œ¢mE(òÛ8{Œ%¿pR…ÑÄ7ñC+LN5wØhq'íªU€]MîyyQ|*øç®ik¢Eôͱ õˆÐÙ+ÝS5håºEåñÚ’ðS]«šLEêVõe%*‚täC¤ Œãní¹Ív›¼Þ\˜ ÁØð`lƘ$üûÔÚjI]² saQu-]]õÕWPgïºâö\Ù>ƒœĸz)0üç‚ÈKN ê©\‰‚#qøm¾ŽÆ5û£è¿Ã#@"¹<ûp™hÌ*;ûLËÎ@6±{ü|Õ´´€³»ké¯ì+}ås·‹JDA5û"¼"´#]þ{K?ùÐ÷ÈŸa_´èÐfó{¤ÿðÂqÞm˜[ñ¥õf]/Á.Eþ¸¾bÂõ}º¤kˆ)yYÂwé‡Å¡ Û̘Ҷ«áPvP×øŒN ELÄ |r™-݃cϺÚG€ìðIîNï+6õÆØ«ý4Ñ¿s4^Ñ"¶§ S½)ô&*®‹0ÛáOIï{­6&ï˜o0oÆ—þDÿÜPì×/É ¤cƒR}ÔvIÝbo¹ºÆwÀŠqK)™S¡wEXˆ+ `ÀCuD²Ào1`%MJŸÆªºâ%Ì ¿D,¾]”ˆ™´œ6Hˆ1‘FÝ0ø‘ÉRûºäaS¹Ž’s¤S a½£=õ ´ë÷VÜíp9hq gÖhÙ-Öö‰–kày £‡Dhô¡ªxÁªVY~9(~cƒJRãÂnŒë꿬f–?ŸÏ«iêîò@c±ò⮃«ÍI’b·òqè’X {2xžPò¶² +ù²‰óßt“xì A¼êÍÝ2ðÒMàcTÍϸÙÒ0Zñ‚K¿`PÕXóO²ßêï ö@ÅZ²ÔÝbò‚¶˜‡9šh-¶‰®/‡{²R«ü±IKŒî:·Â +i@îèŸLÍ÷¶a„Ù^cËY;ø•ï÷ª6·™êi”°m§íÉÄx—/–Xž!ϧ€ÀR¶hQ[Ïcƒ‹ŠH­MÅR–K‰²¹Œ˜¾92ÎɰíÅsZ¨†ÓR33e1|ƒ‰%ÄðµâÕd+t}2ÚQÿ,Ȩ!±3dœŠžûµ ®0£²@£X@#òÏܽ5ŠR·ˆslÅ&Ñ[¡ {Mšé¹>ºìUm¿#ˆàæ`P„uŒ%¡ûéÃGÐ|J)©ô›÷~Q zþZ ^%ÀàtB‡ÙóMµ\5]þ ]Jr^ûL°$Éc®Šj+;Æ/ž[Þhõë3^¥kZA #Ø+ˆãž€–•ôó&¬ Ëã[Â{”ç‚à¬h:F7øv†Aâ:u‹Q·øŠž‚áó}A%s…AÖ4{ +ú¾¦c›M(¢Š +5Û@-s,Y®ÁUÞw)H ýÿŒfœøZ«öƒÔ+}ÝiÎ*IdzéÙ=„d¥šûioÉÞ)JP¸ÿÕÝôWc$žt£¤kcjl•è ¦N­ñ~48SPÙo‹ŸÒHN2äçÔ69Raú€#ƒb³Ð¢ØiÕŽ$ÍÅ6ürPœ úz0MÓBµ›–ZIq)þíx¼ÿ®ýÉiÊ +(¬†K­óH݉CŸÈ\/ˆ¯•’ïA +¬þ%’r ¼éSÄW²¿…Âß÷õL¡…Ø é²#;‰þ<}ãQ{úœŸT/Úó5Vgl*ÞS©F‰Tê¢ð9@Ã\Gë…Bë>b/ºTüÝo$5™.2jNw‹Á¾sü)Ì¡+®Sh¶sš(x áHf!\R±Cl?piŸe"@T@ öiŬ!µòá÷–*ÚqÇÚ,sÓï “ó~LÖ¿|د_"¬¨:¬°] ¦¬ SÖ¥ +ªÀggx-¬¿Œúh–_..ི‹Í¨ÂÊ;ÛÙkMÏXQ«:ÿòì+6UÞ-™·´rÞ]ëªß³x°ÆvÚqÞ7h#mºQËÍ +~´l”îg¤cö]YôóÖÝO ´zv!è•­¨iSÍÃ\Øá¿m·W’±£(¤&°"lb¤×SYx£•V¾ Ílˆf’,,´Gù+Lš¿%5©ƒã‘ÕußêƒN«‚ÝOxb|¯žw±ªê¨"^“…utCŽ ¿^¾éµ‹²†3ãÇ6±¤Ja+’³°•e¶¦èpØjø¤x8 ‚«D1-4/\¹aUa|³É!vhÑ«z™ 'SïHÔ±^Í~ÇhFrºÅß|æX5ä8PGëfÍ8Fž„Œ¡ºd¨žË kÀÀŠ‘äÈ‹´#µá3¶eC@Yàï'”#.ÜaÓdî€#Þ X”Ú–×ØhÐŽ·WÕÒ‡Œì9ZòŽd§ìÃÇL ©èŽW?ý|,؈'½"Gã•°Á}4¤äd-ä.ÕÓ Ðäx;sqS³0jê{1Tùf¢µuŸÜX~)¨*jz\ìÀB,…4Ùª†îÃBë>–W‘¦ Ë;²aó¶–i§mOKéÿ`\Š +endstream +endobj +60 0 obj +<< +/Length 6207 +/Filter /FlateDecode +>> +stream +H‰¼WÝoÛF×_±OñPÑÜr¹@Q ŽÓk +p.ÂõÁéƒ,É’[thÚ¾÷ÇßÌì—iÙNîÀ"9»ó=¿™9yS7»«Å²a?þxò¦iËízÅ.NæÕ-ûãäô´ú7»2Õ\i¦¹H×%ËË"Í”bº©ÌJ8øñþ²ùz»f'¿¬«uÍNæôv¾Øìö‹fWíÙO?ž½e“/Y0-ŠT•Ljê‚É"Õ‚ÕëÉïl?9ONæóŒq6¿šÌ²4Ë›/™{xd\° þÃâ)hó4/Øüfr1}—̛dVÀï2álºNfðwÅ’?æ¿NÞÍ'^¡€QÀäkðÏd¦Ù´JšÌ›²9½€’Mï’™aÓ†RC}AÏ{úšH÷d‚ý‚^{¥Ú­Ž‚—i."%§³dþç·±ôKö:–çòžs2éÆ÷Œ|h=iݵ{°o5½}%·±7ôrý­–”;çW{çµ)Ѫ(%”CœÓùwp>ÄŽò ͬ¹ÖšÝžÒÌýl^j?0J›´(‡£;â‡\g)Ê‚,;W¾L—(ÙéÁ ñ,%7)Ç[&-UO ™“Ó˜£KdšáàwEÂÀ}HüGÂ:(‘ +i"bW ŽÇÕëéÙÒwÑž¬Ð¨Wÿ\`G_ê»ë÷Ö ÄÅ„Ó}¡{òmNXE¿þÀ¥5Êû6xÏóiµ)ɘpÞ†j‘Àñ¢Uþ3úTù(£—Âæ}ÂjêÂçíÑ$3ЫV° [§ ¹G¸pèžû5i& k ùH3Ÿ^'gÏ8Ïsz¼e”ü˜øR\|^¹ÒÔPVñ;‹Æ£*U0¸åbʼµ½Ù}pÂÙÅT'‚ûž†’Y +f€)E©˜ÐBBÞ$9ß,À•`ì_Ð͵u!2é”1><2ppž{ß⌀ÇCê\…:°]õrdüëýæÝzÝw[šKÝ÷’ ¦€ˆ\¹3ÿ±‡Âý,Ísáýª”À‚¬¼L *]ßc"hP~Ö_^PúµÆwž²˜ly +ÌMH„L¨žêV_@káZ'DcÀP.Ukáó»q|ŽÇ[U–©Î©åÔÌD9ƒ³­xÙdëÑ͸q6ˆ®Û}™œüãcÆ6w"fZC¸ ‡ +ÿ +Ãbe²,ã¹dË›ÉÉû›ŒUÐþȰŽU¿½}<œ&œm&Jb( ´¬NÈhÛs¯þÞgpНªLíièíYwô<FÞÚ:"Áuu°£$‡{*ô'œ ³l°­Z'Zë „GY¹eŽ-ý˜m&Oñ<”Ì‹ª¦1P·öqsÄÆY(¢vUè™MªèŒž©9¿u…Z%Têv†»‚—‹žë×tsþfK‡pøÃÂjè·-gF%È`ñA[SÓž¬7Vˆ?v‹ßK+›º,Im> !@JŒFÉ—:£o¹€ +ËÈöÎÎð?pµ€Ö€sgälr¨uñŠžÑO*r¥‹B!*rnó^n˜ZX|o#ƒË›m‰äJ+Ê»{Å6¸ÖkDé>ckûž^V™IÅ“GÊJµe…Œ•H »e ÐO%íj–x y<^—†B1|ÕÚì¯:³[r;~ùiªUÝi)²Y•IŽç mĹ÷c½Íâ„B{•žB_Ø»Œp\DâpɈªô²žƒ°E #ìPW´|c\:,Ì3ÂÂliÃx fÍÌF² þAX”öÝ™AŽâ+Ú\º’?Kf‹¯ºO¨F/ñÝÐØì†˜RhlÎ,bÎäÁÇÏahÜ8µµsÜÂ1÷ŸY‡‹§Ön¢ßì°ü÷DÛ°î™ aYݸeÈÿ®Ý@`‚B ¯#΀4”sg\‰«En—82±ª>{ÉÄ`îhL…«Ú᜵̪.\ ölSûGÿk-`w¿˜÷Ô¡/îygù" ¹[$ Þ­Y嵊|‡€½Œ…_Ç![é§ÈC`£LüàrÑ¥”ÏÅb$ËìɆ÷Y˜Þ‡“QƒÅ…MÆU‚Is—àpvrÌÇÊŽË‹~s ça78®L_2@‘ècmÀ µÂPÙ#½#Ž ýÈUßÝìÕ1¤“k•²Î°Ä‘¼Üªç¿¤2äÙá§l{E³ð€ÄÎaw“-ÅÓ¾ñÆ>™¶à&¹JòÇž½§,ÎH5TëÖ‹¾Æ4ímmà é>–x–:šx#—ÛºÂËã‰7&Ûjfãg¿Çô·Ç膅ÇY Ñ•6º6/á¿«~ $˜XÝ97fÖJ õŒÉžË65 +ÆüÂé7×kÌçø€É³Ã Ã/¨t<÷óWtà/s|\ËEÖ‹:øNŒGÝRG£>rÙEÝ]ú˜l«Y¦qÚQ-úÄÑýl˜f "ÚØÊ8,±%Ž®“–ëè69Ì7¢2~’8×",oЃ2¦pÿ‚Ù0ÇE¶èçñ/¢½':‰LÐxk4ƒT9¦æ«¢d•”©v#ÀíÒ0?1;e”𔕇I«†¢^BV°¼8žS¹¼IÂöä6×ñ¹Õ¶4Âʹ˜žÙ¥¶=~‡CqÜ«{Ucµ˜!¤ç¶L +šWö !ê*¡5i‡+ÔÊŽö‹fÝãªÀê26{tŽTyrsmA¶kXÓã}®41ǰÆ!‹8n!Hœ6¶‡Îæw –î£Ùº¶ïˆT@—p\ëÓ +<¬m7ë«É s`¸ì cGÍ;× —ÂE º_~¨-d‚VœE§crw¦º"/…ûϹˆïw'HŒV}N<ërŠÄÈ×ù€ÀÊVÕØ®ÆÏ ×°9õøG´ÕÖmF[Û^ÔÊ’}­‡­öþœîö}«K%Ÿï5«6­+7€Ž˜BÏìVEÜ. +J[ŽzÝÝ¥‡µ‚÷¬ê{߸‘kFÏ›~sý/ëUÒÛFŽ…ïý+êh‘"Y[_éØÁdH'Íç"Ûò‚Ø’#Ëòïç­,’UT¬`.¶¤G¾ß÷H‰9(X¬ ¥.4ô¥ü_M¨¯_$F÷Cîìy«ïBì›~À.õúçûD-äГ¥Ë ü¡/'Äè‡&³¦šËÙ…šI‚¯ƒ¸ëú ~9z!Ë£,pcÔÉF e¦IAyÍÅ}™–Ý^I1ÀDF̰Ùêóîí1Òè,¶¶ ×ÁÈÙÔÜëFÁSHçAðÀxBç+±Üx˜P]‘ÛIɨ“+ÞâfkJ`x¸x ɯ«usÞW*UüÃ…˜ƒâÎ#Õ=ÛÓˆH‡¸^hƒ-–;‰ +c¤è 4ââRŠ<jt½¦õ\Ým0|ô›ž‡I Õ£¡Á§Ã\ ŠìØ­¯ÚÜ£³þ«º5ž èQ#ß]sÔR Š ¯åBÕ÷ü»h Ó¶/"èÎñ„zÎkø0)ÞÀ¶ÙÈÓøÉ&·&c¢ê”¼¥d_„|ñ ‰mühÿ¬øšš-¼o˜hx&Nž(çwŽ!]œqìrRS²Á@­¾¼þ1›å +)•`Šd€I-Žøgb+Ì»/áeŽ¿Îâ ¾¤¹HÉ~î*ã…ÄBµ8FÒ]¯àÜчuÓȲ¨F¿®åû®ð' Û_ʲ$&ôÓ ‹A7Á(lWzò_»ì5,{—Jó™í‡ú?gQ"·Ö³pØìUÚmöì®UÏÙÎuÅ.u°Ý8ØÐ€Ï•E_ü~õB8ïÕü‰_fz¡ Ž*Dë­}Ûgçs±ÆUlÞô}2}©‹3›;ª»ü‰éh+¥Ê¿qTcÒqÂæl€Á¶—ÜÈ|¶Þñ¬Éúø N¦c‡ä1iýc4šž¤×•ØNª¹£ö’,¬û…dR„è·5] ®š +[Sæò~)Û•”°RÒ”ÑÝœ˜‡¬…]£Í¬¬ô˜M;+ƒÔ)Žƒë]©Ç¸c±ýº¶‚)³£÷‚п©R ¹1 uÕ õBd„ø´’õã\X¸¼Ül„žkÀê]ˆžçù£Æ¡p´ +Û¢éÞœç«ÝfhÉVÖÇúåc… +ts‰±Ñ (ìû{úÔJ™Ârt¾Â0kö1¬.±Yì£ Ö)ŪšøGd‚ʰÀ×̰i‡½oŒiûÝgÀ>è\X^>îùOÄ#•ªò³ð«ÅÂ@”K¦L¥E •%%(c¶.Ê®õëÓ1Ò k”aZR¿mQÌð²t#sØ+qtjˆSïôÈJúÖÐéw" 8gÙ¹¾55igšJÍb¿+¢û¬¬+>szôoT ~ÞߊÍë’¡‡á0† C ñæAüàjâ-L}}<=:Á# î_ܘI mf%1ðÓר©_Q#?OÄ›'Ú+—´„ℼ„:ÃÙ”I—–Ç¥tÎS:Y£þj6‡­E´¾ÝL<º…i½Âü6T»þ~õRðgPq’x_Í,”Ïl–Ë÷7kÌ¡£Gw¼·Ä³U†îêíkѶÂ$áðí¶˜¥ +ãáÿpz²ô†²7_üÈ╌4…ì0Ï+y¿âŸ¢ú¬X Bl¯\âƒÃŒ+šµHZ=ªd0TÁÅ.®üqßBâ¯÷'¨Û’Ò† ãQº&±±þ­™<ïZÑVÜS/ÀAÄôfBØ^÷ÊLô°S(PMÕQÏl¬Ãy×1”|3±ÒÑ]òåjBµ x’®®ÃSçÜ.oåyW…êÿ¾«÷|{U< ++dÔ]]æ/©ìÔ´’ο«"5·*D‚çêÞó îöþüŠ,´…ô%­¦…ž§¸,ßC;’ˆ•D"Ú·Z Sõ½;Ç‚ßë !Jã*Ô¼×7íŠl¦ˆ’[üi¢žŸ <z4Ë6Ä çhWºH$iû š5\õKÝ’l¼Å×,ûcú€þy‚ñ+¦ùéÑ¥°Êsµs<Ï´D wHM#jç0~šV Ó¬ð…50#;ðG±¡‰ˆ"(–—Zc{ˆê9zÈZd’<Å„&i$·•b@Ú ¿Á1Ò¹eÈi¬ç“º¤ÉõTßTbçŽÒQO“(T¥šøpq¦l»ŠO¡¯ŠWÚ¾• g1µ\MÒ5BŠgV‰HºòdSÃâ·\ÓÃã8쇞È9os¥Qûg$}Z ôégI±‡Z7óÿQöCX¥C˜ÁþcXYðˆZV5ÎÔñ®]­£ÕÍ)ú·gÄ~Y1ôßoˆ¼è?=^…O» ï7âl006™1î^ƒ­‘Ã)­„ƒS£‘¶-óu¦Ìhk»vÛåNÏ\ä'‰8pè¯}ü®TžR¥ÜÉ8u=|Þ6é˜Vù1MÇIÛ©Þ7nó0–Q“=¿îKUœŒrÖê¤zŠ~PÍ[_¥SÁ¬Œm4…Ïñ!uœÃy ±ܪŸgíŸTíÏ'-Zù*„ø=Eå¬.÷>s«Ï<ý m} &ƒ«^3ö Ç™³õ<> Oþ¬9@oy€^«g[Å,}°¸•v³=‘Ö."ßs`¬a§°.ð$×3àö+Ô“ŽJ'áÿ|-q<‡Ãuϡˮ­èÀ½j—ò¹›U]›ÂÚyX»ÖG›5 +Pièý£ûÆß7r_ÇÙÓ£OT\¾¢û²ŽX~{›úÕƒÜhÞŒ’Åô«ÐÓRÔãÒï=S£©ÀVB$Lñy°*‹j$†bšëúô5 ¯=@¯gÖâExädñÛ«·7WÛ•?Úxb}ûÍ0 +‰»s(ÿ“‰¥¥Br.0"-€Y +à +Y Ë r¹L-ô@ !‡+˜+j=ºÁ†X 6´–Ÿ–l„Ý`s= c +Ì5Æn.¨³hI‰¹&ØÍ5Öº&ÆFlŠÕ` `!?LQÍ0&pÒã +endstream +endobj +61 0 obj +<< +/Length 4680 +/Filter /FlateDecode +>> +stream +H‰ÌWÙrÛF}çWô#15Ñ Ð€*•*ÉRfœŠcÇÆL䔋¢¸MDR¦ +ššÏ]º WËy»JÄÒ}û.çž{0<ßT‹éh\‰ï¾žWÕh<ŸÜŠëa¹¾¿ /.Öˆk­c+VªXI›‹4ÏâÄa3ë$‡…oªçû‰þs2ºlݤ»w£Ùb5ªë•øþû‹ËW¢÷¹§3aU›\hkc› ÅV‰Í¤÷«Xõ.ÊÞ°,!E9í ’8I”(ÇÂ]< ©DÿáÇÈŠüŽ#)ú“hoEô[ùcïªìýòÌŠm4=x ¬è¯ÁJôEI7à‡ý‡hPˆ~%À©D ¿àç^}G¯)~‚Y{¡ÛµJæqªNöQùŸo3© è-“âe&[É•ä*4Ò®ï%å3ÉéZ|á» Ý=SÚÄ9Ý<4þ®Ç„‚…Ë+ïy)$jµ†vh _þ‰À‹f"´Þ‚‡ËÑ,V3÷3ûÚ äVP¶ˆ³|wu÷ä!µIŒgaCæ­-Ÿ{Fj<Ùù!3u’F±Ä]Eœ›Ž:¥¤+Çæh4Á?`oJ‡Aúð>”WØð +œˆ•./Û^H´AŽÐF&SD|Ó«7Àe¸o}^¬«j½¬ÔÄZ§V¨”4^ߺִÐVÍ{l‹KMl@¸Ð›ë¾ðÑv.tûÂö$®ûy¤¤ŸA” ‚(§d¹R r)´¼A*!ØÿÂ4·œB4Òjc¼xà4õ.¾B€Ët¦¡8¡·Œ¬B~}Þ|Zïºi‹Sm»YÒ!"â8"5nÍÿxQØŸÄiª¼£?@—YP”7QNoˆD„ÏÍs$3‚_|{àæd¶©›„D™Žëì/°µr£b0¨Ô¦Žˆøùj??7å­Éóئ4¿Rf*Ì ¶U_§l½!Ú™ƒ5) +YÂM»Ï½á?>$böУ—‰µEA¼ ‹²þe…h:“$‰Lµ/{Ã×ËD\®aüQ`<ŠßaLo^½¾ ÎHŒæÄ@2²M,&ª=žÑPÆô6°$²%yÂu–Šò’ +ŽwLQ¸Œèñ[fm½)üK”!Îq[Jà$ÂÃvb0¢‰‰ðvÖˆÃÈaÍS©BC =TLè‹<ßgBqZêcŸ¡º`ËSèÏ” 1G‡î¹ð'%f•4–Üêq¤Ý¤Å7¼ønâ’ñ1bÛg5iÀ41zg—µ¼z ¬€Œ>Æ„̼ØÍ|5=ùlÝ,¤c–à”æ¤HÊÊœ½ž€H1~3†D¹dÚ­&\£f(0ÂÏŸ TyXoi0à²e$¥±N“fÁ¤¶â§î®6ÌÞMÂ$—[ïNÛ„¸ŠT#3„q˜òÇÕ¾¹¨cA¡Ðjq[ç*)£±ëÛÐe{7èc›/Ij1ä=’‚Ü+‡ÿ‰=¤’+óH¸H;-#‡¼z£ilü¨”B†GÑ"~nn2üÁKÉ?ëv ØÌ·fcX?Ûê—OŸ>E\z¾êþ{S/9m×®_{êú÷wrŸXõÔ˜€šò0ðŒ¸oCÚDMÖž­åß-›ˆäôˆ'&Muj‘æ1Ü1´fDJÊHáÉÎâÚA»ÍoN[›ƒKÂ]ÃÊæcÎ9sgz£#鑟:œÍ³ˆ' ]êSJùWCj÷›Ó@…–ZEÖ®= +»ŸJn„R£Û@~‚ï¬Ó¾jiÕNScU£Ö²ÿÏõÝúlhë…nòº°´ô؈(4{[<]F5Qkú¾ýâ:qgãܸªÓ¸…kÜ”)$ßÕ·&˜¾æ!Ï'dùpÝO³ñRÄœZój]¨ƒÅ–I³Ú¸øX¹eÐÊíù-¶ª" ¥‚2¼Gm€êæ ¶+Öl…Ê/f´†hÖ8@cZßç>zÀ¦Æ2þ k`ÏÒ‹aÜ‚ÈmKŸBÂÆÔi-”·,îѧ´þ½C›4<Ügãu lÝtVxÇ‚ôÆ×ÞUm¼ÔG5ó×§8$s s·Nü )„ÑÖÖG|`B†LÑwÜþøõ®ÓÌuSGš~Ø1syëúÃÀqDé~æy)ÜíÜUfZ÷ôwø±¤ü-+Úó st²dùªù´m'V„v-vÝâ£ÐÖÇtË[þ (ßCâ Z58(H º›R"]Bk)‘=„æ„HÁˆ·Í½Ó(÷Ÿs€î–:ÜÀA9nMî +W:¿’2¬ûžÍÅnYE½ ž=º20ÅÁ¤ÎG—I¨£È…cî/N¨Lœ¶ ÷ÒK+’P¤H,K+¢JLØK¿éÌ­äzÖ c½N‹…zÌ‹Jv´ +ï‚Ã,*Ç}[MÞÊŠn}5&¼=ó ')È:ÌÑ'A%¦¶œ‘§‹`x¶('¾XªGÜÃÛo?ó’ror¨‰mæAÖm·Úž:&I¬‰42­ƒ"èMUíN ¼BYT>îŸëp߃”Æ«²7<ߣŒÆ•¸À'q/˜õui}Ë07³<äèCPø½²?»›éÌ¿å’ÂÕy¾ÇrQýº”¯rÝp5Üüx̤ï´5jIü° JRdíðÉœKïøµ;†ežŠÎpérTc¸ü Õ~&zdV ãÿMÓÁÍ»˜¥H¹#ÈšlûèÌmÅXâÙ˜5‡Þû‰“u áÈœÊoj\×G\ÉZé å2=ïšµMNÕØÉóW-¦1a1É·„/þd½ZšÚ8‚ð_±§›2ÊÎk¹É1N)EH”ÊÁ¹B2 ,ùõéîéžÝÙ› ’v§g¦ßšKWEœl@¿PÖ?îϼô\‚¶·ä-¯aë ‹s~Ï9¹„'eKD·bä_7ÙO^ãZüŽRhÒ´Ïßd[oð !ïGêºÆqÂ# ºœç„29¯Åúþ8³ùFêjøè³s`$u¯¼ßwWŸÉ>ì©õŠ.¡‰4‘qÙ(•™ Cо2Ç®‚ÝwјQÔµÔu2r®‹^À+uÀ´)»VáL"%|«h˜.›ªûþ>Ú k!@$ïË]¨dâõÛuýè"ЖE%]vášT"z‚íU®±× (ŸD®«çOpr ™ñR”ð=–á£1ôDén åÇ4No/96º¸©ƒ]9;_ûqÌ«! Ö“*dò Ø9—â»™&Œ':ÖÙt;ÚŠr[®ó’>@Ñ„¨d&òŽw˜µ^§­VËu\˜/gÈUêüÝUZáä¬!ЊHrÂÛä„Ö…žËrÎ\ÙÅJ¶Q~'…1²â„RÂîPCYµÐ˜ä蕽JÒk®WÅ­æ:ÕñJ\­¹ÄqEá¢EA‹rÑe¿Uö×·ñ ¢å1R»¢ÕjÄ€Úà ¶÷îlûÐ*ÁÈ–oºN-Er­¼¸Å +Õ­[»ðÐç…!Ȉ‰ÈUûeËÚ‚~¼%å·ÊÖKv7ÙvŠ3||Çíµá§ëp±áÅ7¸¡ihw:µa¨:ûb‚ «š (Õh²dÀÂÊK Së `!?w¶ûlHfÇ;ÐC(5Š +ãŒKà ûÂVÐÍVØÑH¼Öl›™Vu ˆ_¨¾_ÆšUDÃSRqƒCÕUÛÎ2ò=Fq;„¹;6^Ëtu&>’bXÇe@]§Á¿…~馧_°éí‘XºyË1ß@„K ÞúŽ}ä:WÄ÷÷˜Oª!ìŒvëž>™;†²Ê$ÝŒTDù€6þ/—¤8Y‡Òg£·”þm¶Øøö¢‡¬é‹c|Zó +åݲãßØØ¸×¬®S}T¥åë ×HrE6JNCP/}·”|\óð¤²<‹a1ÜÞ—¥ðÝeN½¹¯ðïl›«ÆsO<7N¥”ܺÛ/íÏÕE†ç àß¼8Ù²kÐëÀ™ŽÆÖHUÊE-ð07ÌVÙ"®O¯Oƒ³zÂ8ðCÖ>±8NŸº {Ú6’,Îz¡ÃIäñ©ˆslšÝÂ{þbb¿2Ĭ]sBb?Ìç.SÙ|è°h¹P•¶§ÂR¢Øš¾%¾á÷«Î®ÖïJES¾V!H…º£dcÕ'_býN¦”¤ÏÕqn°¾‡ø²zò§Ö3 ;MÄ®p¾ Ÿ¶0ýËoLpo“ ¨u˜to„æþ ØEåλ3œ8?ʽÉ8Ä`9\fSþæ'y‰7æ„b²ôæÜµ ù%¼v|_„÷W!è{ÌÒq…±=’žÎ“‰u¨¼äþð$½k£} UT°ŽG¼Ï‡ÉÆ7…ML[-R´Ž*µ< ÞÅõÊ ³­'þzˆåt’e+ŸñÁ4zëìnHû\C–i;ÂC¢?1»-"Ÿæ´ÃŒËòó±gÔ)@öTN‚ü÷œŽsïRÅ3 SðV~§Ž—ëý…ÜʨhísðŽm:gÿïV²˜ÈTw0¢`Œd@ÿ÷¼4A™Ë«ïF^µÁ…ο{E€Ußš´#M{´û‚G±üûÃt,hvÄŽi½w¡,ÁåYìqÌË1öÎö6'VÖ#ÂöYˆèRÞ®ùNŸòù¢Ó„n„§b™Xµù¹÷W«‡Íe›Z-©ý¼§<ã@ñ&eÓ4™²(*!£7€5 4ÌEöÖ´ÈnöˆWê×ë½Ó½\´8¼ _¹¬qóU‘mYÁ:ma‡Â¸±èøøÙèn z \D È^@G7M1y.v™ÆÖššV7p4W nÞowô*^i\f4`b »¤¥¶IZþ`à h +endstream +endobj +62 0 obj +<< +/K [682 0 R 63 0 R 64 0 R 65 0 R 66 0 R 683 0 R 93 0 R 94 0 R 95 0 R 96 0 R +97 0 R 684 0 R 228 0 R 685 0 R 338 0 R 339 0 R 340 0 R 341 0 R 342 0 R 343 0 R +686 0 R 461 0 R 687 0 R 495 0 R 496 0 R 688 0 R 549 0 R 550 0 R 551 0 R 552 0 R +689 0 R 581 0 R 582 0 R 583 0 R 584 0 R 597 0 R 598 0 R 585 0 R 586 0 R 587 0 R +588 0 R 589 0 R 590 0 R 591 0 R 599 0 R 600 0 R 601 0 R 602 0 R 592 0 R 593 0 R +594 0 R 595 0 R 596 0 R 611 0 R 612 0 R 613 0 R 614 0 R 615 0 R 616 0 R 617 0 R +618 0 R 619 0 R 620 0 R 621 0 R 622 0 R 623 0 R 624 0 R 625 0 R 626 0 R 627 0 R +628 0 R 629 0 R 630 0 R 652 0 R 631 0 R 656 0 R 632 0 R 633 0 R 653 0 R 634 0 R +635 0 R 636 0 R 655 0 R 637 0 R 654 0 R 638 0 R 657 0 R 639 0 R 640 0 R 658 0 R +641 0 R 642 0 R 643 0 R 644 0 R 645 0 R 650 0 R 646 0 R 647 0 R 648 0 R 651 0 R +649 0 R] +/P 27 0 R +/S /Sect +>> +endobj +63 0 obj +<< +/K 0 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /H2 +>> +endobj +64 0 obj +<< +/K 1 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /H2 +>> +endobj +65 0 obj +<< +/K 2 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /H1 +>> +endobj +66 0 obj +<< +/K 3 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /H2 +>> +endobj +67 0 obj +<< +/K 4 +/Lang (EN-US) +/P 690 0 R +/Pg 21 0 R +/S /P +>> +endobj +68 0 obj +<< +/K 5 +/Lang (EN-US) +/P 691 0 R +/Pg 21 0 R +/S /P +>> +endobj +69 0 obj +<< +/K 6 +/Pg 21 0 R +/S /Artifact +>> +endobj +70 0 obj +<< +/K 7 +/Pg 21 0 R +/S /Artifact +>> +endobj +71 0 obj +<< +/K 13 +/Pg 21 0 R +/S /Artifact +>> +endobj +72 0 obj +<< +/K 14 +/Pg 21 0 R +/S /Artifact +>> +endobj +73 0 obj +<< +/K 16 +/Pg 21 0 R +/S /Artifact +>> +endobj +74 0 obj +<< +/K 17 +/Lang (EN-US) +/P 692 0 R +/Pg 21 0 R +/S /P +>> +endobj +75 0 obj +<< +/K 18 +/Lang (EN-US) +/P 693 0 R +/Pg 21 0 R +/S /P +>> +endobj +76 0 obj +<< +/K 19 +/Pg 21 0 R +/S /Artifact +>> +endobj +77 0 obj +<< +/K 20 +/Lang (EN-US) +/P 694 0 R +/Pg 21 0 R +/S /P +>> +endobj +78 0 obj +<< +/K 21 +/Lang (EN-US) +/P 695 0 R +/Pg 21 0 R +/S /P +>> +endobj +79 0 obj +<< +/K 22 +/Pg 21 0 R +/S /Artifact +>> +endobj +80 0 obj +<< +/K 23 +/Pg 21 0 R +/S /Artifact +>> +endobj +81 0 obj +<< +/K 25 +/Pg 21 0 R +/S /Artifact +>> +endobj +82 0 obj +<< +/K 26 +/Lang (EN-US) +/P 696 0 R +/Pg 21 0 R +/S /P +>> +endobj +83 0 obj +<< +/K 27 +/Lang (EN-US) +/P 697 0 R +/Pg 21 0 R +/S /P +>> +endobj +84 0 obj +<< +/K 28 +/Pg 21 0 R +/S /Artifact +>> +endobj +85 0 obj +<< +/K 29 +/Lang (EN-US) +/P 698 0 R +/Pg 21 0 R +/S /P +>> +endobj +86 0 obj +<< +/K 30 +/Lang (EN-US) +/P 699 0 R +/Pg 21 0 R +/S /P +>> +endobj +87 0 obj +<< +/K 31 +/Pg 21 0 R +/S /Artifact +>> +endobj +88 0 obj +<< +/K 32 +/Pg 21 0 R +/S /Artifact +>> +endobj +89 0 obj +<< +/K 34 +/Pg 21 0 R +/S /Artifact +>> +endobj +90 0 obj +<< +/K 35 +/Lang (EN-US) +/P 700 0 R +/Pg 21 0 R +/S /P +>> +endobj +91 0 obj +<< +/K 36 +/Lang (EN-US) +/P 701 0 R +/Pg 21 0 R +/S /P +>> +endobj +92 0 obj +<< +/K 37 +/Pg 21 0 R +/S /Artifact +>> +endobj +93 0 obj +<< +/K 38 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /P +>> +endobj +94 0 obj +<< +/K 39 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /P +>> +endobj +95 0 obj +<< +/K 40 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /P +>> +endobj +96 0 obj +<< +/K 41 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /H2 +>> +endobj +97 0 obj +<< +/K 42 +/Lang (EN-US) +/P 62 0 R +/Pg 21 0 R +/S /P +>> +endobj +98 0 obj +<< +/K 43 +/Lang (EN-US) +/P 702 0 R +/Pg 21 0 R +/S /P +>> +endobj +99 0 obj +<< +/K 44 +/Lang (EN-US) +/P 703 0 R +/Pg 21 0 R +/S /P +>> +endobj +100 0 obj +<< +/K 45 +/Pg 21 0 R +/S /Artifact +>> +endobj +101 0 obj +<< +/K 46 +/Pg 21 0 R +/S /Artifact +>> +endobj +102 0 obj +<< +/K 52 +/Pg 21 0 R +/S /Artifact +>> +endobj +103 0 obj +<< +/K 53 +/Pg 21 0 R +/S /Artifact +>> +endobj +104 0 obj +<< +/K 55 +/Pg 21 0 R +/S /Artifact +>> +endobj +105 0 obj +<< +/K 58 +/Pg 21 0 R +/S /Artifact +>> +endobj +106 0 obj +<< +/K 59 +/Pg 21 0 R +/S /Artifact +>> +endobj +107 0 obj +<< +/K 65 +/Pg 21 0 R +/S /Artifact +>> +endobj +108 0 obj +<< +/K 67 +/Pg 21 0 R +/S /Artifact +>> +endobj +109 0 obj +<< +/K 72 +/Lang (EN-US) +/P 704 0 R +/Pg 21 0 R +/S /P +>> +endobj +110 0 obj +<< +/K 75 +/Pg 21 0 R +/S /Artifact +>> +endobj +111 0 obj +<< +/K 80 +/Lang (EN-US) +/P 705 0 R +/Pg 21 0 R +/S /P +>> +endobj +112 0 obj +<< +/K 82 +/Pg 21 0 R +/S /Artifact +>> +endobj +113 0 obj +<< +/K 84 +/Pg 21 0 R +/S /Artifact +>> +endobj +114 0 obj +<< +/K 88 +/Lang (EN-US) +/P 706 0 R +/Pg 21 0 R +/S /P +>> +endobj +115 0 obj +<< +/K 89 +/Pg 21 0 R +/S /Artifact +>> +endobj +116 0 obj +<< +/K 91 +/Lang (EN-US) +/P 707 0 R +/Pg 21 0 R +/S /P +>> +endobj +117 0 obj +<< +/K 92 +/Pg 21 0 R +/S /Artifact +>> +endobj +118 0 obj +<< +/K 94 +/Pg 21 0 R +/S /Artifact +>> +endobj +119 0 obj +<< +/K 98 +/Lang (EN-US) +/P 708 0 R +/Pg 21 0 R +/S /P +>> +endobj +120 0 obj +<< +/K 99 +/Pg 21 0 R +/S /Artifact +>> +endobj +121 0 obj +<< +/K 101 +/Lang (EN-US) +/P 709 0 R +/Pg 21 0 R +/S /P +>> +endobj +122 0 obj +<< +/K 102 +/Pg 21 0 R +/S /Artifact +>> +endobj +123 0 obj +<< +/K 104 +/Pg 21 0 R +/S /Artifact +>> +endobj +124 0 obj +<< +/K 108 +/Lang (EN-US) +/P 710 0 R +/Pg 21 0 R +/S /P +>> +endobj +125 0 obj +<< +/K 109 +/Pg 21 0 R +/S /Artifact +>> +endobj +126 0 obj +<< +/K 111 +/Lang (EN-US) +/P 711 0 R +/Pg 21 0 R +/S /P +>> +endobj +127 0 obj +<< +/K 112 +/Pg 21 0 R +/S /Artifact +>> +endobj +128 0 obj +<< +/K 114 +/Pg 21 0 R +/S /Artifact +>> +endobj +129 0 obj +<< +/K 118 +/Lang (EN-US) +/P 712 0 R +/Pg 21 0 R +/S /P +>> +endobj +130 0 obj +<< +/K 119 +/Lang (EN-US) +/P 713 0 R +/Pg 21 0 R +/S /P +>> +endobj +131 0 obj +<< +/K 120 +/Lang (EN-US) +/P 714 0 R +/Pg 21 0 R +/S /P +>> +endobj +132 0 obj +<< +/K 121 +/Lang (EN-US) +/P 715 0 R +/Pg 21 0 R +/S /P +>> +endobj +133 0 obj +<< +/K 122 +/Lang (EN-US) +/P 716 0 R +/Pg 21 0 R +/S /P +>> +endobj +134 0 obj +<< +/K 123 +/Pg 21 0 R +/S /Artifact +>> +endobj +135 0 obj +<< +/K 125 +/Pg 21 0 R +/S /Artifact +>> +endobj +136 0 obj +<< +/K 129 +/Lang (EN-US) +/P 717 0 R +/Pg 21 0 R +/S /P +>> +endobj +137 0 obj +<< +/K 130 +/Pg 21 0 R +/S /Artifact +>> +endobj +138 0 obj +<< +/K 132 +/Lang (EN-US) +/P 718 0 R +/Pg 21 0 R +/S /P +>> +endobj +139 0 obj +<< +/K 133 +/Pg 21 0 R +/S /Artifact +>> +endobj +140 0 obj +<< +/K 135 +/Pg 21 0 R +/S /Artifact +>> +endobj +141 0 obj +<< +/K 139 +/Lang (EN-US) +/P 719 0 R +/Pg 21 0 R +/S /P +>> +endobj +142 0 obj +<< +/K 140 +/Lang (EN-US) +/P 720 0 R +/Pg 21 0 R +/S /P +>> +endobj +143 0 obj +<< +/K 141 +/Pg 21 0 R +/S /Artifact +>> +endobj +144 0 obj +<< +/K 143 +/Lang (EN-US) +/P 721 0 R +/Pg 21 0 R +/S /P +>> +endobj +145 0 obj +<< +/K 144 +/Lang (EN-US) +/P 722 0 R +/Pg 21 0 R +/S /P +>> +endobj +146 0 obj +<< +/K 145 +/Pg 21 0 R +/S /Artifact +>> +endobj +147 0 obj +<< +/K 147 +/Pg 21 0 R +/S /Artifact +>> +endobj +148 0 obj +<< +/K 151 +/Lang (EN-US) +/P 723 0 R +/Pg 21 0 R +/S /P +>> +endobj +149 0 obj +<< +/K 152 +/Lang (EN-US) +/P 724 0 R +/Pg 21 0 R +/S /P +>> +endobj +150 0 obj +<< +/K 153 +/Lang (EN-US) +/P 725 0 R +/Pg 21 0 R +/S /P +>> +endobj +151 0 obj +<< +/K 154 +/Pg 21 0 R +/S /Artifact +>> +endobj +152 0 obj +<< +/K 155 +/Lang (EN-US) +/P 726 0 R +/Pg 21 0 R +/S /P +>> +endobj +153 0 obj +<< +/K 156 +/Lang (EN-US) +/P 727 0 R +/Pg 21 0 R +/S /P +>> +endobj +154 0 obj +<< +/K 157 +/Lang (EN-US) +/P 728 0 R +/Pg 21 0 R +/S /P +>> +endobj +155 0 obj +<< +/K [158 159] +/Lang (EN-US) +/P 729 0 R +/Pg 21 0 R +/S /P +>> +endobj +156 0 obj +<< +/K 160 +/Lang (EN-US) +/P 730 0 R +/Pg 21 0 R +/S /P +>> +endobj +157 0 obj +<< +/K 161 +/Lang (EN-US) +/P 731 0 R +/Pg 21 0 R +/S /P +>> +endobj +158 0 obj +<< +/K 162 +/Lang (EN-US) +/P 732 0 R +/Pg 21 0 R +/S /P +>> +endobj +159 0 obj +<< +/K 163 +/Lang (EN-US) +/P 733 0 R +/Pg 21 0 R +/S /P +>> +endobj +160 0 obj +<< +/K 164 +/Lang (EN-US) +/P 734 0 R +/Pg 21 0 R +/S /P +>> +endobj +161 0 obj +<< +/K 165 +/Lang (EN-US) +/P 735 0 R +/Pg 21 0 R +/S /P +>> +endobj +162 0 obj +<< +/K 166 +/Lang (EN-US) +/P 736 0 R +/Pg 21 0 R +/S /P +>> +endobj +163 0 obj +<< +/K [167 168] +/Lang (EN-US) +/P 737 0 R +/Pg 21 0 R +/S /P +>> +endobj +164 0 obj +<< +/K [169 170 171] +/Lang (EN-US) +/P 738 0 R +/Pg 21 0 R +/S /P +>> +endobj +165 0 obj +<< +/K [172 173] +/Lang (EN-US) +/P 739 0 R +/Pg 21 0 R +/S /P +>> +endobj +166 0 obj +<< +/K 174 +/Lang (EN-US) +/P 740 0 R +/Pg 21 0 R +/S /LBody +>> +endobj +167 0 obj +<< +/K 175 +/Lang (EN-US) +/P 741 0 R +/Pg 21 0 R +/S /P +>> +endobj +168 0 obj +<< +/K 176 +/Lang (EN-US) +/P 742 0 R +/Pg 21 0 R +/S /P +>> +endobj +169 0 obj +<< +/K 177 +/Lang (EN-US) +/P 743 0 R +/Pg 21 0 R +/S /P +>> +endobj +170 0 obj +<< +/K 178 +/Lang (EN-US) +/P 744 0 R +/Pg 21 0 R +/S /LBody +>> +endobj +171 0 obj +<< +/K 179 +/Lang (EN-US) +/P 745 0 R +/Pg 21 0 R +/S /P +>> +endobj +172 0 obj +<< +/K 180 +/Lang (EN-US) +/P 746 0 R +/Pg 21 0 R +/S /P +>> +endobj +173 0 obj +<< +/K 181 +/Lang (EN-US) +/P 746 0 R +/Pg 21 0 R +/S /P +>> +endobj +174 0 obj +<< +/K 182 +/Lang (EN-US) +/P 747 0 R +/Pg 21 0 R +/S /LBody +>> +endobj +175 0 obj +<< +/K 0 +/Lang (EN-US) +/P 748 0 R +/Pg 22 0 R +/S /P +>> +endobj +176 0 obj +<< +/K 1 +/Lang (EN-US) +/P 749 0 R +/Pg 22 0 R +/S /P +>> +endobj +177 0 obj +<< +/K 2 +/Pg 22 0 R +/S /Artifact +>> +endobj +178 0 obj +<< +/K 3 +/Lang (EN-US) +/P 750 0 R +/Pg 22 0 R +/S /P +>> +endobj +179 0 obj +<< +/K 4 +/Lang (EN-US) +/P 751 0 R +/Pg 22 0 R +/S /P +>> +endobj +180 0 obj +<< +/K 5 +/Pg 22 0 R +/S /Artifact +>> +endobj +181 0 obj +<< +/K 6 +/Lang (EN-US) +/P 752 0 R +/Pg 22 0 R +/S /P +>> +endobj +182 0 obj +<< +/K 7 +/Lang (EN-US) +/P 753 0 R +/Pg 22 0 R +/S /P +>> +endobj +183 0 obj +<< +/K 8 +/Pg 22 0 R +/S /Artifact +>> +endobj +184 0 obj +<< +/K 9 +/Lang (EN-US) +/P 754 0 R +/Pg 22 0 R +/S /P +>> +endobj +185 0 obj +<< +/K 10 +/Lang (EN-US) +/P 755 0 R +/Pg 22 0 R +/S /P +>> +endobj +186 0 obj +<< +/K 11 +/Pg 22 0 R +/S /Artifact +>> +endobj +187 0 obj +<< +/K 12 +/Lang (EN-US) +/P 756 0 R +/Pg 22 0 R +/S /P +>> +endobj +188 0 obj +<< +/K 13 +/Lang (EN-US) +/P 757 0 R +/Pg 22 0 R +/S /P +>> +endobj +189 0 obj +<< +/K 14 +/Pg 22 0 R +/S /Artifact +>> +endobj +190 0 obj +<< +/K 15 +/Lang (EN-US) +/P 758 0 R +/Pg 22 0 R +/S /P +>> +endobj +191 0 obj +<< +/K 16 +/Lang (EN-US) +/P 759 0 R +/Pg 22 0 R +/S /P +>> +endobj +192 0 obj +<< +/K 17 +/Pg 22 0 R +/S /Artifact +>> +endobj +193 0 obj +<< +/K 18 +/Lang (EN-US) +/P 760 0 R +/Pg 22 0 R +/S /P +>> +endobj +194 0 obj +<< +/K 19 +/Lang (EN-US) +/P 761 0 R +/Pg 22 0 R +/S /P +>> +endobj +195 0 obj +<< +/K 20 +/Pg 22 0 R +/S /Artifact +>> +endobj +196 0 obj +<< +/K 21 +/Lang (EN-US) +/P 762 0 R +/Pg 22 0 R +/S /P +>> +endobj +197 0 obj +<< +/K 22 +/Lang (EN-US) +/P 763 0 R +/Pg 22 0 R +/S /P +>> +endobj +198 0 obj +<< +/K 23 +/Pg 22 0 R +/S /Artifact +>> +endobj +199 0 obj +<< +/K 24 +/Pg 22 0 R +/S /Artifact +>> +endobj +200 0 obj +<< +/K 30 +/Pg 22 0 R +/S /Artifact +>> +endobj +201 0 obj +<< +/K 31 +/Lang (EN-US) +/P 764 0 R +/Pg 22 0 R +/S /P +>> +endobj +202 0 obj +<< +/K 32 +/Lang (EN-US) +/P 765 0 R +/Pg 22 0 R +/S /P +>> +endobj +203 0 obj +<< +/K 33 +/Pg 22 0 R +/S /Artifact +>> +endobj +204 0 obj +<< +/K 34 +/Lang (EN-US) +/P 766 0 R +/Pg 22 0 R +/S /P +>> +endobj +205 0 obj +<< +/K 35 +/Lang (EN-US) +/P 767 0 R +/Pg 22 0 R +/S /P +>> +endobj +206 0 obj +<< +/K 36 +/Pg 22 0 R +/S /Artifact +>> +endobj +207 0 obj +<< +/K 37 +/Lang (EN-US) +/P 768 0 R +/Pg 22 0 R +/S /P +>> +endobj +208 0 obj +<< +/K 38 +/Lang (EN-US) +/P 769 0 R +/Pg 22 0 R +/S /P +>> +endobj +209 0 obj +<< +/K 39 +/Pg 22 0 R +/S /Artifact +>> +endobj +210 0 obj +<< +/K 40 +/Lang (EN-US) +/P 770 0 R +/Pg 22 0 R +/S /P +>> +endobj +211 0 obj +<< +/K 41 +/Lang (EN-US) +/P 771 0 R +/Pg 22 0 R +/S /P +>> +endobj +212 0 obj +<< +/K 42 +/Pg 22 0 R +/S /Artifact +>> +endobj +213 0 obj +<< +/K 43 +/Lang (EN-US) +/P 772 0 R +/Pg 22 0 R +/S /P +>> +endobj +214 0 obj +<< +/K 44 +/Lang (EN-US) +/P 773 0 R +/Pg 22 0 R +/S /P +>> +endobj +215 0 obj +<< +/K 45 +/Pg 22 0 R +/S /Artifact +>> +endobj +216 0 obj +<< +/K 46 +/Lang (EN-US) +/P 774 0 R +/Pg 22 0 R +/S /P +>> +endobj +217 0 obj +<< +/K 47 +/Lang (EN-US) +/P 775 0 R +/Pg 22 0 R +/S /P +>> +endobj +218 0 obj +<< +/K 48 +/Pg 22 0 R +/S /Artifact +>> +endobj +219 0 obj +<< +/K 49 +/Lang (EN-US) +/P 776 0 R +/Pg 22 0 R +/S /P +>> +endobj +220 0 obj +<< +/K 50 +/Lang (EN-US) +/P 777 0 R +/Pg 22 0 R +/S /P +>> +endobj +221 0 obj +<< +/K 51 +/Pg 22 0 R +/S /Artifact +>> +endobj +222 0 obj +<< +/K 52 +/Lang (EN-US) +/P 778 0 R +/Pg 22 0 R +/S /P +>> +endobj +223 0 obj +<< +/K 53 +/Lang (EN-US) +/P 779 0 R +/Pg 22 0 R +/S /P +>> +endobj +224 0 obj +<< +/K 54 +/Pg 22 0 R +/S /Artifact +>> +endobj +225 0 obj +<< +/K 55 +/Lang (EN-US) +/P 780 0 R +/Pg 22 0 R +/S /P +>> +endobj +226 0 obj +<< +/K 56 +/Lang (EN-US) +/P 781 0 R +/Pg 22 0 R +/S /P +>> +endobj +227 0 obj +<< +/K 57 +/Pg 22 0 R +/S /Artifact +>> +endobj +228 0 obj +<< +/K 58 +/Lang (EN-US) +/P 62 0 R +/Pg 22 0 R +/S /H2 +>> +endobj +229 0 obj +<< +/K 59 +/Pg 22 0 R +/S /Artifact +>> +endobj +230 0 obj +<< +/K 65 +/Pg 22 0 R +/S /Artifact +>> +endobj +231 0 obj +<< +/K 66 +/Pg 22 0 R +/S /Artifact +>> +endobj +232 0 obj +<< +/K 68 +/Pg 22 0 R +/S /Artifact +>> +endobj +233 0 obj +<< +/K 70 +/Pg 22 0 R +/S /Artifact +>> +endobj +234 0 obj +<< +/K 71 +/Pg 22 0 R +/S /Artifact +>> +endobj +235 0 obj +<< +/K 73 +/Pg 22 0 R +/S /Artifact +>> +endobj +236 0 obj +<< +/K 75 +/Pg 22 0 R +/S /Artifact +>> +endobj +237 0 obj +<< +/K 76 +/Pg 22 0 R +/S /Artifact +>> +endobj +238 0 obj +<< +/K 78 +/Pg 22 0 R +/S /Artifact +>> +endobj +239 0 obj +<< +/K 80 +/Pg 22 0 R +/S /Artifact +>> +endobj +240 0 obj +<< +/K 81 +/Pg 22 0 R +/S /Artifact +>> +endobj +241 0 obj +<< +/K 83 +/Pg 22 0 R +/S /Artifact +>> +endobj +242 0 obj +<< +/K 85 +/Pg 22 0 R +/S /Artifact +>> +endobj +243 0 obj +<< +/K 86 +/Pg 22 0 R +/S /Artifact +>> +endobj +244 0 obj +<< +/K 92 +/Pg 22 0 R +/S /Artifact +>> +endobj +245 0 obj +<< +/K 93 +/Pg 22 0 R +/S /Artifact +>> +endobj +246 0 obj +<< +/K 95 +/Pg 22 0 R +/S /Artifact +>> +endobj +247 0 obj +<< +/K 97 +/Pg 22 0 R +/S /Artifact +>> +endobj +248 0 obj +<< +/K 98 +/Pg 22 0 R +/S /Artifact +>> +endobj +249 0 obj +<< +/K 100 +/Pg 22 0 R +/S /Artifact +>> +endobj +250 0 obj +<< +/K 102 +/Pg 22 0 R +/S /Artifact +>> +endobj +251 0 obj +<< +/K 103 +/Pg 22 0 R +/S /Artifact +>> +endobj +252 0 obj +<< +/K 105 +/Pg 22 0 R +/S /Artifact +>> +endobj +253 0 obj +<< +/K 107 +/Pg 22 0 R +/S /Artifact +>> +endobj +254 0 obj +<< +/K 108 +/Pg 22 0 R +/S /Artifact +>> +endobj +255 0 obj +<< +/K 114 +/Pg 22 0 R +/S /Artifact +>> +endobj +256 0 obj +<< +/K 115 +/Pg 22 0 R +/S /Artifact +>> +endobj +257 0 obj +<< +/K 117 +/Pg 22 0 R +/S /Artifact +>> +endobj +258 0 obj +<< +/K 119 +/Pg 22 0 R +/S /Artifact +>> +endobj +259 0 obj +<< +/K 120 +/Pg 22 0 R +/S /Artifact +>> +endobj +260 0 obj +<< +/K 122 +/Pg 22 0 R +/S /Artifact +>> +endobj +261 0 obj +<< +/K 124 +/Pg 22 0 R +/S /Artifact +>> +endobj +262 0 obj +<< +/K 125 +/Pg 22 0 R +/S /Artifact +>> +endobj +263 0 obj +<< +/K 127 +/Pg 22 0 R +/S /Artifact +>> +endobj +264 0 obj +<< +/K 129 +/Pg 22 0 R +/S /Artifact +>> +endobj +265 0 obj +<< +/K 130 +/Pg 22 0 R +/S /Artifact +>> +endobj +266 0 obj +<< +/K 132 +/Pg 22 0 R +/S /Artifact +>> +endobj +267 0 obj +<< +/K 134 +/Pg 22 0 R +/S /Artifact +>> +endobj +268 0 obj +<< +/K 135 +/Pg 22 0 R +/S /Artifact +>> +endobj +269 0 obj +<< +/K 137 +/Pg 22 0 R +/S /Artifact +>> +endobj +270 0 obj +<< +/K 139 +/Pg 22 0 R +/S /Artifact +>> +endobj +271 0 obj +<< +/K 140 +/Pg 22 0 R +/S /Artifact +>> +endobj +272 0 obj +<< +/K 142 +/Pg 22 0 R +/S /Artifact +>> +endobj +273 0 obj +<< +/K 144 +/Pg 22 0 R +/S /Artifact +>> +endobj +274 0 obj +<< +/K 145 +/Pg 22 0 R +/S /Artifact +>> +endobj +275 0 obj +<< +/K 147 +/Pg 22 0 R +/S /Artifact +>> +endobj +276 0 obj +<< +/K 149 +/Pg 22 0 R +/S /Artifact +>> +endobj +277 0 obj +<< +/K 150 +/Pg 22 0 R +/S /Artifact +>> +endobj +278 0 obj +<< +/K 156 +/Pg 22 0 R +/S /Artifact +>> +endobj +279 0 obj +<< +/K 157 +/Lang (EN-US) +/P 782 0 R +/Pg 22 0 R +/S /P +>> +endobj +280 0 obj +<< +/K 158 +/Lang (EN-US) +/P 783 0 R +/Pg 22 0 R +/S /P +>> +endobj +281 0 obj +<< +/K 159 +/Pg 22 0 R +/S /Artifact +>> +endobj +282 0 obj +<< +/K 160 +/Lang (EN-US) +/P 784 0 R +/Pg 22 0 R +/S /P +>> +endobj +283 0 obj +<< +/K 161 +/Lang (EN-US) +/P 785 0 R +/Pg 22 0 R +/S /P +>> +endobj +284 0 obj +<< +/K 162 +/Pg 22 0 R +/S /Artifact +>> +endobj +285 0 obj +<< +/K 163 +/Lang (EN-US) +/P 786 0 R +/Pg 22 0 R +/S /P +>> +endobj +286 0 obj +<< +/K 164 +/Lang (EN-US) +/P 787 0 R +/Pg 22 0 R +/S /P +>> +endobj +287 0 obj +<< +/K 165 +/Pg 22 0 R +/S /Artifact +>> +endobj +288 0 obj +<< +/K 166 +/Lang (EN-US) +/P 788 0 R +/Pg 22 0 R +/S /LBody +>> +endobj +289 0 obj +<< +/K 167 +/Lang (EN-US) +/P 789 0 R +/Pg 22 0 R +/S /LBody +>> +endobj +290 0 obj +<< +/K 168 +/Lang (EN-US) +/P 790 0 R +/Pg 22 0 R +/S /P +>> +endobj +291 0 obj +<< +/K 169 +/Lang (EN-US) +/P 791 0 R +/Pg 22 0 R +/S /P +>> +endobj +292 0 obj +<< +/K 170 +/Lang (EN-US) +/P 792 0 R +/Pg 22 0 R +/S /P +>> +endobj +293 0 obj +<< +/K 171 +/Lang (EN-US) +/P 793 0 R +/Pg 22 0 R +/S /P +>> +endobj +294 0 obj +<< +/K 172 +/Lang (EN-US) +/P 794 0 R +/Pg 22 0 R +/S /P +>> +endobj +295 0 obj +<< +/K 173 +/Lang (EN-US) +/P 795 0 R +/Pg 22 0 R +/S /P +>> +endobj +296 0 obj +<< +/K 174 +/Lang (EN-US) +/P 796 0 R +/Pg 22 0 R +/S /P +>> +endobj +297 0 obj +<< +/K 175 +/Lang (EN-US) +/P 797 0 R +/Pg 22 0 R +/S /P +>> +endobj +298 0 obj +<< +/K 176 +/Lang (EN-US) +/P 798 0 R +/Pg 22 0 R +/S /LBody +>> +endobj +299 0 obj +<< +/K 177 +/Lang (EN-US) +/P 799 0 R +/Pg 22 0 R +/S /P +>> +endobj +300 0 obj +<< +/K 178 +/Lang (EN-US) +/P 800 0 R +/Pg 22 0 R +/S /P +>> +endobj +301 0 obj +<< +/K 179 +/Lang (EN-US) +/P 801 0 R +/Pg 22 0 R +/S /P +>> +endobj +302 0 obj +<< +/K 180 +/Lang (EN-US) +/P 802 0 R +/Pg 22 0 R +/S /P +>> +endobj +303 0 obj +<< +/K 181 +/Lang (EN-US) +/P 803 0 R +/Pg 22 0 R +/S /P +>> +endobj +304 0 obj +<< +/K 182 +/Lang (EN-US) +/P 804 0 R +/Pg 22 0 R +/S /P +>> +endobj +305 0 obj +<< +/K 183 +/Lang (EN-US) +/P 805 0 R +/Pg 22 0 R +/S /LBody +>> +endobj +306 0 obj +<< +/K 184 +/Lang (EN-US) +/P 806 0 R +/Pg 22 0 R +/S /P +>> +endobj +307 0 obj +<< +/K 185 +/Lang (EN-US) +/P 807 0 R +/Pg 22 0 R +/S /P +>> +endobj +308 0 obj +<< +/K 186 +/Lang (EN-US) +/P 808 0 R +/Pg 22 0 R +/S /P +>> +endobj +309 0 obj +<< +/K 187 +/Lang (EN-US) +/P 809 0 R +/Pg 22 0 R +/S /P +>> +endobj +310 0 obj +<< +/K 188 +/Lang (EN-US) +/P 810 0 R +/Pg 22 0 R +/S /P +>> +endobj +311 0 obj +<< +/K 189 +/Lang (EN-US) +/P 811 0 R +/Pg 22 0 R +/S /P +>> +endobj +312 0 obj +<< +/K 190 +/Lang (EN-US) +/P 812 0 R +/Pg 22 0 R +/S /P +>> +endobj +313 0 obj +<< +/K 191 +/Lang (EN-US) +/P 813 0 R +/Pg 22 0 R +/S /P +>> +endobj +314 0 obj +<< +/K 192 +/Lang (EN-US) +/P 814 0 R +/Pg 22 0 R +/S /P +>> +endobj +315 0 obj +<< +/K 193 +/Lang (EN-US) +/P 815 0 R +/Pg 22 0 R +/S /P +>> +endobj +316 0 obj +<< +/K 194 +/Lang (EN-US) +/P 816 0 R +/Pg 22 0 R +/S /P +>> +endobj +317 0 obj +<< +/K 195 +/Lang (EN-US) +/P 817 0 R +/Pg 22 0 R +/S /P +>> +endobj +318 0 obj +<< +/K 196 +/Lang (EN-US) +/P 818 0 R +/Pg 22 0 R +/S /P +>> +endobj +319 0 obj +<< +/K 197 +/Lang (EN-US) +/P 819 0 R +/Pg 22 0 R +/S /P +>> +endobj +320 0 obj +<< +/K 198 +/Lang (EN-US) +/P 820 0 R +/Pg 22 0 R +/S /LBody +>> +endobj +321 0 obj +<< +/K 0 +/Lang (EN-US) +/P 821 0 R +/Pg 23 0 R +/S /P +>> +endobj +322 0 obj +<< +/K 1 +/Lang (EN-US) +/P 822 0 R +/Pg 23 0 R +/S /P +>> +endobj +323 0 obj +<< +/K 2 +/Pg 23 0 R +/S /Artifact +>> +endobj +324 0 obj +<< +/K 3 +/Lang (EN-US) +/P 823 0 R +/Pg 23 0 R +/S /P +>> +endobj +325 0 obj +<< +/K 4 +/Lang (EN-US) +/P 824 0 R +/Pg 23 0 R +/S /P +>> +endobj +326 0 obj +<< +/K 5 +/Pg 23 0 R +/S /Artifact +>> +endobj +327 0 obj +<< +/K 6 +/Lang (EN-US) +/P 825 0 R +/Pg 23 0 R +/S /P +>> +endobj +328 0 obj +<< +/K 7 +/Lang (EN-US) +/P 826 0 R +/Pg 23 0 R +/S /P +>> +endobj +329 0 obj +<< +/K 8 +/Pg 23 0 R +/S /Artifact +>> +endobj +330 0 obj +<< +/K 9 +/Lang (EN-US) +/P 827 0 R +/Pg 23 0 R +/S /P +>> +endobj +331 0 obj +<< +/K 10 +/Lang (EN-US) +/P 828 0 R +/Pg 23 0 R +/S /P +>> +endobj +332 0 obj +<< +/K 11 +/Pg 23 0 R +/S /Artifact +>> +endobj +333 0 obj +<< +/K 12 +/Pg 23 0 R +/S /Artifact +>> +endobj +334 0 obj +<< +/K 14 +/Pg 23 0 R +/S /Artifact +>> +endobj +335 0 obj +<< +/K 15 +/Lang (EN-US) +/P 829 0 R +/Pg 23 0 R +/S /P +>> +endobj +336 0 obj +<< +/K 16 +/Lang (EN-US) +/P 830 0 R +/Pg 23 0 R +/S /P +>> +endobj +337 0 obj +<< +/K 17 +/Pg 23 0 R +/S /Artifact +>> +endobj +338 0 obj +<< +/K 18 +/Lang (EN-US) +/P 62 0 R +/Pg 23 0 R +/S /P +>> +endobj +339 0 obj +<< +/K 19 +/Lang (EN-US) +/P 62 0 R +/Pg 23 0 R +/S /P +>> +endobj +340 0 obj +<< +/K 20 +/Lang (EN-US) +/P 62 0 R +/Pg 23 0 R +/S /P +>> +endobj +341 0 obj +<< +/K 21 +/Lang (EN-US) +/P 62 0 R +/Pg 23 0 R +/S /P +>> +endobj +342 0 obj +<< +/K 22 +/Lang (EN-US) +/P 62 0 R +/Pg 23 0 R +/S /H2 +>> +endobj +343 0 obj +<< +/K 23 +/Lang (EN-US) +/P 62 0 R +/Pg 23 0 R +/S /P +>> +endobj +344 0 obj +<< +/K 24 +/Lang (EN-US) +/P 831 0 R +/Pg 23 0 R +/S /P +>> +endobj +345 0 obj +<< +/K 25 +/Lang (EN-US) +/P 832 0 R +/Pg 23 0 R +/S /P +>> +endobj +346 0 obj +<< +/K 26 +/Pg 23 0 R +/S /Artifact +>> +endobj +347 0 obj +<< +/K 27 +/Pg 23 0 R +/S /Artifact +>> +endobj +348 0 obj +<< +/K 33 +/Pg 23 0 R +/S /Artifact +>> +endobj +349 0 obj +<< +/K 34 +/Lang (EN-US) +/P 833 0 R +/Pg 23 0 R +/S /P +>> +endobj +350 0 obj +<< +/K 35 +/Lang (EN-US) +/P 834 0 R +/Pg 23 0 R +/S /P +>> +endobj +351 0 obj +<< +/K 36 +/Pg 23 0 R +/S /Artifact +>> +endobj +352 0 obj +<< +/K 37 +/Lang (EN-US) +/P 835 0 R +/Pg 23 0 R +/S /P +>> +endobj +353 0 obj +<< +/K 38 +/Lang (EN-US) +/P 836 0 R +/Pg 23 0 R +/S /P +>> +endobj +354 0 obj +<< +/K 39 +/Pg 23 0 R +/S /Artifact +>> +endobj +355 0 obj +<< +/K 40 +/Lang (EN-US) +/P 837 0 R +/Pg 23 0 R +/S /P +>> +endobj +356 0 obj +<< +/K 41 +/Lang (EN-US) +/P 838 0 R +/Pg 23 0 R +/S /P +>> +endobj +357 0 obj +<< +/K 42 +/Pg 23 0 R +/S /Artifact +>> +endobj +358 0 obj +<< +/K 43 +/Lang (EN-US) +/P 839 0 R +/Pg 23 0 R +/S /P +>> +endobj +359 0 obj +<< +/K 44 +/Lang (EN-US) +/P 840 0 R +/Pg 23 0 R +/S /P +>> +endobj +360 0 obj +<< +/K 45 +/Pg 23 0 R +/S /Artifact +>> +endobj +361 0 obj +<< +/K 46 +/Pg 23 0 R +/S /Artifact +>> +endobj +362 0 obj +<< +/K 52 +/Pg 23 0 R +/S /Artifact +>> +endobj +363 0 obj +<< +/K 53 +/Pg 23 0 R +/S /Artifact +>> +endobj +364 0 obj +<< +/K 55 +/Pg 23 0 R +/S /Artifact +>> +endobj +365 0 obj +<< +/K 57 +/Pg 23 0 R +/S /Artifact +>> +endobj +366 0 obj +<< +/K 58 +/Pg 23 0 R +/S /Artifact +>> +endobj +367 0 obj +<< +/K 60 +/Pg 23 0 R +/S /Artifact +>> +endobj +368 0 obj +<< +/K 62 +/Pg 23 0 R +/S /Artifact +>> +endobj +369 0 obj +<< +/K 63 +/Pg 23 0 R +/S /Artifact +>> +endobj +370 0 obj +<< +/K 65 +/Pg 23 0 R +/S /Artifact +>> +endobj +371 0 obj +<< +/K 67 +/Pg 23 0 R +/S /Artifact +>> +endobj +372 0 obj +<< +/K 68 +/Pg 23 0 R +/S /Artifact +>> +endobj +373 0 obj +<< +/K 74 +/Pg 23 0 R +/S /Artifact +>> +endobj +374 0 obj +<< +/K 75 +/Lang (EN-US) +/P 841 0 R +/Pg 23 0 R +/S /P +>> +endobj +375 0 obj +<< +/K 76 +/Lang (EN-US) +/P 842 0 R +/Pg 23 0 R +/S /P +>> +endobj +376 0 obj +<< +/K 77 +/Pg 23 0 R +/S /Artifact +>> +endobj +377 0 obj +<< +/K 78 +/Lang (EN-US) +/P 843 0 R +/Pg 23 0 R +/S /P +>> +endobj +378 0 obj +<< +/K 79 +/Lang (EN-US) +/P 844 0 R +/Pg 23 0 R +/S /P +>> +endobj +379 0 obj +<< +/K 80 +/Pg 23 0 R +/S /Artifact +>> +endobj +380 0 obj +<< +/K 81 +/Lang (EN-US) +/P 845 0 R +/Pg 23 0 R +/S /P +>> +endobj +381 0 obj +<< +/K 82 +/Lang (EN-US) +/P 846 0 R +/Pg 23 0 R +/S /P +>> +endobj +382 0 obj +<< +/K 83 +/Pg 23 0 R +/S /Artifact +>> +endobj +383 0 obj +<< +/K 84 +/Lang (EN-US) +/P 847 0 R +/Pg 23 0 R +/S /P +>> +endobj +384 0 obj +<< +/K 85 +/Lang (EN-US) +/P 848 0 R +/Pg 23 0 R +/S /P +>> +endobj +385 0 obj +<< +/K 86 +/Pg 23 0 R +/S /Artifact +>> +endobj +386 0 obj +<< +/K 87 +/Lang (EN-US) +/P 849 0 R +/Pg 23 0 R +/S /P +>> +endobj +387 0 obj +<< +/K 88 +/Lang (EN-US) +/P 850 0 R +/Pg 23 0 R +/S /P +>> +endobj +388 0 obj +<< +/K 89 +/Pg 23 0 R +/S /Artifact +>> +endobj +389 0 obj +<< +/K 90 +/Lang (EN-US) +/P 851 0 R +/Pg 23 0 R +/S /P +>> +endobj +390 0 obj +<< +/K 91 +/Lang (EN-US) +/P 852 0 R +/Pg 23 0 R +/S /P +>> +endobj +391 0 obj +<< +/K 92 +/Pg 23 0 R +/S /Artifact +>> +endobj +392 0 obj +<< +/K 93 +/Pg 23 0 R +/S /Artifact +>> +endobj +393 0 obj +<< +/K 99 +/Pg 23 0 R +/S /Artifact +>> +endobj +394 0 obj +<< +/K 100 +/Lang (EN-US) +/P 853 0 R +/Pg 23 0 R +/S /P +>> +endobj +395 0 obj +<< +/K 101 +/Lang (EN-US) +/P 854 0 R +/Pg 23 0 R +/S /P +>> +endobj +396 0 obj +<< +/K 102 +/Pg 23 0 R +/S /Artifact +>> +endobj +397 0 obj +<< +/K 103 +/Lang (EN-US) +/P 855 0 R +/Pg 23 0 R +/S /P +>> +endobj +398 0 obj +<< +/K 104 +/Lang (EN-US) +/P 856 0 R +/Pg 23 0 R +/S /P +>> +endobj +399 0 obj +<< +/K 105 +/Pg 23 0 R +/S /Artifact +>> +endobj +400 0 obj +<< +/K 106 +/Lang (EN-US) +/P 857 0 R +/Pg 23 0 R +/S /P +>> +endobj +401 0 obj +<< +/K 107 +/Lang (EN-US) +/P 858 0 R +/Pg 23 0 R +/S /P +>> +endobj +402 0 obj +<< +/K 108 +/Pg 23 0 R +/S /Artifact +>> +endobj +403 0 obj +<< +/K 109 +/Pg 23 0 R +/S /Artifact +>> +endobj +404 0 obj +<< +/K 115 +/Pg 23 0 R +/S /Artifact +>> +endobj +405 0 obj +<< +/K 116 +/Lang (EN-US) +/P 859 0 R +/Pg 23 0 R +/S /P +>> +endobj +406 0 obj +<< +/K 117 +/Lang (EN-US) +/P 860 0 R +/Pg 23 0 R +/S /P +>> +endobj +407 0 obj +<< +/K 118 +/Pg 23 0 R +/S /Artifact +>> +endobj +408 0 obj +<< +/K 119 +/Lang (EN-US) +/P 861 0 R +/Pg 23 0 R +/S /P +>> +endobj +409 0 obj +<< +/K 120 +/Lang (EN-US) +/P 862 0 R +/Pg 23 0 R +/S /P +>> +endobj +410 0 obj +<< +/K 121 +/Pg 23 0 R +/S /Artifact +>> +endobj +411 0 obj +<< +/K 122 +/Lang (EN-US) +/P 863 0 R +/Pg 23 0 R +/S /P +>> +endobj +412 0 obj +<< +/K 123 +/Lang (EN-US) +/P 864 0 R +/Pg 23 0 R +/S /LBody +>> +endobj +413 0 obj +<< +/K 124 +/Lang (EN-US) +/P 865 0 R +/Pg 23 0 R +/S /LBody +>> +endobj +414 0 obj +<< +/K 125 +/Lang (EN-US) +/P 866 0 R +/Pg 23 0 R +/S /P +>> +endobj +415 0 obj +<< +/K 126 +/Lang (EN-US) +/P 867 0 R +/Pg 23 0 R +/S /P +>> +endobj +416 0 obj +<< +/K 127 +/Lang (EN-US) +/P 868 0 R +/Pg 23 0 R +/S /P +>> +endobj +417 0 obj +<< +/K 128 +/Lang (EN-US) +/P 869 0 R +/Pg 23 0 R +/S /P +>> +endobj +418 0 obj +<< +/K 129 +/Lang (EN-US) +/P 870 0 R +/Pg 23 0 R +/S /P +>> +endobj +419 0 obj +<< +/K 130 +/Lang (EN-US) +/P 871 0 R +/Pg 23 0 R +/S /P +>> +endobj +420 0 obj +<< +/K 131 +/Lang (EN-US) +/P 872 0 R +/Pg 23 0 R +/S /LBody +>> +endobj +421 0 obj +<< +/K 132 +/Lang (EN-US) +/P 873 0 R +/Pg 23 0 R +/S /LBody +>> +endobj +422 0 obj +<< +/K 133 +/Lang (EN-US) +/P 874 0 R +/Pg 23 0 R +/S /LBody +>> +endobj +423 0 obj +<< +/K 0 +/Lang (EN-US) +/P 875 0 R +/Pg 24 0 R +/S /P +>> +endobj +424 0 obj +<< +/K 1 +/Lang (EN-US) +/P 876 0 R +/Pg 24 0 R +/S /P +>> +endobj +425 0 obj +<< +/K 2 +/Pg 24 0 R +/S /Artifact +>> +endobj +426 0 obj +<< +/K 3 +/Lang (EN-US) +/P 877 0 R +/Pg 24 0 R +/S /P +>> +endobj +427 0 obj +<< +/K 4 +/Lang (EN-US) +/P 878 0 R +/Pg 24 0 R +/S /P +>> +endobj +428 0 obj +<< +/K 5 +/Pg 24 0 R +/S /Artifact +>> +endobj +429 0 obj +<< +/K 6 +/Lang (EN-US) +/P 879 0 R +/Pg 24 0 R +/S /P +>> +endobj +430 0 obj +<< +/K 7 +/Lang (EN-US) +/P 880 0 R +/Pg 24 0 R +/S /P +>> +endobj +431 0 obj +<< +/K 8 +/Pg 24 0 R +/S /Artifact +>> +endobj +432 0 obj +<< +/K 9 +/Lang (EN-US) +/P 881 0 R +/Pg 24 0 R +/S /P +>> +endobj +433 0 obj +<< +/K 10 +/Lang (EN-US) +/P 882 0 R +/Pg 24 0 R +/S /P +>> +endobj +434 0 obj +<< +/K 11 +/Pg 24 0 R +/S /Artifact +>> +endobj +435 0 obj +<< +/K 12 +/Pg 24 0 R +/S /Artifact +>> +endobj +436 0 obj +<< +/K 18 +/Pg 24 0 R +/S /Artifact +>> +endobj +437 0 obj +<< +/K 19 +/Lang (EN-US) +/P 883 0 R +/Pg 24 0 R +/S /P +>> +endobj +438 0 obj +<< +/K 20 +/Lang (EN-US) +/P 884 0 R +/Pg 24 0 R +/S /P +>> +endobj +439 0 obj +<< +/K 21 +/Pg 24 0 R +/S /Artifact +>> +endobj +440 0 obj +<< +/K 22 +/Lang (EN-US) +/P 885 0 R +/Pg 24 0 R +/S /P +>> +endobj +441 0 obj +<< +/K 23 +/Lang (EN-US) +/P 886 0 R +/Pg 24 0 R +/S /P +>> +endobj +442 0 obj +<< +/K 24 +/Pg 24 0 R +/S /Artifact +>> +endobj +443 0 obj +<< +/K 25 +/Lang (EN-US) +/P 887 0 R +/Pg 24 0 R +/S /P +>> +endobj +444 0 obj +<< +/K 26 +/Lang (EN-US) +/P 888 0 R +/Pg 24 0 R +/S /P +>> +endobj +445 0 obj +<< +/K 27 +/Pg 24 0 R +/S /Artifact +>> +endobj +446 0 obj +<< +/K 28 +/Lang (EN-US) +/P 889 0 R +/Pg 24 0 R +/S /P +>> +endobj +447 0 obj +<< +/K 29 +/Lang (EN-US) +/P 890 0 R +/Pg 24 0 R +/S /P +>> +endobj +448 0 obj +<< +/K 30 +/Pg 24 0 R +/S /Artifact +>> +endobj +449 0 obj +<< +/K 31 +/Lang (EN-US) +/P 891 0 R +/Pg 24 0 R +/S /P +>> +endobj +450 0 obj +<< +/K 32 +/Lang (EN-US) +/P 892 0 R +/Pg 24 0 R +/S /P +>> +endobj +451 0 obj +<< +/K 33 +/Pg 24 0 R +/S /Artifact +>> +endobj +452 0 obj +<< +/K 34 +/Lang (EN-US) +/P 893 0 R +/Pg 24 0 R +/S /P +>> +endobj +453 0 obj +<< +/K 35 +/Lang (EN-US) +/P 894 0 R +/Pg 24 0 R +/S /P +>> +endobj +454 0 obj +<< +/K 36 +/Pg 24 0 R +/S /Artifact +>> +endobj +455 0 obj +<< +/K 37 +/Lang (EN-US) +/P 895 0 R +/Pg 24 0 R +/S /P +>> +endobj +456 0 obj +<< +/K 38 +/Lang (EN-US) +/P 896 0 R +/Pg 24 0 R +/S /P +>> +endobj +457 0 obj +<< +/K 39 +/Pg 24 0 R +/S /Artifact +>> +endobj +458 0 obj +<< +/K 40 +/Lang (EN-US) +/P 897 0 R +/Pg 24 0 R +/S /P +>> +endobj +459 0 obj +<< +/K 41 +/Lang (EN-US) +/P 898 0 R +/Pg 24 0 R +/S /P +>> +endobj +460 0 obj +<< +/K 42 +/Pg 24 0 R +/S /Artifact +>> +endobj +461 0 obj +<< +/K 43 +/Lang (EN-US) +/P 62 0 R +/Pg 24 0 R +/S /H2 +>> +endobj +462 0 obj +<< +/K 44 +/Pg 24 0 R +/S /Artifact +>> +endobj +463 0 obj +<< +/K 50 +/Pg 24 0 R +/S /Artifact +>> +endobj +464 0 obj +<< +/K 51 +/Lang (EN-US) +/P 899 0 R +/Pg 24 0 R +/S /P +>> +endobj +465 0 obj +<< +/K 52 +/Lang (EN-US) +/P 900 0 R +/Pg 24 0 R +/S /P +>> +endobj +466 0 obj +<< +/K 53 +/Pg 24 0 R +/S /Artifact +>> +endobj +467 0 obj +<< +/K 54 +/Lang (EN-US) +/P 901 0 R +/Pg 24 0 R +/S /P +>> +endobj +468 0 obj +<< +/K 55 +/Lang (EN-US) +/P 902 0 R +/Pg 24 0 R +/S /P +>> +endobj +469 0 obj +<< +/K 56 +/Pg 24 0 R +/S /Artifact +>> +endobj +470 0 obj +<< +/K 57 +/Lang (EN-US) +/P 903 0 R +/Pg 24 0 R +/S /P +>> +endobj +471 0 obj +<< +/K 58 +/Lang (EN-US) +/P 904 0 R +/Pg 24 0 R +/S /P +>> +endobj +472 0 obj +<< +/K 59 +/Pg 24 0 R +/S /Artifact +>> +endobj +473 0 obj +<< +/K 60 +/Lang (EN-US) +/P 905 0 R +/Pg 24 0 R +/S /P +>> +endobj +474 0 obj +<< +/K 61 +/Lang (EN-US) +/P 906 0 R +/Pg 24 0 R +/S /P +>> +endobj +475 0 obj +<< +/K 62 +/Pg 24 0 R +/S /Artifact +>> +endobj +476 0 obj +<< +/K 63 +/Lang (EN-US) +/P 907 0 R +/Pg 24 0 R +/S /P +>> +endobj +477 0 obj +<< +/K 64 +/Lang (EN-US) +/P 908 0 R +/Pg 24 0 R +/S /P +>> +endobj +478 0 obj +<< +/K 65 +/Pg 24 0 R +/S /Artifact +>> +endobj +479 0 obj +<< +/K 66 +/Pg 24 0 R +/S /Artifact +>> +endobj +480 0 obj +<< +/K 72 +/Pg 24 0 R +/S /Artifact +>> +endobj +481 0 obj +<< +/K 73 +/Lang (EN-US) +/P 909 0 R +/Pg 24 0 R +/S /P +>> +endobj +482 0 obj +<< +/K 74 +/Lang (EN-US) +/P 910 0 R +/Pg 24 0 R +/S /P +>> +endobj +483 0 obj +<< +/K 75 +/Pg 24 0 R +/S /Artifact +>> +endobj +484 0 obj +<< +/K 76 +/Lang (EN-US) +/P 911 0 R +/Pg 24 0 R +/S /P +>> +endobj +485 0 obj +<< +/K 77 +/Lang (EN-US) +/P 912 0 R +/Pg 24 0 R +/S /P +>> +endobj +486 0 obj +<< +/K 78 +/Pg 24 0 R +/S /Artifact +>> +endobj +487 0 obj +<< +/K 79 +/Lang (EN-US) +/P 913 0 R +/Pg 24 0 R +/S /P +>> +endobj +488 0 obj +<< +/K 80 +/Lang (EN-US) +/P 914 0 R +/Pg 24 0 R +/S /P +>> +endobj +489 0 obj +<< +/K 81 +/Pg 24 0 R +/S /Artifact +>> +endobj +490 0 obj +<< +/K 82 +/Pg 24 0 R +/S /Artifact +>> +endobj +491 0 obj +<< +/K 84 +/Pg 24 0 R +/S /Artifact +>> +endobj +492 0 obj +<< +/K 85 +/Lang (EN-US) +/P 915 0 R +/Pg 24 0 R +/S /P +>> +endobj +493 0 obj +<< +/K 86 +/Lang (EN-US) +/P 916 0 R +/Pg 24 0 R +/S /P +>> +endobj +494 0 obj +<< +/K 87 +/Pg 24 0 R +/S /Artifact +>> +endobj +495 0 obj +<< +/K 88 +/Lang (EN-US) +/P 62 0 R +/Pg 24 0 R +/S /P +>> +endobj +496 0 obj +<< +/K 89 +/Lang (EN-US) +/P 62 0 R +/Pg 24 0 R +/S /H2 +>> +endobj +497 0 obj +<< +/K 90 +/Lang (EN-US) +/P 917 0 R +/Pg 24 0 R +/S /P +>> +endobj +498 0 obj +<< +/K 91 +/Lang (EN-US) +/P 918 0 R +/Pg 24 0 R +/S /P +>> +endobj +499 0 obj +<< +/K 92 +/Pg 24 0 R +/S /Artifact +>> +endobj +500 0 obj +<< +/K 93 +/Pg 24 0 R +/S /Artifact +>> +endobj +501 0 obj +<< +/K 99 +/Pg 24 0 R +/S /Artifact +>> +endobj +502 0 obj +<< +/K 100 +/Pg 24 0 R +/S /Artifact +>> +endobj +503 0 obj +<< +/K 102 +/Pg 24 0 R +/S /Artifact +>> +endobj +504 0 obj +<< +/K 104 +/Pg 24 0 R +/S /Artifact +>> +endobj +505 0 obj +<< +/K 105 +/Pg 24 0 R +/S /Artifact +>> +endobj +506 0 obj +<< +/K 107 +/Pg 24 0 R +/S /Artifact +>> +endobj +507 0 obj +<< +/K 109 +/Pg 24 0 R +/S /Artifact +>> +endobj +508 0 obj +<< +/K 110 +/Pg 24 0 R +/S /Artifact +>> +endobj +509 0 obj +<< +/K 112 +/Pg 24 0 R +/S /Artifact +>> +endobj +510 0 obj +<< +/K 114 +/Pg 24 0 R +/S /Artifact +>> +endobj +511 0 obj +<< +/K 115 +/Pg 24 0 R +/S /Artifact +>> +endobj +512 0 obj +<< +/K 117 +/Pg 24 0 R +/S /Artifact +>> +endobj +513 0 obj +<< +/K 119 +/Pg 24 0 R +/S /Artifact +>> +endobj +514 0 obj +<< +/K 120 +/Pg 24 0 R +/S /Artifact +>> +endobj +515 0 obj +<< +/K 126 +/Pg 24 0 R +/S /Artifact +>> +endobj +516 0 obj +<< +/K 127 +/Pg 24 0 R +/S /Artifact +>> +endobj +517 0 obj +<< +/K 129 +/Pg 24 0 R +/S /Artifact +>> +endobj +518 0 obj +<< +/K 131 +/Pg 24 0 R +/S /Artifact +>> +endobj +519 0 obj +<< +/K 132 +/Lang (EN-US) +/P 919 0 R +/Pg 24 0 R +/S /LBody +>> +endobj +520 0 obj +<< +/K 133 +/Lang (EN-US) +/P 920 0 R +/Pg 24 0 R +/S /LBody +>> +endobj +521 0 obj +<< +/K 134 +/Lang (EN-US) +/P 921 0 R +/Pg 24 0 R +/S /LBody +>> +endobj +522 0 obj +<< +/K 135 +/Lang (EN-US) +/P 922 0 R +/Pg 24 0 R +/S /P +>> +endobj +523 0 obj +<< +/K 136 +/Lang (EN-US) +/P 923 0 R +/Pg 24 0 R +/S /LBody +>> +endobj +524 0 obj +<< +/K 137 +/Lang (EN-US) +/P 924 0 R +/Pg 24 0 R +/S /P +>> +endobj +525 0 obj +<< +/K 138 +/Lang (EN-US) +/P 925 0 R +/Pg 24 0 R +/S /P +>> +endobj +526 0 obj +<< +/K 139 +/Lang (EN-US) +/P 926 0 R +/Pg 24 0 R +/S /P +>> +endobj +527 0 obj +<< +/K [140 141] +/Lang (EN-US) +/P 927 0 R +/Pg 24 0 R +/S /P +>> +endobj +528 0 obj +<< +/K 142 +/Lang (EN-US) +/P 928 0 R +/Pg 24 0 R +/S /P +>> +endobj +529 0 obj +<< +/K 143 +/Lang (EN-US) +/P 929 0 R +/Pg 24 0 R +/S /P +>> +endobj +530 0 obj +<< +/K 144 +/Lang (EN-US) +/P 930 0 R +/Pg 24 0 R +/S /P +>> +endobj +531 0 obj +<< +/K 145 +/Lang (EN-US) +/P 931 0 R +/Pg 24 0 R +/S /P +>> +endobj +532 0 obj +<< +/K 146 +/Lang (EN-US) +/P 932 0 R +/Pg 24 0 R +/S /LBody +>> +endobj +533 0 obj +<< +/K 147 +/Lang (EN-US) +/P 933 0 R +/Pg 24 0 R +/S /P +>> +endobj +534 0 obj +<< +/K 148 +/Lang (EN-US) +/P 934 0 R +/Pg 24 0 R +/S /P +>> +endobj +535 0 obj +<< +/K 0 +/Pg 25 0 R +/S /Artifact +>> +endobj +536 0 obj +<< +/K 2 +/Pg 25 0 R +/S /Artifact +>> +endobj +537 0 obj +<< +/K 4 +/Pg 25 0 R +/S /Artifact +>> +endobj +538 0 obj +<< +/K 5 +/Pg 25 0 R +/S /Artifact +>> +endobj +539 0 obj +<< +/K 7 +/Pg 25 0 R +/S /Artifact +>> +endobj +540 0 obj +<< +/K 9 +/Pg 25 0 R +/S /Artifact +>> +endobj +541 0 obj +<< +/K 10 +/Pg 25 0 R +/S /Artifact +>> +endobj +542 0 obj +<< +/K 12 +/Pg 25 0 R +/S /Artifact +>> +endobj +543 0 obj +<< +/K 14 +/Pg 25 0 R +/S /Artifact +>> +endobj +544 0 obj +<< +/K 15 +/Pg 25 0 R +/S /Artifact +>> +endobj +545 0 obj +<< +/K 17 +/Pg 25 0 R +/S /Artifact +>> +endobj +546 0 obj +<< +/K 18 +/Lang (EN-US) +/P 935 0 R +/Pg 25 0 R +/S /P +>> +endobj +547 0 obj +<< +/K 19 +/Lang (EN-US) +/P 936 0 R +/Pg 25 0 R +/S /P +>> +endobj +548 0 obj +<< +/K 20 +/Pg 25 0 R +/S /Artifact +>> +endobj +549 0 obj +<< +/K 21 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +550 0 obj +<< +/K 22 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /H2 +>> +endobj +551 0 obj +<< +/K 23 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +552 0 obj +<< +/K 24 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +553 0 obj +<< +/K 25 +/Pg 25 0 R +/S /Artifact +>> +endobj +554 0 obj +<< +/K 27 +/Pg 25 0 R +/S /Artifact +>> +endobj +555 0 obj +<< +/K 28 +/Lang (EN-US) +/P 937 0 R +/Pg 25 0 R +/S /P +>> +endobj +556 0 obj +<< +/K 29 +/Lang (EN-US) +/P 938 0 R +/Pg 25 0 R +/S /P +>> +endobj +557 0 obj +<< +/K 30 +/Pg 25 0 R +/S /Artifact +>> +endobj +558 0 obj +<< +/K 31 +/Lang (EN-US) +/P 939 0 R +/Pg 25 0 R +/S /P +>> +endobj +559 0 obj +<< +/K 32 +/Lang (EN-US) +/P 940 0 R +/Pg 25 0 R +/S /P +>> +endobj +560 0 obj +<< +/K 33 +/Pg 25 0 R +/S /Artifact +>> +endobj +561 0 obj +<< +/K 34 +/Lang (EN-US) +/P 941 0 R +/Pg 25 0 R +/S /P +>> +endobj +562 0 obj +<< +/K 35 +/Lang (EN-US) +/P 942 0 R +/Pg 25 0 R +/S /P +>> +endobj +563 0 obj +<< +/K 36 +/Pg 25 0 R +/S /Artifact +>> +endobj +564 0 obj +<< +/K 37 +/Lang (EN-US) +/P 943 0 R +/Pg 25 0 R +/S /P +>> +endobj +565 0 obj +<< +/K 38 +/Lang (EN-US) +/P 944 0 R +/Pg 25 0 R +/S /P +>> +endobj +566 0 obj +<< +/K 39 +/Pg 25 0 R +/S /Artifact +>> +endobj +567 0 obj +<< +/K 40 +/Lang (EN-US) +/P 945 0 R +/Pg 25 0 R +/S /P +>> +endobj +568 0 obj +<< +/K 41 +/Lang (EN-US) +/P 946 0 R +/Pg 25 0 R +/S /P +>> +endobj +569 0 obj +<< +/K 42 +/Pg 25 0 R +/S /Artifact +>> +endobj +570 0 obj +<< +/K 43 +/Lang (EN-US) +/P 947 0 R +/Pg 25 0 R +/S /P +>> +endobj +571 0 obj +<< +/K 44 +/Lang (EN-US) +/P 948 0 R +/Pg 25 0 R +/S /P +>> +endobj +572 0 obj +<< +/K 45 +/Pg 25 0 R +/S /Artifact +>> +endobj +573 0 obj +<< +/K 46 +/Lang (EN-US) +/P 949 0 R +/Pg 25 0 R +/S /P +>> +endobj +574 0 obj +<< +/K 47 +/Lang (EN-US) +/P 950 0 R +/Pg 25 0 R +/S /P +>> +endobj +575 0 obj +<< +/K 48 +/Pg 25 0 R +/S /Artifact +>> +endobj +576 0 obj +<< +/K 49 +/Lang (EN-US) +/P 951 0 R +/Pg 25 0 R +/S /P +>> +endobj +577 0 obj +<< +/K 50 +/Lang (EN-US) +/P 952 0 R +/Pg 25 0 R +/S /P +>> +endobj +578 0 obj +<< +/K 51 +/Lang (EN-US) +/P 952 0 R +/Pg 25 0 R +/S /P +>> +endobj +579 0 obj +<< +/K 52 +/Lang (EN-US) +/P 952 0 R +/Pg 25 0 R +/S /P +>> +endobj +580 0 obj +<< +/K 53 +/Pg 25 0 R +/S /Artifact +>> +endobj +581 0 obj +<< +/K 54 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +582 0 obj +<< +/K 55 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /H2 +>> +endobj +583 0 obj +<< +/K 56 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /H2 +>> +endobj +584 0 obj +<< +/K 57 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +585 0 obj +<< +/K 58 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +586 0 obj +<< +/K 59 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +587 0 obj +<< +/K 60 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +588 0 obj +<< +/K 61 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +589 0 obj +<< +/K 62 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +590 0 obj +<< +/K 63 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +591 0 obj +<< +/K 64 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +592 0 obj +<< +/K 65 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +593 0 obj +<< +/K 66 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +594 0 obj +<< +/K 67 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +595 0 obj +<< +/K 68 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +596 0 obj +<< +/K 69 +/Lang (EN-US) +/P 62 0 R +/Pg 25 0 R +/S /P +>> +endobj +597 0 obj +<< +/A 953 0 R +/K 70 +/P 62 0 R +/Pg 25 0 R +/S /Figure +/T () +>> +endobj +598 0 obj +<< +/A 954 0 R +/K 71 +/P 62 0 R +/Pg 25 0 R +/S /Figure +/T () +>> +endobj +599 0 obj +<< +/A 955 0 R +/K 72 +/P 62 0 R +/Pg 25 0 R +/S /Figure +/T () +>> +endobj +600 0 obj +<< +/A 956 0 R +/K 73 +/P 62 0 R +/Pg 25 0 R +/S /Figure +/T () +>> +endobj +601 0 obj +<< +/A 957 0 R +/K 74 +/P 62 0 R +/Pg 25 0 R +/S /Figure +/T () +>> +endobj +602 0 obj +<< +/A 958 0 R +/K 75 +/P 62 0 R +/Pg 25 0 R +/S /Figure +/T () +>> +endobj +603 0 obj +<< +/K 76 +/Lang (EN-US) +/P 959 0 R +/Pg 25 0 R +/S /P +>> +endobj +604 0 obj +<< +/K 77 +/Lang (EN-US) +/P 960 0 R +/Pg 25 0 R +/S /P +>> +endobj +605 0 obj +<< +/K 78 +/Lang (EN-US) +/P 961 0 R +/Pg 25 0 R +/S /P +>> +endobj +606 0 obj +<< +/K [79 80] +/Lang (EN-US) +/P 962 0 R +/Pg 25 0 R +/S /P +>> +endobj +607 0 obj +<< +/K 81 +/Lang (EN-US) +/P 963 0 R +/Pg 25 0 R +/S /P +>> +endobj +608 0 obj +<< +/K [82 83] +/Lang (EN-US) +/P 964 0 R +/Pg 25 0 R +/S /P +>> +endobj +609 0 obj +<< +/K 84 +/Lang (EN-US) +/P 965 0 R +/Pg 25 0 R +/S /P +>> +endobj +610 0 obj +<< +/K 85 +/Lang (EN-US) +/P 966 0 R +/Pg 25 0 R +/S /P +>> +endobj +611 0 obj +<< +/K 0 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +612 0 obj +<< +/K 1 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +613 0 obj +<< +/K 2 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +614 0 obj +<< +/K 3 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +615 0 obj +<< +/K 4 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +616 0 obj +<< +/K 5 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +617 0 obj +<< +/K 6 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +618 0 obj +<< +/K 7 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +619 0 obj +<< +/K 8 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +620 0 obj +<< +/K 9 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +621 0 obj +<< +/K 10 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +622 0 obj +<< +/K 11 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +623 0 obj +<< +/K 12 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +624 0 obj +<< +/K 13 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +625 0 obj +<< +/K 14 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +626 0 obj +<< +/K 15 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +627 0 obj +<< +/K 16 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +628 0 obj +<< +/K 17 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +629 0 obj +<< +/K 18 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +630 0 obj +<< +/K 19 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +631 0 obj +<< +/K 20 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +632 0 obj +<< +/K 22 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +633 0 obj +<< +/K 23 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +634 0 obj +<< +/K 24 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +635 0 obj +<< +/K 25 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +636 0 obj +<< +/K 26 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +637 0 obj +<< +/K 27 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +638 0 obj +<< +/K 28 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +639 0 obj +<< +/K 30 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +640 0 obj +<< +/K 31 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +641 0 obj +<< +/K 33 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +642 0 obj +<< +/K 34 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +643 0 obj +<< +/K 35 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +644 0 obj +<< +/K 36 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +645 0 obj +<< +/K 37 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +646 0 obj +<< +/K 38 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +647 0 obj +<< +/K 39 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +648 0 obj +<< +/K 40 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +649 0 obj +<< +/K 41 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +650 0 obj +<< +/A 967 0 R +/K 42 +/P 62 0 R +/Pg 26 0 R +/S /Figure +/T () +>> +endobj +651 0 obj +<< +/A 968 0 R +/K 43 +/P 62 0 R +/Pg 26 0 R +/S /Figure +/T () +>> +endobj +652 0 obj +<< +/A 969 0 R +/K 44 +/P 62 0 R +/Pg 26 0 R +/S /Figure +/T () +>> +endobj +653 0 obj +<< +/A 970 0 R +/K 45 +/P 62 0 R +/Pg 26 0 R +/S /Figure +/T () +>> +endobj +654 0 obj +<< +/A 971 0 R +/K 46 +/P 62 0 R +/Pg 26 0 R +/S /Figure +/T () +>> +endobj +655 0 obj +<< +/A 972 0 R +/K 47 +/P 62 0 R +/Pg 26 0 R +/S /Figure +/T () +>> +endobj +656 0 obj +<< +/K 48 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +657 0 obj +<< +/K 49 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +658 0 obj +<< +/K 50 +/Lang (EN-US) +/P 62 0 R +/Pg 26 0 R +/S /P +>> +endobj +659 0 obj +<< +/Length 85 +/BBox [0.0 0.0 59.9993 14.5517] +/FormType 1 +/Matrix [1.0 0.0 0.0 1.0 0.0 0.0] +/Resources << +/Font << +/Helv 9 0 R +>> +/ProcSet [/PDF /Text] +>> +/Subtype /Form +/Type /XObject +>> +stream +/Tx BMC +q +1 1 57.9993 12.5517 re +W +n +BT +/Helv 12 Tf +0 g +2 2.8241 Td +(0) Tj +ET +Q +EMC + +endstream +endobj +660 0 obj +<< +/Next 973 0 R +/Parent 34 0 R +/Title (Distributing Points) +>> +endobj +661 0 obj +<< +/Parent 34 0 R +/Prev 974 0 R +/Title (Timeframe) +>> +endobj +662 0 obj +<< +/D [21 0 R /XYZ 34 462 0.0] +/S /GoTo +>> +endobj +663 0 obj +<< +/A 975 0 R +/Next 976 0 R +/Parent 20 0 R +/Prev 36 0 R +/Title (Section II: Parking Lot Assessment- Basic Knowledge and Control) +>> +endobj +664 0 obj +<< +/D [25 0 R /XYZ 34 253 0.0] +/S /GoTo +>> +endobj +665 0 obj +<< +/A 977 0 R +/Next 37 0 R +/Parent 20 0 R +/Prev 978 0 R +/Title (Total Points) +>> +endobj +666 0 obj +<< +/Parent 38 0 R +/Title (Have every DA scan the QR code below to fill out a quick 3 question survey regarding the Enhanced Road Test:) +>> +endobj +667 0 obj +<< +/Ascent 979 +/CapHeight 693 +/Descent -289 +/Flags 32 +/FontBBox [-220 -289 1307 979] +/FontFamily (Amazon Ember) +/FontFile2 979 0 R +/FontName /PSGFJE+AmazonEmber-Bold +/FontStretch /Normal +/FontWeight 700 +/ItalicAngle 0 +/StemV 148 +/Type /FontDescriptor +/XHeight 511 +>> +endobj +668 0 obj +<< +/Length 381 +/Filter /FlateDecode +>> +stream +H‰\’Ënƒ0E÷|…—Í"‚ð° !U$‘Xô¡Ò~!E*²àï;ÃR©–ÀÇöÌõµ=~^ ÛÍÊwC]Ò¬ÚÎ6ަáæjRºvÖÛ…ªéêù>Zÿu_žÏÉå2ÍÔ¶¼4Uþ/N³[ÔÓs3\hãùo®!×Ù«zúÊËòËÛ8þPOvVÊ2ÕPËB/ÕøZõ¤ü5m[4¼ÞÍË–sþ">—‘T¸Žw0S McU“«ì•¼4à–©ôÌ-óÈ6ÿÖ£i—¶þ®œ—†Ü1ÁGæó‘ÌG!8ŽÀ‘pN˜ãÝÊÜ1ÇàX1ñs„Ï`6™&ÐOD?~"ú tÑÑÐ×¢¯£%F#F¯1ØKË^Ú€ð¼†-tÎ…qv-g×'ðI>µø4¸#wbàÙˆg?Füø1âÇÀ?Fƒµ0¼ñfà‡;y¬û«È³qu©GMÔ7ç¸Ö\ë@* ³ô¨ÒqgÉçý +0Œ‡ºQ +endstream +endobj +669 0 obj +<< +/Ascent 974 +/CapHeight 693 +/Descent -281 +/Flags 32 +/FontBBox [-207 -281 1292 974] +/FontFamily (Amazon Ember) +/FontFile2 980 0 R +/FontName /PSGFJE+AmazonEmber-Regular +/FontStretch /Normal +/FontWeight 400 +/ItalicAngle 0 +/StemV 100 +/Type /FontDescriptor +/XHeight 507 +>> +endobj +670 0 obj +<< +/Length 508 +/Filter /FlateDecode +>> +stream +H‰\”Qo¢@…ßùóØ>4(ܹ·Mˆ‰Õ6ñaÛfÝý£K²ñÁ¿s8M›¬‰ò˜s?NóÍn»ë»ÉåqhöarÇ®oc¸ רw§®Ï–…k»fú<››s=fyZ¼¿]¦pÞõÇ!«*—ÿL/S¼¹»u;Â}–¿Ç6Ä®?¹»ß›ý½Ë÷×qüΡŸÜ­V® Çô£ßêspù¼ìaצëÝt{Hk¾ïøuƒ+æó%eš¡ —±nB¬ûSȪEú¬\õš>«,ôí×µà²Ã±ùSǬ*pób‘‰Éà'òxCÞ€·ä-ø…ü’¸dN‰œrI^‚ r.É%XÈödV²‚l`º•p+×äubá,Á,á,Á,á,Á,á,Á,á,Á,á,Á,a¾ _øì‚g—gò3˜=zö èA؃ y%§ò+ÏN<:ñôôðôôôðôôôðôôôðôôôðôôôðôìÄ£e¦"S™©ÈTf*2•™ŠLe¦"S™©ÈTfêœÉ=({Pô ìAу²EÊ=({Pô ìAу±CFgƒ³ÑÙàlt68 ÎFgƒ³ÑÙàlt68= žÆwÃðn=ÓáóÇ–H;×}í·æcÚjóöž÷vWׇ¯€q]Z…oöO€ç˜þ{ +endstream +endobj +671 0 obj +<< +/Ascent 979 +/CapHeight 693 +/Descent -286 +/Flags 32 +/FontBBox [-210 -286 1307 979] +/FontFamily (Amazon Ember Display) +/FontFile2 981 0 R +/FontName /USPEBU+AmazonEmberDisplay-Bold +/FontStretch /Normal +/FontWeight 700 +/ItalicAngle 0 +/StemV 148 +/Type /FontDescriptor +/XHeight 511 +>> +endobj +672 0 obj +<< +/Length 487 +/Filter /FlateDecode +>> +stream +H‰\“ÍŠÛ0…÷~ +-gƒÿ¤{0L2Yô‡¦}ÇVRC#ÅYäí«ã3L¡†ÄŸ‘tôùZ7ßö‡0Ì&ÿÇîègsBým¼ÇΛ“¿ !++ÓÝüñ´üw×vÊò´øø¸Íþzç1k“ÿHƒ·9>ÌÓ¶Oþ9Ë¿ÅÞÇ!\ÌÓ¯ÝñÙäÇû4ýñWfS˜ÍÆôþœ‚¾´Ó×öêM¾,{9ôi|˜/iÍ¿?“7Õò\R¦{›ÚÎÇ6\|ÖéÚ˜æ=]›Ì‡þ¿q·â²Ó¹ûÝÆ¬©0¹(Ò-±¼"¯Àkò¼%oÁ;òüF~¿““@S3¿F~]’K0sjäÔœ_c¾å‹9¶"Wàš\ƒ-Ù‚Ùéoáoéláléiái÷ä=˜ÎΖŽÎÎŽŽŽŽŽŽŽùùÂw¼£0_/Ìä óùÂ|A¾0_/JV0¿—à{ ÷ì+¬³ ÎòJ~³&‚šk"¨‰°&‚šÎÊš(j¢ôWø+ýþJ…¿Ò_á¯ôWø+ýþJ…¿Ò_á¯ôWø+ýþkìUåz9Ø'G> +endobj +674 0 obj +<< +/Length 401 +/Filter /FlateDecode +>> +stream +H‰\’AOë0„ïþ>Â¥M“5•¢JЂÔðDßûi²-‘¨¹é¡ÿž âIXJüEölfìÍÖÛÍ6v£Ïþ¤¾Ùéè]l“žûKjÔïõØE7Ï}Û5ã÷×ônNõà2ï®çQOÛxè]UùìÝÏcºú›‡¶ßë­ËÞR«©‹Góo½»õÙî2 ŸzÒ8ú™_­|«+ôR¯õI}6Éî¶­­wãõÎ4ÿwü½êóé{N3Mßêy¨Mu<ª«f6V¾z¶±rÛ_ë‹@ÙþÐ|ÔÉU96Ïf6ß“ïÁKò¼!oŒÛäªb>±Mƹ—äÌ:êkòÌšjÏd3\•ôSÂO™“sð‚¼ó_%þU +YŒ…~~„ZV¨h…ZVèSàSXG¦:À<Á™³²È#ùÌ\‚\Â\‚\òD~3£ c`Æ€Œ>|ú ðè3Àg ÏŸlÂå~ß"®ÙºÑÿôPsIÉÚgjÙ©oÐ1]ÔŸ®úÁ› +û`¶¶Ä +endstream +endobj +675 0 obj +<< +/Ascent 974 +/CapHeight 693 +/Descent -278 +/Flags 32 +/FontBBox [-184 -278 1292 974] +/FontFamily (Amazon Ember Display) +/FontFile2 983 0 R +/FontName /KQRBRO+AmazonEmberDisplay-Regular +/FontStretch /Normal +/FontWeight 400 +/ItalicAngle 0 +/StemV 100 +/Type /FontDescriptor +/XHeight 507 +>> +endobj +676 0 obj +<< +/Length 554 +/Filter /FlateDecode +>> +stream +H‰\ÔÍŠâ@à}ž¢–Ý‹&šºu¯BlíóÃ8ó1)À˜„¾ýÔÉiz`„n$uëã@U¾;ì];¹üûØ×Ç8¹sÛ5c¼õ÷±Žî/m—- ×´õôñkþ__«!ËÓâãã6Åë¡;÷YYºüGzx›Æ‡{Ú6ý)>gù·±‰cÛ]ÜÓ¯ÝñÙåÇû0ü‰×ØMná6×Äsô¥¾V×èòyÙË¡IÏÛéñ’Öü{ãçcˆ®˜/‰©û&Þ†ªŽcÕ]bV.ÒgãÊ÷ôÙd±kþ{nËNçúw5fe—‹ô•rÁ\ +³"¯˜WÈkæ5òŽy‡¼gÞ#¿1¿!¿3'Lé¹—Ç^~ɼDæ¾ûzÏì‘…Ys@¦ÍÃæÙéôpz:=œ~˼E~e~E¦ÍÃ&ô} +ûô)ìSЧÐ,0öÐg ?Àèðúüþ ?Àèðúüæ³r¾b¾r¾b¾r¾b¾r¾b¾r¾b¾r¾b¾r¾b¾²E?ʽtÞ‹ý(úQö£èGÙ¢e?Š~”ý(úQö£èÇØ¡£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú þæ> +/Filter /FlateDecode +/Height 79 +/Name /X +/Subtype /Image +/Type /XObject +/Width 264 +>> +stream +H‰Ü—{|ÕÇïn–¼6 Ix¨i HˆÄ(" I " $Ÿ¤`±bP H£áðÕZk¥ˆ¶h­TÔŠ`"´Fð‘ˆM0(H6 yABÞÙÇlwgwîýÍÌÝEh{þÚ=÷wÏ9÷;wî=C5Ã-iyW³ÝçŽ.H #^íÚÑYù/¬›•:P¦Kºú…ü)cb4熦çì‡ØcJ¥p˜<³Òk·ÜMiÒNeyöGp}¿“Ó͆5«ÄµaNÑ­6Õ˜)œËá!Τ%ýÕìö},ð½§T§®¿xOò18¬ª?—ÃÍEí+Md¹Ç”ö|‡"Þö®,áÍ7bõjVí4Ûʋ氄{؈&|Áå°ØÊÑþ³—c«ÂCšÊ +·ìFÍ^zÒ‡‹±Gi9cÑÝö#UE;ºL>rXîVŧÅt°ò‚~œÃ¡ù/žóYUÀÿmCoJôø´ÏÞx‰ʤùºÁ[ÔÓ¹GU¡t­<þŒ‡ˆUÒá½ÞÎÓâzõMÈíàUÎ8ÜiË"qì_çZiÞ¸âz³+8´—ø·ª]f=]\Z-óVsæ ¥GýHf[Ù‚–Œ›Gº5}À{0Òå›;ì 5ÑÚ>/üð¨ì€ûy‘ýù<˜V±4ößAô×Ý×"OQ3)68¬ÿ”pµHóÓØ³(emÅTþ•ð-² s¬—¾ lR$ír–¥=„ËáÐÇBûÎïW )¦¿8Ðúf,—Ã~wË–+Ûh‡¸¼“àü²Hóg0×8–j0ì½ K±ãÿRê3 ¿µÑ>ç&x«xÊ#DŸþeðU’ûéު׳Ä=@3Ç®ÄP Òö I» ¼RÝ)tïÔÜÈRå•CF'D«NÜ£à<ŸIÅ_2oi°šCÛh·0¶r-!Q·Þ»ôõ­Ûw~ùSL oÞ<‡‘’÷1Øe´^ú¨¢nž”»ië¶¥k!“zP>‡¾ˆÚ–CýO3o'ôÑ…°¼D5‡bz)½ÏœôÝÕŒ²ÜðòÐà 9|B•©€ì\¯bYº€0üëÃ[øê½Áºï£Ì{ä™Ì-¤©9°k<“Åíæ&–sXÀãðUâîdŸe£ÙaÕÊá#‡¹x²Ÿ@ý‘€çuЇƒ8z<œsóxæRõ€ìÆugfôAaÐ ñ8$ÖÀ"„ 6p;øgÃøÏ«842¡‡À¤I?_ýZ™²ãâr˜Hg%Ã…}Cgq†Áæ¬xõ‹ZÅW +‡ƒá=·=#Ë` §àWŽŠÃo‹Ožïâu[\,1rø‰oâ>û¾±“÷uÇáð+,éëù»‡Sž8|æ‘Ã<íž™ËaÌæÐï‘ï4S©9Ü +Ý…½é6*Ñâð $*9xâ°´ÁÇ #á|¸Î;‡•u¼ Éá4ŒZgˆ49| +™JïksˆƒÛWÉ4Úp_ô¡Þ1ì E1›™ Ù~ æÆ±nQ +¼:ïÐx=¿eÞ²@_9@Ÿ`G«ee<àû˜ÎšI…Ëd«Ìv9õ;ìLØu¨Š²Çé§î86»}K½rÀÏÚ]tiVYÞý=ýÁ!¸B~.é†6ÈR]Ћ^£ìÎT›)^”ýžy„iRÄclu¤þ¸W{!zóL×øxlëf[ëúO0æ/Å‹P7¶J±Â¿ˆmcäÏ,©bÈ5à›!"ŒÚÌIuÛ2 —pºÆegîoÆ¿ÝÞ ï|É{t°¼¦ÌêN³Å"¦b/ -n[¤½r¯¶ê|—Uì‚`5·lV붸‹©Ùîl©Æª›¨¯Oò=Y“FP€Ô“Ë`éRäÞÀKŠ©Oœ¾àñy ïað.þ´€£1.kÑòG'ÄA:ïþ­ÿü—wÔ³÷ÞrèOÙÁWµ «ñzÄ}#ÿì­¸‡÷™—Ët‹ÞXxioïÅ[à»UŸ{Nkî}îå²° +»Ý4üŠž?%<NK¿’e(lª£‰·\Á +"¾rŸ­g«MÕõ-¶ÿ +$Y<­vÇ\¡|Æ"Ó§/ÎÅÚôÞ9'Ü}PÚ*o£ˆýâ;îZ¡"‡†¡W­Ñ¢‹]­iÙà«use‰ùF]¥ôÿ ð´AzNßwg Œ\ÁÖ¢WÖÄ ’ÞA{+ïbDhÒÞÆdÅ!­°Ý«ée1 Vh?YDŸ¦•ÝÌë_Cúz‰`›=éb-!0õ¼þú"I㦳ˆþ}°UÇØAÜ- ævÚÚI$`õ¸„hѼXW(D;«X¢ßæÃmà´.žVÑÎBÒ÷ÚH»?ûÕÅiPp1ñ+Nø ³ã÷p ¥ ¶o`[( ø +r3÷߃µåjÔµ@(nñYDë[Xt ˆsS’ºVQ˜»?þCiòÿ}\¥!AdÁ§âwþ'²Ñ§µ!ܧU¶€Ë¿ÿ£ë¦8ñQf*³vÉŠ“PL}kµ~r¦äžw0BX…\j2T&Ç8È­ŸÐÍûºC®"”[¨æ~ª Gÿ0Câÿ¿ï—W¯ªÐ$¾Å.í;cÕîw0Lû³Í)¯=ŠÜW ¾?(œuþ` ø|tR‚‰¦ª¼'öö˜’š–sÉÒ›8Ìøy%Y½H¬.þ"H¿Ã–&àiãÇÇç—ö­_  ;Ë2bƒ"ãR˺ç¯ +l<|íõ—ß8uÿ=ì΋b¨µ”>MXrKö•ïxB‚Lðñ(zëYùÑÿÿw”Ä‹Dçi¸sYàS™º-@;LÂ{¤Ž™×¿öQàßû3ÅØ¬8ýÿÕ  vlûE`¸•f‰Ղ÷÷Íoဠ+ÂÝûޝÔ$þ~¼´^n ý@À(ëXvÌø¾2Ö”ÂÖèàœù‡.¼ÇÒÔÄ þ}t|ƒ ú¬x\J·|$..N ³VŽÂ6]{¸ÿå÷ßhyåßß?¿^]>thMŒ> +endobj +679 0 obj +<< +/Length 226 +/Filter /FlateDecode +>> +stream +H‰\ÏjÄ Æï>Åw‹Ùœ%P¶rèšöŒNR¡ebyûŽ6l¡*ã÷ýäsô­ì)dÐoÝ€¦@žq;„ç@êÚ‚.]ÝÝb“ÒûšqéiŠÊÐï"®™w8=ø8âYéWöÈf8}Þ†3èaKé¤ txœä¡g›^ì‚ +vé½è!ïaþ{Bhký ã¢Ç5Y‡liFe©Ì“T§ü?ý ÆÉ}YV¦-Þ¦‘£xÛBÉçàÉmÌ’¦N Æ(á}H)&ª,õ#ÀÙÆo0 +endstream +endobj +680 0 obj +<< +/Ascent 1040 +/CapHeight 716 +/Descent -325 +/Flags 32 +/FontBBox [-665 -325 2000 1040] +/FontFamily (Arial) +/FontFile2 985 0 R +/FontName /YGQCFM+ArialMT +/FontStretch /Normal +/FontWeight 400 +/ItalicAngle 0 +/StemV 88 +/Type /FontDescriptor +/XHeight 519 +>> +endobj +681 0 obj +<< +/Length 226 +/Filter /FlateDecode +>> +stream +H‰\ÏjÄ Æï>Åw‹Ùœ%P¶rèšöŒNR¡ebyûŽ6l¡*ã÷ýäsô­ì)dÐoÝ€¦@žq;„ç@êÚ‚.]ÝÝb“ÒûšqéiŠÊÐï"®™w8=ø8âYéWöÈf8}Þ†3èaKé¤ txœä¡g›^ì‚ +vé½è!ïaþ{Bhký ã¢Ç5Y‡liFe©Ì“T§ü?ý ÆÉ}YV¦-Þ¦‘£xÛBÉçàÉmÌ’¦N Æ(á}H)&ª,õ#ÀÙÆo0 +endstream +endobj +682 0 obj +<< +/A 986 0 R +/K [987 0 R 988 0 R 989 0 R 990 0 R 991 0 R 992 0 R 993 0 R 994 0 R] +/P 62 0 R +/S /Table +>> +endobj +683 0 obj +<< +/A 995 0 R +/K [996 0 R 997 0 R 998 0 R 999 0 R 1000 0 R 1001 0 R 1002 0 R 1003 0 R 1004 0 R 1005 0 R] +/P 62 0 R +/S /Table +>> +endobj +684 0 obj +<< +/A 1006 0 R +/K [1007 0 R 1008 0 R 1009 0 R 1010 0 R 1011 0 R 1012 0 R 1013 0 R 1014 0 R 1015 0 R 1016 0 R +1017 0 R 1018 0 R 1019 0 R 1020 0 R 1021 0 R 1022 0 R 1023 0 R 1024 0 R 1025 0 R 1026 0 R +1027 0 R 1028 0 R] +/P 62 0 R +/S /Table +>> +endobj +685 0 obj +<< +/A 1029 0 R +/K [1030 0 R 1031 0 R 1032 0 R 1033 0 R 1034 0 R 1035 0 R 1036 0 R 1037 0 R 1038 0 R 1039 0 R +1040 0 R 1041 0 R 1042 0 R 1043 0 R 1044 0 R 1045 0 R 1046 0 R 1047 0 R 1048 0 R 1049 0 R +1050 0 R 1051 0 R 1052 0 R 1053 0 R 1054 0 R 1055 0 R 1056 0 R] +/P 62 0 R +/S /Table +>> +endobj +686 0 obj +<< +/A 1057 0 R +/K [1058 0 R 1059 0 R 1060 0 R 1061 0 R 1062 0 R 1063 0 R 1064 0 R 1065 0 R 1066 0 R 1067 0 R +1068 0 R 1069 0 R 1070 0 R 1071 0 R 1072 0 R 1073 0 R 1074 0 R 1075 0 R 1076 0 R 1077 0 R +1078 0 R 1079 0 R 1080 0 R 1081 0 R 1082 0 R 1083 0 R 1084 0 R 1085 0 R 1086 0 R 1087 0 R +1088 0 R 1089 0 R 1090 0 R 1091 0 R 1092 0 R 1093 0 R 1094 0 R] +/P 62 0 R +/S /Table +>> +endobj +687 0 obj +<< +/A 1095 0 R +/K [1096 0 R 1097 0 R 1098 0 R 1099 0 R 1100 0 R 1101 0 R 1102 0 R 1103 0 R 1104 0 R 1105 0 R +1106 0 R 1107 0 R] +/P 62 0 R +/S /Table +>> +endobj +688 0 obj +<< +/A 1108 0 R +/K [1109 0 R 1110 0 R 1111 0 R 1112 0 R 1113 0 R 1114 0 R 1115 0 R 1116 0 R 1117 0 R 1118 0 R +1119 0 R 1120 0 R 1121 0 R] +/P 62 0 R +/S /Table +>> +endobj +689 0 obj +<< +/A 1122 0 R +/K [1123 0 R 1124 0 R 1125 0 R 1126 0 R 1127 0 R 1128 0 R 1129 0 R 1130 0 R 1131 0 R] +/P 62 0 R +/S /Table +>> +endobj +690 0 obj +<< +/K 67 0 R +/P 996 0 R +/S /TD +>> +endobj +691 0 obj +<< +/K 68 0 R +/P 996 0 R +/S /TD +>> +endobj +692 0 obj +<< +/K 74 0 R +/P 999 0 R +/S /TD +>> +endobj +693 0 obj +<< +/K 75 0 R +/P 999 0 R +/S /TD +>> +endobj +694 0 obj +<< +/K 77 0 R +/P 1000 0 R +/S /TD +>> +endobj +695 0 obj +<< +/K 78 0 R +/P 1000 0 R +/S /TD +>> +endobj +696 0 obj +<< +/K 82 0 R +/P 1002 0 R +/S /TD +>> +endobj +697 0 obj +<< +/K 83 0 R +/P 1002 0 R +/S /TD +>> +endobj +698 0 obj +<< +/K 85 0 R +/P 1003 0 R +/S /TD +>> +endobj +699 0 obj +<< +/K 86 0 R +/P 1003 0 R +/S /TD +>> +endobj +700 0 obj +<< +/K 90 0 R +/P 1005 0 R +/S /TD +>> +endobj +701 0 obj +<< +/K 91 0 R +/P 1005 0 R +/S /TD +>> +endobj +702 0 obj +<< +/K 98 0 R +/P 1007 0 R +/S /TD +>> +endobj +703 0 obj +<< +/K 99 0 R +/P 1007 0 R +/S /TD +>> +endobj +704 0 obj +<< +/A 1132 0 R +/K 109 0 R +/P 987 0 R +/S /TH +>> +endobj +705 0 obj +<< +/A 1133 0 R +/K 111 0 R +/P 987 0 R +/S /TH +>> +endobj +706 0 obj +<< +/K 114 0 R +/P 988 0 R +/S /TD +>> +endobj +707 0 obj +<< +/K 116 0 R +/P 988 0 R +/S /TD +>> +endobj +708 0 obj +<< +/K 119 0 R +/P 989 0 R +/S /TD +>> +endobj +709 0 obj +<< +/K 121 0 R +/P 989 0 R +/S /TD +>> +endobj +710 0 obj +<< +/K 124 0 R +/P 990 0 R +/S /TD +>> +endobj +711 0 obj +<< +/K 126 0 R +/P 990 0 R +/S /TD +>> +endobj +712 0 obj +<< +/K 129 0 R +/P 991 0 R +/S /TD +>> +endobj +713 0 obj +<< +/K 130 0 R +/P 991 0 R +/S /TD +>> +endobj +714 0 obj +<< +/K 131 0 R +/P 991 0 R +/S /TD +>> +endobj +715 0 obj +<< +/K 132 0 R +/P 991 0 R +/S /TD +>> +endobj +716 0 obj +<< +/K 133 0 R +/P 991 0 R +/S /TD +>> +endobj +717 0 obj +<< +/K 136 0 R +/P 992 0 R +/S /TD +>> +endobj +718 0 obj +<< +/K 138 0 R +/P 992 0 R +/S /TD +>> +endobj +719 0 obj +<< +/K 141 0 R +/P 993 0 R +/S /TD +>> +endobj +720 0 obj +<< +/K 142 0 R +/P 993 0 R +/S /TD +>> +endobj +721 0 obj +<< +/K 144 0 R +/P 993 0 R +/S /TD +>> +endobj +722 0 obj +<< +/K 145 0 R +/P 993 0 R +/S /TD +>> +endobj +723 0 obj +<< +/K 148 0 R +/P 994 0 R +/S /TD +>> +endobj +724 0 obj +<< +/K 149 0 R +/P 994 0 R +/S /TD +>> +endobj +725 0 obj +<< +/K 150 0 R +/P 994 0 R +/S /TD +>> +endobj +726 0 obj +<< +/A 1134 0 R +/K 152 0 R +/P 987 0 R +/Pg 21 0 R +/S /TH +>> +endobj +727 0 obj +<< +/A 1135 0 R +/K 153 0 R +/P 987 0 R +/Pg 21 0 R +/S /TH +>> +endobj +728 0 obj +<< +/A 1136 0 R +/K 154 0 R +/P 988 0 R +/Pg 21 0 R +/S /TH +>> +endobj +729 0 obj +<< +/K 155 0 R +/P 988 0 R +/S /TD +>> +endobj +730 0 obj +<< +/A 1137 0 R +/K 156 0 R +/P 989 0 R +/Pg 21 0 R +/S /TH +>> +endobj +731 0 obj +<< +/K 157 0 R +/P 989 0 R +/S /TD +>> +endobj +732 0 obj +<< +/A 1138 0 R +/K 158 0 R +/P 990 0 R +/Pg 21 0 R +/S /TH +>> +endobj +733 0 obj +<< +/K 159 0 R +/P 990 0 R +/S /TD +>> +endobj +734 0 obj +<< +/A 1139 0 R +/K 160 0 R +/P 991 0 R +/Pg 21 0 R +/S /TH +>> +endobj +735 0 obj +<< +/A 1140 0 R +/K 161 0 R +/P 992 0 R +/Pg 21 0 R +/S /TH +>> +endobj +736 0 obj +<< +/K 162 0 R +/P 992 0 R +/S /TD +>> +endobj +737 0 obj +<< +/A 1141 0 R +/K 163 0 R +/P 993 0 R +/Pg 21 0 R +/S /TH +>> +endobj +738 0 obj +<< +/K 164 0 R +/P 993 0 R +/S /TD +>> +endobj +739 0 obj +<< +/A 1142 0 R +/K 165 0 R +/P 994 0 R +/Pg 21 0 R +/S /TH +>> +endobj +740 0 obj +<< +/K 166 0 R +/P 1143 0 R +/S /LI +>> +endobj +741 0 obj +<< +/K 167 0 R +/P 998 0 R +/S /TD +>> +endobj +742 0 obj +<< +/K 168 0 R +/P 1001 0 R +/S /TD +>> +endobj +743 0 obj +<< +/K 169 0 R +/P 1004 0 R +/S /TD +>> +endobj +744 0 obj +<< +/K 170 0 R +/P 1144 0 R +/S /LI +>> +endobj +745 0 obj +<< +/K 171 0 R +/P 1009 0 R +/S /TD +>> +endobj +746 0 obj +<< +/K [172 0 R 173 0 R] +/P 1009 0 R +/S /TD +>> +endobj +747 0 obj +<< +/K 174 0 R +/P 1145 0 R +/S /LI +>> +endobj +748 0 obj +<< +/K 175 0 R +/P 1011 0 R +/S /TD +>> +endobj +749 0 obj +<< +/K 176 0 R +/P 1011 0 R +/S /TD +>> +endobj +750 0 obj +<< +/K 178 0 R +/P 1012 0 R +/S /TD +>> +endobj +751 0 obj +<< +/K 179 0 R +/P 1012 0 R +/S /TD +>> +endobj +752 0 obj +<< +/K 181 0 R +/P 1013 0 R +/S /TD +>> +endobj +753 0 obj +<< +/K 182 0 R +/P 1013 0 R +/S /TD +>> +endobj +754 0 obj +<< +/K 184 0 R +/P 1014 0 R +/S /TD +>> +endobj +755 0 obj +<< +/K 185 0 R +/P 1014 0 R +/S /TD +>> +endobj +756 0 obj +<< +/K 187 0 R +/P 1015 0 R +/S /TD +>> +endobj +757 0 obj +<< +/K 188 0 R +/P 1015 0 R +/S /TD +>> +endobj +758 0 obj +<< +/K 190 0 R +/P 1016 0 R +/S /TD +>> +endobj +759 0 obj +<< +/K 191 0 R +/P 1016 0 R +/S /TD +>> +endobj +760 0 obj +<< +/K 193 0 R +/P 1017 0 R +/S /TD +>> +endobj +761 0 obj +<< +/K 194 0 R +/P 1017 0 R +/S /TD +>> +endobj +762 0 obj +<< +/K 196 0 R +/P 1018 0 R +/S /TD +>> +endobj +763 0 obj +<< +/K 197 0 R +/P 1018 0 R +/S /TD +>> +endobj +764 0 obj +<< +/K 201 0 R +/P 1020 0 R +/S /TD +>> +endobj +765 0 obj +<< +/K 202 0 R +/P 1020 0 R +/S /TD +>> +endobj +766 0 obj +<< +/K 204 0 R +/P 1021 0 R +/S /TD +>> +endobj +767 0 obj +<< +/K 205 0 R +/P 1021 0 R +/S /TD +>> +endobj +768 0 obj +<< +/K 207 0 R +/P 1022 0 R +/S /TD +>> +endobj +769 0 obj +<< +/K 208 0 R +/P 1022 0 R +/S /TD +>> +endobj +770 0 obj +<< +/K 210 0 R +/P 1023 0 R +/S /TD +>> +endobj +771 0 obj +<< +/K 211 0 R +/P 1023 0 R +/S /TD +>> +endobj +772 0 obj +<< +/K 213 0 R +/P 1024 0 R +/S /TD +>> +endobj +773 0 obj +<< +/K 214 0 R +/P 1024 0 R +/S /TD +>> +endobj +774 0 obj +<< +/K 216 0 R +/P 1025 0 R +/S /TD +>> +endobj +775 0 obj +<< +/K 217 0 R +/P 1025 0 R +/S /TD +>> +endobj +776 0 obj +<< +/K 219 0 R +/P 1026 0 R +/S /TD +>> +endobj +777 0 obj +<< +/K 220 0 R +/P 1026 0 R +/S /TD +>> +endobj +778 0 obj +<< +/K 222 0 R +/P 1027 0 R +/S /TD +>> +endobj +779 0 obj +<< +/K 223 0 R +/P 1027 0 R +/S /TD +>> +endobj +780 0 obj +<< +/K 225 0 R +/P 1028 0 R +/S /TD +>> +endobj +781 0 obj +<< +/K 226 0 R +/P 1028 0 R +/S /TD +>> +endobj +782 0 obj +<< +/K 279 0 R +/P 1048 0 R +/S /TD +>> +endobj +783 0 obj +<< +/K 280 0 R +/P 1048 0 R +/S /TD +>> +endobj +784 0 obj +<< +/K 282 0 R +/P 1049 0 R +/S /TD +>> +endobj +785 0 obj +<< +/K 283 0 R +/P 1049 0 R +/S /TD +>> +endobj +786 0 obj +<< +/K 285 0 R +/P 1050 0 R +/S /TD +>> +endobj +787 0 obj +<< +/K 286 0 R +/P 1050 0 R +/S /TD +>> +endobj +788 0 obj +<< +/K 288 0 R +/P 1146 0 R +/S /LI +>> +endobj +789 0 obj +<< +/K 289 0 R +/P 1147 0 R +/S /LI +>> +endobj +790 0 obj +<< +/K 290 0 R +/P 1031 0 R +/S /TD +>> +endobj +791 0 obj +<< +/K 291 0 R +/P 1031 0 R +/S /TD +>> +endobj +792 0 obj +<< +/K 292 0 R +/P 1032 0 R +/S /TD +>> +endobj +793 0 obj +<< +/K 293 0 R +/P 1032 0 R +/S /TD +>> +endobj +794 0 obj +<< +/K 294 0 R +/P 1033 0 R +/S /TD +>> +endobj +795 0 obj +<< +/K 295 0 R +/P 1033 0 R +/S /TD +>> +endobj +796 0 obj +<< +/K 296 0 R +/P 1034 0 R +/S /TD +>> +endobj +797 0 obj +<< +/K 297 0 R +/P 1034 0 R +/S /TD +>> +endobj +798 0 obj +<< +/K 298 0 R +/P 1148 0 R +/S /LI +>> +endobj +799 0 obj +<< +/K 299 0 R +/P 1036 0 R +/S /TD +>> +endobj +800 0 obj +<< +/K 300 0 R +/P 1036 0 R +/S /TD +>> +endobj +801 0 obj +<< +/K 301 0 R +/P 1037 0 R +/S /TD +>> +endobj +802 0 obj +<< +/K 302 0 R +/P 1037 0 R +/S /TD +>> +endobj +803 0 obj +<< +/K 303 0 R +/P 1038 0 R +/S /TD +>> +endobj +804 0 obj +<< +/K 304 0 R +/P 1038 0 R +/S /TD +>> +endobj +805 0 obj +<< +/K 305 0 R +/P 1149 0 R +/S /LI +>> +endobj +806 0 obj +<< +/K 306 0 R +/P 1040 0 R +/S /TD +>> +endobj +807 0 obj +<< +/K 307 0 R +/P 1040 0 R +/S /TD +>> +endobj +808 0 obj +<< +/K 308 0 R +/P 1041 0 R +/S /TD +>> +endobj +809 0 obj +<< +/K 309 0 R +/P 1041 0 R +/S /TD +>> +endobj +810 0 obj +<< +/K 310 0 R +/P 1042 0 R +/S /TD +>> +endobj +811 0 obj +<< +/K 311 0 R +/P 1042 0 R +/S /TD +>> +endobj +812 0 obj +<< +/K 312 0 R +/P 1043 0 R +/S /TD +>> +endobj +813 0 obj +<< +/K 313 0 R +/P 1043 0 R +/S /TD +>> +endobj +814 0 obj +<< +/K 314 0 R +/P 1044 0 R +/S /TD +>> +endobj +815 0 obj +<< +/K 315 0 R +/P 1044 0 R +/S /TD +>> +endobj +816 0 obj +<< +/K 316 0 R +/P 1045 0 R +/S /TD +>> +endobj +817 0 obj +<< +/K 317 0 R +/P 1045 0 R +/S /TD +>> +endobj +818 0 obj +<< +/K 318 0 R +/P 1046 0 R +/S /TD +>> +endobj +819 0 obj +<< +/K 319 0 R +/P 1046 0 R +/S /TD +>> +endobj +820 0 obj +<< +/K 320 0 R +/P 1150 0 R +/S /LI +>> +endobj +821 0 obj +<< +/K 321 0 R +/P 1051 0 R +/S /TD +>> +endobj +822 0 obj +<< +/K 322 0 R +/P 1051 0 R +/S /TD +>> +endobj +823 0 obj +<< +/K 324 0 R +/P 1052 0 R +/S /TD +>> +endobj +824 0 obj +<< +/K 325 0 R +/P 1052 0 R +/S /TD +>> +endobj +825 0 obj +<< +/K 327 0 R +/P 1053 0 R +/S /TD +>> +endobj +826 0 obj +<< +/K 328 0 R +/P 1053 0 R +/S /TD +>> +endobj +827 0 obj +<< +/K 330 0 R +/P 1054 0 R +/S /TD +>> +endobj +828 0 obj +<< +/K 331 0 R +/P 1054 0 R +/S /TD +>> +endobj +829 0 obj +<< +/K 335 0 R +/P 1056 0 R +/S /TD +>> +endobj +830 0 obj +<< +/K 336 0 R +/P 1056 0 R +/S /TD +>> +endobj +831 0 obj +<< +/K 344 0 R +/P 1058 0 R +/S /TD +>> +endobj +832 0 obj +<< +/K 345 0 R +/P 1058 0 R +/S /TD +>> +endobj +833 0 obj +<< +/K 349 0 R +/P 1060 0 R +/S /TD +>> +endobj +834 0 obj +<< +/K 350 0 R +/P 1060 0 R +/S /TD +>> +endobj +835 0 obj +<< +/K 352 0 R +/P 1061 0 R +/S /TD +>> +endobj +836 0 obj +<< +/K 353 0 R +/P 1061 0 R +/S /TD +>> +endobj +837 0 obj +<< +/K 355 0 R +/P 1062 0 R +/S /TD +>> +endobj +838 0 obj +<< +/K 356 0 R +/P 1062 0 R +/S /TD +>> +endobj +839 0 obj +<< +/K 358 0 R +/P 1063 0 R +/S /TD +>> +endobj +840 0 obj +<< +/K 359 0 R +/P 1063 0 R +/S /TD +>> +endobj +841 0 obj +<< +/K 374 0 R +/P 1069 0 R +/S /TD +>> +endobj +842 0 obj +<< +/K 375 0 R +/P 1069 0 R +/S /TD +>> +endobj +843 0 obj +<< +/K 377 0 R +/P 1070 0 R +/S /TD +>> +endobj +844 0 obj +<< +/K 378 0 R +/P 1070 0 R +/S /TD +>> +endobj +845 0 obj +<< +/K 380 0 R +/P 1071 0 R +/S /TD +>> +endobj +846 0 obj +<< +/K 381 0 R +/P 1071 0 R +/S /TD +>> +endobj +847 0 obj +<< +/K 383 0 R +/P 1072 0 R +/S /TD +>> +endobj +848 0 obj +<< +/K 384 0 R +/P 1072 0 R +/S /TD +>> +endobj +849 0 obj +<< +/K 386 0 R +/P 1073 0 R +/S /TD +>> +endobj +850 0 obj +<< +/K 387 0 R +/P 1073 0 R +/S /TD +>> +endobj +851 0 obj +<< +/K 389 0 R +/P 1074 0 R +/S /TD +>> +endobj +852 0 obj +<< +/K 390 0 R +/P 1074 0 R +/S /TD +>> +endobj +853 0 obj +<< +/K 394 0 R +/P 1076 0 R +/S /TD +>> +endobj +854 0 obj +<< +/K 395 0 R +/P 1076 0 R +/S /TD +>> +endobj +855 0 obj +<< +/K 397 0 R +/P 1077 0 R +/S /TD +>> +endobj +856 0 obj +<< +/K 398 0 R +/P 1077 0 R +/S /TD +>> +endobj +857 0 obj +<< +/K 400 0 R +/P 1078 0 R +/S /TD +>> +endobj +858 0 obj +<< +/K 401 0 R +/P 1078 0 R +/S /TD +>> +endobj +859 0 obj +<< +/K 405 0 R +/P 1080 0 R +/S /TD +>> +endobj +860 0 obj +<< +/K 406 0 R +/P 1080 0 R +/S /TD +>> +endobj +861 0 obj +<< +/K 408 0 R +/P 1081 0 R +/S /TD +>> +endobj +862 0 obj +<< +/K 409 0 R +/P 1081 0 R +/S /TD +>> +endobj +863 0 obj +<< +/K 411 0 R +/P 1055 0 R +/S /TD +>> +endobj +864 0 obj +<< +/K 412 0 R +/P 1151 0 R +/S /LI +>> +endobj +865 0 obj +<< +/K 413 0 R +/P 1152 0 R +/S /LI +>> +endobj +866 0 obj +<< +/K 414 0 R +/P 1065 0 R +/S /TD +>> +endobj +867 0 obj +<< +/K 415 0 R +/P 1065 0 R +/S /TD +>> +endobj +868 0 obj +<< +/K 416 0 R +/P 1066 0 R +/S /TD +>> +endobj +869 0 obj +<< +/K 417 0 R +/P 1066 0 R +/S /TD +>> +endobj +870 0 obj +<< +/K 418 0 R +/P 1067 0 R +/S /TD +>> +endobj +871 0 obj +<< +/K 419 0 R +/P 1067 0 R +/S /TD +>> +endobj +872 0 obj +<< +/K 420 0 R +/P 1153 0 R +/S /LI +>> +endobj +873 0 obj +<< +/K 421 0 R +/P 1154 0 R +/S /LI +>> +endobj +874 0 obj +<< +/K 422 0 R +/P 1155 0 R +/S /LI +>> +endobj +875 0 obj +<< +/K 423 0 R +/P 1082 0 R +/S /TD +>> +endobj +876 0 obj +<< +/K 424 0 R +/P 1082 0 R +/S /TD +>> +endobj +877 0 obj +<< +/K 426 0 R +/P 1083 0 R +/S /TD +>> +endobj +878 0 obj +<< +/K 427 0 R +/P 1083 0 R +/S /TD +>> +endobj +879 0 obj +<< +/K 429 0 R +/P 1084 0 R +/S /TD +>> +endobj +880 0 obj +<< +/K 430 0 R +/P 1084 0 R +/S /TD +>> +endobj +881 0 obj +<< +/K 432 0 R +/P 1085 0 R +/S /TD +>> +endobj +882 0 obj +<< +/K 433 0 R +/P 1085 0 R +/S /TD +>> +endobj +883 0 obj +<< +/K 437 0 R +/P 1087 0 R +/S /TD +>> +endobj +884 0 obj +<< +/K 438 0 R +/P 1087 0 R +/S /TD +>> +endobj +885 0 obj +<< +/K 440 0 R +/P 1088 0 R +/S /TD +>> +endobj +886 0 obj +<< +/K 441 0 R +/P 1088 0 R +/S /TD +>> +endobj +887 0 obj +<< +/K 443 0 R +/P 1089 0 R +/S /TD +>> +endobj +888 0 obj +<< +/K 444 0 R +/P 1089 0 R +/S /TD +>> +endobj +889 0 obj +<< +/K 446 0 R +/P 1090 0 R +/S /TD +>> +endobj +890 0 obj +<< +/K 447 0 R +/P 1090 0 R +/S /TD +>> +endobj +891 0 obj +<< +/K 449 0 R +/P 1091 0 R +/S /TD +>> +endobj +892 0 obj +<< +/K 450 0 R +/P 1091 0 R +/S /TD +>> +endobj +893 0 obj +<< +/K 452 0 R +/P 1092 0 R +/S /TD +>> +endobj +894 0 obj +<< +/K 453 0 R +/P 1092 0 R +/S /TD +>> +endobj +895 0 obj +<< +/K 455 0 R +/P 1093 0 R +/S /TD +>> +endobj +896 0 obj +<< +/K 456 0 R +/P 1093 0 R +/S /TD +>> +endobj +897 0 obj +<< +/K 458 0 R +/P 1094 0 R +/S /TD +>> +endobj +898 0 obj +<< +/K 459 0 R +/P 1094 0 R +/S /TD +>> +endobj +899 0 obj +<< +/K 464 0 R +/P 1097 0 R +/S /TD +>> +endobj +900 0 obj +<< +/K 465 0 R +/P 1097 0 R +/S /TD +>> +endobj +901 0 obj +<< +/K 467 0 R +/P 1098 0 R +/S /TD +>> +endobj +902 0 obj +<< +/K 468 0 R +/P 1098 0 R +/S /TD +>> +endobj +903 0 obj +<< +/K 470 0 R +/P 1099 0 R +/S /TD +>> +endobj +904 0 obj +<< +/K 471 0 R +/P 1099 0 R +/S /TD +>> +endobj +905 0 obj +<< +/K 473 0 R +/P 1100 0 R +/S /TD +>> +endobj +906 0 obj +<< +/K 474 0 R +/P 1100 0 R +/S /TD +>> +endobj +907 0 obj +<< +/K 476 0 R +/P 1101 0 R +/S /TD +>> +endobj +908 0 obj +<< +/K 477 0 R +/P 1101 0 R +/S /TD +>> +endobj +909 0 obj +<< +/K 481 0 R +/P 1103 0 R +/S /TD +>> +endobj +910 0 obj +<< +/K 482 0 R +/P 1103 0 R +/S /TD +>> +endobj +911 0 obj +<< +/K 484 0 R +/P 1104 0 R +/S /TD +>> +endobj +912 0 obj +<< +/K 485 0 R +/P 1104 0 R +/S /TD +>> +endobj +913 0 obj +<< +/K 487 0 R +/P 1105 0 R +/S /TD +>> +endobj +914 0 obj +<< +/K 488 0 R +/P 1105 0 R +/S /TD +>> +endobj +915 0 obj +<< +/K 492 0 R +/P 1107 0 R +/S /TD +>> +endobj +916 0 obj +<< +/K 493 0 R +/P 1107 0 R +/S /TD +>> +endobj +917 0 obj +<< +/K 497 0 R +/P 1109 0 R +/S /TD +>> +endobj +918 0 obj +<< +/K 498 0 R +/P 1109 0 R +/S /TD +>> +endobj +919 0 obj +<< +/K 519 0 R +/P 1156 0 R +/S /LI +>> +endobj +920 0 obj +<< +/K 520 0 R +/P 1157 0 R +/S /LI +>> +endobj +921 0 obj +<< +/K 521 0 R +/P 1158 0 R +/S /LI +>> +endobj +922 0 obj +<< +/K 522 0 R +/P 1106 0 R +/S /TD +>> +endobj +923 0 obj +<< +/K 523 0 R +/P 1159 0 R +/S /LI +>> +endobj +924 0 obj +<< +/K 524 0 R +/P 1111 0 R +/S /TD +>> +endobj +925 0 obj +<< +/K 525 0 R +/P 1111 0 R +/S /TD +>> +endobj +926 0 obj +<< +/K 526 0 R +/P 1112 0 R +/S /TD +>> +endobj +927 0 obj +<< +/K 527 0 R +/P 1112 0 R +/S /TD +>> +endobj +928 0 obj +<< +/K 528 0 R +/P 1113 0 R +/S /TD +>> +endobj +929 0 obj +<< +/K 529 0 R +/P 1113 0 R +/S /TD +>> +endobj +930 0 obj +<< +/K 530 0 R +/P 1114 0 R +/S /TD +>> +endobj +931 0 obj +<< +/K 531 0 R +/P 1114 0 R +/S /TD +>> +endobj +932 0 obj +<< +/K 532 0 R +/P 1160 0 R +/S /LI +>> +endobj +933 0 obj +<< +/K 533 0 R +/P 1116 0 R +/S /TD +>> +endobj +934 0 obj +<< +/K 534 0 R +/P 1116 0 R +/S /TD +>> +endobj +935 0 obj +<< +/K 546 0 R +/P 1121 0 R +/S /TD +>> +endobj +936 0 obj +<< +/K 547 0 R +/P 1121 0 R +/S /TD +>> +endobj +937 0 obj +<< +/K 555 0 R +/P 1124 0 R +/S /TD +>> +endobj +938 0 obj +<< +/K 556 0 R +/P 1124 0 R +/S /TD +>> +endobj +939 0 obj +<< +/K 558 0 R +/P 1125 0 R +/S /TD +>> +endobj +940 0 obj +<< +/K 559 0 R +/P 1125 0 R +/S /TD +>> +endobj +941 0 obj +<< +/K 561 0 R +/P 1126 0 R +/S /TD +>> +endobj +942 0 obj +<< +/K 562 0 R +/P 1126 0 R +/S /TD +>> +endobj +943 0 obj +<< +/K 564 0 R +/P 1127 0 R +/S /TD +>> +endobj +944 0 obj +<< +/K 565 0 R +/P 1127 0 R +/S /TD +>> +endobj +945 0 obj +<< +/K 567 0 R +/P 1128 0 R +/S /TD +>> +endobj +946 0 obj +<< +/K 568 0 R +/P 1128 0 R +/S /TD +>> +endobj +947 0 obj +<< +/K 570 0 R +/P 1129 0 R +/S /TD +>> +endobj +948 0 obj +<< +/K 571 0 R +/P 1129 0 R +/S /TD +>> +endobj +949 0 obj +<< +/K 573 0 R +/P 1130 0 R +/S /TD +>> +endobj +950 0 obj +<< +/K 574 0 R +/P 1130 0 R +/S /TD +>> +endobj +951 0 obj +<< +/K 576 0 R +/P 1131 0 R +/S /TD +>> +endobj +952 0 obj +<< +/K [577 0 R 578 0 R 579 0 R] +/P 1131 0 R +/S /TD +>> +endobj +953 0 obj +<< +/BBox [35.5695 206.519 575.331 207.38] +/O /Layout +/Placement /Block +>> +endobj +954 0 obj +<< +/BBox [35.5695 191.669 575.331 192.53] +/O /Layout +/Placement /Block +>> +endobj +955 0 obj +<< +/BBox [35.5695 174.413 575.331 175.273] +/O /Layout +/Placement /Block +>> +endobj +956 0 obj +<< +/BBox [35.5695 114.863 575.331 115.723] +/O /Layout +/Placement /Block +>> +endobj +957 0 obj +<< +/BBox [35.5695 100.013 575.331 100.874] +/O /Layout +/Placement /Block +>> +endobj +958 0 obj +<< +/BBox [35.5695 85.1125 575.381 85.9735] +/O /Layout +/Placement /Block +>> +endobj +959 0 obj +<< +/K 603 0 R +/P 1117 0 R +/S /TD +>> +endobj +960 0 obj +<< +/K 604 0 R +/P 1117 0 R +/S /TD +>> +endobj +961 0 obj +<< +/K 605 0 R +/P 1118 0 R +/S /TD +>> +endobj +962 0 obj +<< +/K 606 0 R +/P 1118 0 R +/S /TD +>> +endobj +963 0 obj +<< +/K 607 0 R +/P 1119 0 R +/S /TD +>> +endobj +964 0 obj +<< +/K 608 0 R +/P 1119 0 R +/S /TD +>> +endobj +965 0 obj +<< +/K 609 0 R +/P 1120 0 R +/S /TD +>> +endobj +966 0 obj +<< +/K 610 0 R +/P 1123 0 R +/S /TD +>> +endobj +967 0 obj +<< +/BBox [39.2694 144.208 557.38 145.069] +/O /Layout +/Placement /Block +>> +endobj +968 0 obj +<< +/BBox [39.3195 94.9595 557.43 95.8205] +/O /Layout +/Placement /Block +>> +endobj +969 0 obj +<< +/BBox [117.32 438.605 557.43 439.466] +/O /Layout +/Placement /Block +>> +endobj +970 0 obj +<< +/BBox [166.07 361.774 557.43 362.635] +/O /Layout +/Placement /Block +>> +endobj +971 0 obj +<< +/BBox [220.82 298.804 557.43 299.665] +/O /Layout +/Placement /Block +>> +endobj +972 0 obj +<< +/BBox [72.3195 325.754 557.43 326.615] +/O /Layout +/Placement /Block +>> +endobj +973 0 obj +<< +/Next 974 0 R +/Parent 34 0 R +/Prev 660 0 R +/Title (Demonstration Required) +>> +endobj +974 0 obj +<< +/Next 661 0 R +/Parent 34 0 R +/Prev 973 0 R +/Title (Safety) +>> +endobj +975 0 obj +<< +/D [21 0 R /XYZ 34 187 0.0] +/S /GoTo +>> +endobj +976 0 obj +<< +/A 1161 0 R +/Next 978 0 R +/Parent 20 0 R +/Prev 663 0 R +/Title (Section III: On-Road Skills Assessment- Operating the Vehicle in Traffic) +>> +endobj +977 0 obj +<< +/D [25 0 R /XYZ 34 592 0.0] +/S /GoTo +>> +endobj +978 0 obj +<< +/A 1162 0 R +/Next 665 0 R +/Parent 20 0 R +/Prev 976 0 R +/Title (Section IV: Electric Vehicle Management \(Only if applicable\)) +>> +endobj +979 0 obj +<< +/Length 5622 +/Filter /FlateDecode +/Length1 10329 +>> +stream +H‰„–y|TÕÇ¿ó& K;ã{ƒ ¡¬f%ù$J-Z”¢H`!²„$$€ ²$l²ˆ ¨­– +¸TѸ d‘efXT¨ÐD”2Ó3“åÛ÷™ûÞœwï=÷þæß÷  J` ³ÿ nñ ógëãÚFŽÎÍrn)q`:zßè‚|£A©žd¬s\n}Á§ø Û±;ñìÂn|Ž=Ø‹}Øøñ%á+¬Æ*¬AærãkÁ78ŠcZ'à‚œÄ)œÆœÅ9|‹ó¸€‹øßã\Âe¬Å<‹9*⊴åïù·²†µœÉ•|“>~$wp ù ÿ®?Þl®â6!?a Wó=~Ê2®áûb`!æc&cÖ¡œq¨E þÉ1¼„Yt0^Ì@±V®Oá~Áϲ>\ÁUy‹±LE±7Ø¥$6ày4A—p3_g{vÀ2,g(0ˆÁ(Ô‡Äkè)ZoëÙŽv,ÂF†0 ¸‘/q<¯²’ùþ‹UœÌWUáRæq—1šëù÷òGi,­™ÎöáNØ‚ýµ´CwŒûü&ÑgØÝŠÞŠ‘þ§ÄbÍqH+ª¤hmî”Ïå„a3B†F”mÄ}ŒiF‘±ÀXhTϯ›-Æ.cŸñµ­‰­¥­•­‡-ÕÖß6Â6Ú6ÍVeŸgÎ~Õ 7#ÌhÓaÆšÝÍ3Íìiö1šN³È,6››Ìw­ «©ÕÂrX±VWk€õ°5ÔîG°£‰#Êíhí°;:9ú:F:²cvw¸P^yÝæõùnø|º è0ÑiÈÀ0da6¨Ž/ù‹´TÛd‡ì’#Œ`#, #Vu䫎¹ªc±Qel4^U[ݪ¶ÕÑZu¤Ù2m£ly¶J{‰ê¸bBuD™-:âÍÔ›:òUG‰êØÐѼ^G¦5Èl «×©:ZÝÔ1Fuœ¯ ©itõ:èûÙWã;£ÿA)M0Í/Í·¦Î€^÷í†ôvñ¾¡½Ë|Ã}ƒô:@[C_¸/Ù—îSºx¯i‹ò{#½YÞˆ[³NÍ<‘ž—ýßÝ<–ç„'Û½ßîéé>à>àÉsou++Ý—Ý—õ¼ïä÷a½Â-îPW­ëºë"à:ç:íò¸¹Ò\m\mŽ%9þhø°à¶Æ%ùñ¿øò¶£Š•MÚÞT—Âhnt—s2Dë)JNiMµ  gwÝc¢¶‚[{5”œu#êãÎâ5 µòºÎy70s”8 XÒ5pîWµ½­/þ·fýºÔÿáÜíL‹PWû©¯¾ó”5êù êôm¨å^eÆ Üƒ{9F©P®N×½ªÞŒxaêÿñêçÍ:9”O•J¨—ÔÛUõÞZê÷·R¢eLPû]^Ë|ZŒSŠÍTŽ}¤ž½C)3H9V Ù*e™Ÿd‰Ê²ÕJ³÷ø©ˆr%UiVæçç1Fs„ßòg f>Î œÅù–DJ4÷ñ÷ÓÅnLâ7<¯^ +‘&ÒŒ™Îtóe¾Íë*QÒœ°/ÿÌwØÉÜÂy”øoiÀ!J£lÅWø.åýüsYÄ!|€_ÐÖlÍxöà$%ÏŸøGäI¶a'vá1^ä ãtVøéH'çp(û©«Oq +KùWVó>>¬´:Í?p0¿âÞžÁÏxœßÑ+áÒNLÎàdÿÆø?ä5Nã|i*-ø5ϱ#Ç8LEÞmï™_ßoû™Œw”åÓià'?™•ÀáJ‡±¡¾a›²±1{2½9Œò1>ħ9Šë¸»¹œk9•sù“ظ‚Ï*›wr³¹˜‡y–—¥!¿¯'u¥vŽ´äXn»X,àS|’å2CæËLyJfI¹J…,–ò¤,§L•)ò´äI¾L“Y$KdµL—åR)se©Ì–)’*)–9R*e²LVÈZ™'‡åˆ|#䨓ãâ·|'䢜“oå|À#P“ð¹¤f"­HÊ´âYï=ÄÎréF”Tß(ÐqvßÒYv¨ Z1ñÉI‰±íÁÑÍš·pÄ&%&'Ä7n̘ž‰‰=ý­¸¼¢¨¨¢|¼³¬Ôé,-sN¯ÞüZuõk›«ý2Rtá6šKmß´S3ÄŠ¶’zI]Þû{<‘1bjZÑöò¶-)¾«k‡Î9IRÐ)f£‰ÎE#ó'ΩÛ).ÆŸ¯§î-LÿµÑÀN²öŽIÍ~ÝeHRŠ•dE3}䤌ÂAÃG8ûö¾·WF£»#ßðþ¤õ½<(gð=cS;N‰MIêÖ%Hû8oüyS5od]Þ”›ÙB’šêNC’ë·­+¥döêÝûÞ†F±Â{%¼c—q…½gþ‡õjŠêºÃçÜ‹‹A^˲,°ÀÂîr—]ö…òW\dËSX°Á÷7‰QÓc4)õí3 ©3L§SKÓi’¦‰‰I›ª­vÒ4ÕÄédÆŽ;!Òq·ß9÷îºÚô¯öoϽçžó;ßïû}çì½m›ûKn·#¡›Z‡&vôy×UÚk#ðW]Ã^ä<àe£5‚Ah¬‚.ßÇzñx… S¼IãÐP‡Hï”Ý.ýÁô Ó¡ÞŸ¬œ˜¦î~ ¶4òúµâç0æÌÆ…úÉu0‰¸¨¦NSëÌé³gO<Ü9õ aêÜÙM ‹BŽëé×ÀÍËļ9¼êÚ­CkÂo+}'tif†– S;'wNíä|;ð{ cE¾‚ºãc€µ«ÂŸ +7¸' &=Fp'+k¤%ô½žîî†S-'7 žh—éªá={†‡{lxËdWçóC[&»;'#9íÀzIx½eìxNñ“hÊBX—s.§9›öz«^<}¬ÝúÁ ÂÔPUŸ>ôM +}FwmX'ël¿\ð3ƒ] å.ÕiÁ®5p£¨LFÉâÊ£2qËŸ»šïêï ´[+ükZŽt<ñ!Jn +·°¿Rä&ƤîoJJ$€†CTh¶™Í6¶fx†Z°f¢œ 3å‚Zlö§Æ5É™ êãBÇÝ_æèD"sÛ GçŠZpt*Tu܇Š…תݞñê +÷x0ɵ|^w¡£Ûîk¢ÝuÙ ¡hô¡Çi¸Ì”[kÌkö!‡HþÉ䯻/ÿ`r÷ÃQhØãSòGýT +爆Y1¹[°ÙY]¨u÷»Ö¯_Ÿ2¶ßØØ¾ýcÛÎLLœaþñ…Ë…J¾6ü£SRQNgÉ¢Õ€‹r6ù‚‰yË‹ÛýµVv¿³¤Ø o^.Ús£´ì©ÏÖM [ô¡}ã²öFE3:&ÄáE‘UFIËÓóÓŠê‹4Ú㢠+n,.ŸRéBÇ1Lj}þø` :¥R{ö¨bn@jЙš«Ý›WuÔ7z­’®°±Æµ}õ`ËÀ²¥Ï¦g$2}îºö‚€.=C­M1dy]þUÖ€qKE‡…§±odwWS“k!uð£\6°{ñò¶¦¶Œà¡Cù’¡(IÛÚñewê3‡6ÝÉÏVñóÁ íÓh˜ý{ð-qa.z*¹+P×8îs–»—´ÕÓáй¶|º7Då³%â³¹ˆäzüS]xãôæ­Ç_‡SGè £ì™˜‰1ðhÁjâÃØHzse׳ϟZ¾ú»=­Ç&é`è$&Ñí¡Q:ý›Ž¹ð”NŽïŸÝséw§‡6?·£¶¼4?ËV’:[ˆ}‡î½ûJ‹/u@´Øå/Wi^îM©úœ<$òמËÅõÏñö„”6‡>š¥¯bÜCð’2K¼·+ñôò,}Ì'üiñ)&üó‚ªä§‚LŽWˆ‘hõ¼½@ ä+RAß$àÐÄçLc¿Ç´Mø5€7ÁÏcò |È /Xx€j ˜X ñ¶›Hôu¢,$WPÍ$[È$¡Ø„B¢:I±°˜˜è)’Ô•ÀÀ Ô6  (Š€iR$ÿ/sñk_$ù³*I>Ú:•J»$æž]›•ûúoèg}¶˜yì¾,fœ9¦¯TÑm%àf™€h´@`ªþ êDîç*hf5¤ƒ&{I ¿ä£M¥µ¤“AH#éò<-^¿ 茇^‘ú÷B§^’ÌN:DZTú ÉRéè¹ã·»0€Z»x™sßýã°œxsGšp’¨„ tÿ·Ñzà-Üß@üÒA߆ŸCŸ´Š}h?D›ƒç•öqô5`Í’(ü‰4 +ljJü8“8 ¾”#0H|ô/ðèZ ¼F0¦ƒßû¨DR)|]ðáz ñ‰G‰õ Ëð|_SôÑW‰‘BÌ•Ä+d81¨è×X'“ÄÑÛðÝ’Hƒ±§He>¹ˆ¾dðÏ ¼õ“Å: xÈâ±þYô<ÉŒ+G~CÄ ýQÌÄ 'ÚZ,œåô_@êĹ—ˆŸç6C:˜†B#rý5r´‡ð4øðS’,Þ$Éq*\bÝw‰*Îm?€žÈ_l'’xZŒ@#ô€N¬t$üW®Ó¡ Ã4ÀêPCô¼6µŠÞk¡ÏÏ Éö4Å^~ˆþ÷Ñžp:…_¼@-€Ó)\‚ç¹7"õeõRjÄëvúDj¼äåž±1°ZGŸ1?)b¼ïó óóƒ~«-Ö~ 85~΀ë~`5®ý|õˆ'¯Õ‰yŸ±¾âAî?¦'{ÎÇ0bMîEy]$¦MÄgŒóWÔ[¨÷#ƒÌoxášÇ{ù‡¯GÖ‰rˆæ¾™‰óblDÛÄ€ë}<Þœç½ý¾Âö#ÿ`žçŸDµx Oy¯…߇ß3ð\_ÅÿG >^ +߈î=9OÆÏ©ð»¢`úžæØs~|SvÜÓè‹GÎïÇJ-ÖF}edÜ"{é£á­nÕ ˜³íZ\‹YçÚÔByÆ4…ÄX­”øx°F‘gLC<ÿòßì—_heÀgwÏÆ@¢4->Ø.ETÈåLJñAlÒKê™”ÚZí&»w·äîö²»¡´("…Ö'­ÄQ¡‚AéCŠ ThµODêBÕâ“Å *…ø›/ßQ ¥B_4æ¾ùfç›™oæûf¾¬²ÿ>x›‹ö<>bî‹æë «ï+»Ž= ûxų̀¾¨mÕ†Öµ-6Nu|ÔÚÜþJA¿wþ(9» OïÓYé75ù’Íùè9[Ï5îG¨+³2jjñ·ð?äü„ìUè¤Ï›a¤¸ó²¹°žñyôßýgÈÛ÷â$÷m1c^‘óu‘{‘뀡ÿSÆ&¨ñ˜eœcÞû¯ùFܼ[ µ>Ç|{˜~ô´T¨Ñ7áÍ6À¸ù+²ÅS}÷±Ç)ôLQ;ÏQ£Ÿ²6/`çBe¬ÓØ™ðƒ¯ÁQz]Ÿ\é÷y“Ó•žÅÙó.Í {À§36žï‰kâõ1ú¯àcIÆÒòwzï½{Ý®pyÉ}Œðñ#æO²ÝçŒöl]oÛs¯9Ñ|—–¿0qSôÌ/r½WÑÈC=Wº¯NV]¯ÒŸT—ž›SSãÔÆ1j˜í¿î$½mu¾GX§ùæ|wòímû½>êèÂg±u‡¸Ä¥è,‚ŸÀ[`MÈ›n‡í)Ä:eì§æçûNð%ü}Nr>79.:?âï£r+ý´èöÑO.sV~ƒFG÷­¬½ï^}á.Ÿ?ÃÆ—{Ꞧ×þ"=ÞQéáms›÷.t|ƒ¹ÆTïè¡Õ?ã›úMµãì vAwF}C󮹓aý¿†y8ý‡³7.¸êÜnaÓ*¨h9ÇS%gÉÝàŽ¹-à¸{Æ]4ð»w—7æÍxGÞbA +› +Ã…@{ Ö` þ¿ Žˆû³LðbÞ+=:ëþ9ðtÞ'¯wùó†ošƒld¶B _¶ô:è·,Ý#ãr +I§ÐËì{ùÃÒŽ”œo,íÒ{-íIÉÝhétÙÒë 3K÷Èa÷ñ¤} kõÜß:<2êoyÒò§ƒ æOåaÉŸN¸Ïy ;©úy=Îüj܈ü4š_ˆÓ(óÛiœ¤þþ4Îó¨å·£´g™ŠWÓ¤ù»öLNTʃۚÁÁ¤UnÎDéÐXÒoŒ½JiÑZ!V¦?HÕ«ZœåQ…~žaÔ Ò¹Lÿ»4‘¶Tb©I]rž¿[eXFdj»Ò€—H‹Ù4³)_¦à…R2¼*–*8ËלqE:ç3¯ÃÉ T¢!TÊï¼,0WJ¿µý„__ö:"£§Í˜JÓhɺګð¸×÷q—ì‘IdEÊ2(ÛXÈA#]†ž1Ú‡ø'!AK(»™×ð­Tzƒko¦ôµwY„à„6Ê÷ç8×LÜr£-2r9T‹)ÿ.fÝl]/–ÞÊ­]>†÷×úûS€æ¶Ãˆ +endstream +endobj +980 0 obj +<< +/Length 6778 +/Filter /FlateDecode +/Length1 12054 +>> +stream +H‰„–y|MgÇ¿ç\7‘Iˆ=qî%aP4‘X†CM‡¶Bh§ö ¤!"BH#–ÄNb«¥J«íL;Fh«­¥jWj«ªÝ½±ÔÚ2˜5¹gž{“VýÑ™ó¹Ï9ç=ïûžçùóþ¾ç‚*#â»'4ž1hÌ4½rNcДÄÔõÙ¯løw’‘n·Uœ¢;–:<¥¼íÃá#2‡5 Ôv.P%+9)q¨D¬‹jy³Ä%ëÿÊöÚn¥íÉ)éã›TÌX í¾@àÖ£†$nï·{5Y¨P”’8>U,¼ 4¾¯ãÍ‘‰)I õ>Mšh¿áJ5&ÝZ‚x Y±·?5-)µ¬žæaÞz`H[棂–ºUfé•ä²#óÏH‘@»!bÓßjÈíx˜ZyçwîøBGhV©³š!Ç8ÈZ*}•·d£7›>1½1èKXM[z&°K€oÊ{oÔÑâ;üï­l¦ŠÂL¤¨Â|}G‹±;À†7ŽÔÓ:p¢> QªÕ¯c²áw°_`¾ÄvìÀNìÂnìÁ^ìÃWØøq‡qGñ Žá[ÇwX†¥XŽ Lã\œÀIœÂiœÁY]çá‚E¸€‹¸„ËøWp×p7p?àGÜÂm¬À,¼‰É*â®Ôå³ü+7°˜%œÀ%ü„·K=&0‹oðS}x¯s)7 ¹“9\Æ-"ÜÅ©\Î/ÄÀäa$F!+1ƒP‚bü‹Cy éd <ÈÄ$]¹©˜Žø e,ÜÅ=ÙÈ(¶A1¥lŠ)$VãmTA0ç³±>b!ÑŸYvdéË©ÀèŽÑx «ÎÌÅú1=¸†ïñUÞc>Gðïü7 8ŠªÂLãZ.dWñâ ’ÚlÇdvæ>ØìÞµ´W+´£‹×$úÃÑmteõÁ ï[â$½ÇqZRGZéÚÜ'_ËyÃfø•ŒP#Ìhdt6ÆÙÆ,cŽ‘o¼m|`ëýÆaã¤­Š­¦­–­µ­­»m mˆm¬­ "7â­ˆ{f l†™N3ÊlaƘmÍöfg³§™jf›“ÌyæZs³£‚£ª£†Ãéˆr4sôpôrôq pŠÓî¬â u†9k;#œMœÏ99“"4¼^XòÈæ±¬RËò­KŸ-Ðщ˜ˆÕªã[þ$5UÇ&Ù+ûå”ÃnøtD©ŽtÕ1MuÌ3 +Œ5Ƈªcƒq@uÀ¬:j«Ž¶¶xÛ`[š-?"GuÜ5¡:BÍš>Ñf›_t¤«Žձɧ£z¹ŽxG‚£·£o¹ŽÕQëCUǵb¿âÊP®ƒÖC«Øº¬ÿQMÔë•f-/3 Çý¤!=Oy>ÖÞ…Ö+A=4*YVœÕÎRºxh„z&yB<‰žàdz.æE!Eï{ÏÝ׋Eç‹’ÜGŠ‹Ú»º¥¹7¸÷hÏm÷mݾ0ß}Bp‹ÛßUâz亸®¸.¹Š\Ç]m]u\uÎÆ:ør`_{]ã–Üù_¶iœQ¬¬ÕøD] +£ºÑB®hë¸!ºžB墮©š>ô(›bŒÐÈx\«¡ä,QÞn*R"éœÍ¾™ƒ%õ·€%Í|ûçË[uŸè‹þ­Y?§ú?œ{’iÁêj/ÒÕ÷³‘«4(VϯV§oB )32ñGtâP¥Â up;]£÷Ôû½Ñ/!@ýÿªú¹ÐG'§ò)_ õžz» ÜÛÍ”W ¼þVJc*cÔß^——06RŠMPŽmWÏÖSÊ$(Dz|$[ª,󒬥²l™Òl w‰(WÚ(ͦzyÆ\F*cNñ*ŠÝÙŸ¯q"ßåF ‘0æy¡‹ÍËÓ¼¦^ò“*RñÀ£tó}~¦_ •ê|‰Ïñoüœ-ÇõÜÁ3¼ÎÿHEöà@ VÕ`-~ÀÍ|™fc¦0›aW~Ã"ÖdmF³5G*y^a7ãÖa>ų¼ÁR àxÎôÒ‘©œÌ>|^]}‘£9…ÿàVva/¥Õ%þ‰½ù/ói¶âÇÜÍs¼IJ¸˜Ìä,öd"ÿÉm\Ç/ù€c™'U¥Oò +‡þŽ1H{â;óó7ã3/“ñ¹²|< Ü÷’Y è£t+éa.V²*«±2ƒØžmÙ}ùûñEÎæ`®äjà"®àNã}±q1ßT6ïãA&qOð{Þ–Jü¡œÔ•ÚÉR“øG"ÄÁ Nç8ÎLÉ“ 2]&Ê É’™2OfÉ8™#©2FFËlI“t+2WæË2/‹$_¦Éy]r$[ +d’L–)2UÊbY!¹rBNÉi9*g䬜—¸å¦\—rE®Ê5ŸG &áN¹¥fðbB!†#ÄÍühÏq6•[¥¡²µ4C-ÿ{«D?êԨጊm]=¬š=+¼Aƒp€ÈððHo@!ç+¤¡Úœ­†Ò£Þ#ÂŽõj ½GUßü*Œˆ1áA–±rHµàFë}5EYw¤ìÖ\úw+2:.¶eT}§=¬Zõ_gfd÷®]»{câ´©YÏæÌÏË›ŸŸ×kKáº-› ·xs×Ýýãd!~±­bBØhÇWý¥g·ø¥Ýà««“& Õ|š-&öñ¥ó‹}†¾DAôs„uÊÏnßò•מNj×wT»é™ì÷â‚G·éÖ»aä°^­Ç,MO(»×¬;ºœ7¢Ž6â¢ËJ­ïl[ý¿|WkP“gÎù"JC¢" !H¸.I×$$„Kn‚Â%’‚Øñ‚@ë]kz¡µîl×Ñí.Î8ŽN³u];í^ÜíÚ­?º³¶þpmG»ÕÝNWwÛÝé`¾ìy¿/ ¸;³Ã„òžó>çœçyÞ÷S‘:H^­L#AJ\Bœw[`ÔÓùb“Õ~º¨ðú3ÔôQr÷mÏiͪÕÅ¿´·~HðWæL@“â`´T|•H.Rð¹°‰~ê{z{»žj‚?ÒŶS+ýƒ¥û‡q«8-»?S˜@&’ñJcÐj‡[¬6ëz]—^¤¿JÒlŸÞæÛ0˜UWc1$!ßþQÂÈÆ¾±f6j̹ +{µ/~È\Ê#m’“*2±H \ü&²%töô.Éé,¨îÖîÚ5yê@žS*knÍjÎZzJ_g¡FöH“wév]˜ûÙÍ”dkb2ý¹Xø`]­®Ží©7N`g¨¦{3 ¢Ámú åµ·„ÞDö#®•Ì åxÕá€p)o*­Xl@hÄâ*aª"ä‚pui£qÿÈK{ëŒZÍh_ïýïþ&‹¹¹¼épi…Úa¬¬0P‰ZwZfk…{pãúê>Éê&ÍúýMy[eMuY®&ã³ÜÊ"­­\W˜ÝáÎûÔ?Èe]ÌWñÝý·¸<5°õ(9wÐ×’ï´Xòî8øÿê°²0†hŒ¬U!\ñ £Ùlìžœœé}4:ú¨÷ÆÓ§7Ø<°Ÿ3Yu(TLÅ<áäd7 ™!+ÙVëø€€¦D¥ã'™BÆ“ƒj¨¸Ôsú¾n8/‡v<Ú~õ* 5¢ :ÙcßðzÄÃ2•pÉÇp'Ò`®HUBš}w~Ù»£h­¡öåþ`ªÆðæøËÕUTp£CÝ(´Ú¡ê›ÑÊ*È¿7¬-gë0„¿¥ª¨÷1;gMŒFb™K:qi©*›çüÜ¥s®ÆŽòÉm[&ôýiðÒÅkiÑÄû^^ÉÔGpŽ Îx¥œ‹ª{púìùÓþ{þa*xùÂO¯Pý¡¿…ÅS» ËÅØåŒ Š¢?N¸D¿ Búk¨§‚ö?Ùÿjg×¢ñúY‹bä;á ýQ8Œ«îÙé[œhÎq6'š*_Î'þªâSBúmŸÜ>(§GéÛGœ8íäJ…ë¹ z~»Œ!’g£æÿdhrà;}¾“þ´ì¢?Æ,7¡”Ò³ÐH£{ÆÄ±1X‰,˜ï-''†í[ü>‘`ãb“]ú +5ûë®ßxF>¿7LÉèØú3ænƒ¹¨W’¸lí˜\~Å3 .ŸR„îà·)¦ +çúw¼Gc´”§Ž™«˜>x088|®ý—sÃç6¸ÎGf\Àô"%‚€²‚Ižïzçûwîìã˜Þ0u i¬qk_ïV›É°Ãbò0˜øø(ÁDq Y!ßJÐ.Bi`ÐŒû½¯\9}âǃQàf®¦U,Û ˜ZòÁÛ 1¶!¾$Âæ˜Rr®|%°™óÁêßÙX{uøÂÁ€ÁdпF‡=uýbúˆéG0`ÐëôL¯+°qˆV@ü€€ee!"Öñ·èÙ’­¸mo(´¸»û›j,E56Ëám[]ƒ][aÔKÛMem%Y6©¦8§pE¦ÄeêÉm•ë4kŠSq¯dÄ]Èx¢–£+Éù_ߥ2îR½Ngè-vö­ˆ‡è Õ…La½Ç϶ŒJôöí øÚŽv¤Ìº=G_Ý Wi½³£ƒx[B„7俦qaYØÿᇛQ1¡ËTK”_˜›;áår3KÉjʹwßÈÍáSûýûg‡nRÒÐ÷ ª8t‹2³±'/üOø-•€lË"þFÈñ’gŽt¾¢D«Á¿±‰°->«-ßâh¯¨-®tud hºÝkÍêÒ-yji¦­¶¾o,-š¢=«Sù’Ú²sÉ^ áï¨ êzâ +Va *OÉã§2fØàmlëõïèë¬[þºE¯¯;b¢Ð_éŽOœ4hàš’~Tt­»“Ô]þ樥ÿÇa®Âºc×+;L•åZgC£­Ô Ü=õêjKŠ«+©Ã%dz€y¨%ˆ‹On lbÔA”X:_ ¤v¸’$sæë{JÕ½&W­÷ AªÏ>¤–Èt£6Ûxm¤„V×)!M,ºþs&ïx€½pð!K+ÁtZÑ‘/ò(k¡á esµØ× LLNnéöÅÿJoŽûÊ®·K-ŠCû§C}…ÙŸ66ħTë°nT1lÃ3LÈòN«Q/”MlÕïñü¨¬@•¦\sæ \4$Ú’bŽ—çt6ÓŽH½ÙøÈBf¾;-—ôNËR4"…ÂSáÌæœjf¤ÇmŠwLm÷´tÖ·6Lèª%º5{êêÒ¥U[›vt…tÖèž‹´ÉU£žXt¹mâmÀMÙþ¢o2¬b|iÎæ+[¬^o··Þ +M‰ŠÃyß75ÕÓG«‘ñ÷Ñß“YÎpÝg›½Åé2Y:¾`žŽ£\²´4yEöLÂ=#þ¯Uñ@®5x?{ œ]÷q‹Š»Oè=~LÖ†ç9a\‹7EíbjÃ2…¦Ì+HŠx‰ÕåÔÖÐ ±ˆËjós5ØÃŒXM¸ +ËŠ•'&ýdK¤>Éέoov®Õìu$ôttɼ޺h/)ZË{>ž>N>Ð#0¯2Z,Íë°ðèØ·„ÿê›ø™¾9ºcƒù2K¤oÈ=Jˆ±Ë‘}œ¸E=S”+5r9sd÷îò +ÉÓS“ÓÓ“SÓ­ï]¿þ«ÕÔj«UqtW†¢EZu,³v±ºkXô5¦#¦[ÔïÕ¦ÀØÄÉJš:Ó*ÁS©5§yFcÈ:î‚ÆÎÊly(1UŸ±©ÑØ-zµ”™¾(¤ATdÑ>ÁfG"ÖQ9ðø Žå«‹JÒ©©­å0ß(‘=Ïu/)¢0qøÌ'“ U@î¶‹ ä (“溆ûí-æfcOFas¥zS»·ÞÓZ¬:´B’œ‘ë­iÍ0¯¬Y%I‘ˆõ%g¶Yš{<þ^ ­Ò ^õ(R"SÖ¡^(ÖMwv'Ûfge9¹9‰‚iÈ6$Îü‡ýꎺ¸âo÷{—#$M Ä4¥¾BBbŽPIBÂå’Æ€2:é%¹K’\øæ"àL«u”v´È A‡Q;¶S¥-¥”¶T:VGF©Óˆ-­ˆÂ¤Cê´0ÓAK Ü·Ÿ·ß½p9é8µNÿh¹Ï½·»oß¾}ïíïͱ÷ +ò&;ë[;?—UÌ ü²ùnÚ}õþ®â²†•]Uu-+ņث”‹ÇbnŒÅw£øcù;À¹CŽ¿¾nÛ¶Ðaì˜ñœ’Á»D^‚Lòý!>è[ßý|¯Õ×Õ7üØ ubG,ˆw›Û±Íƒ³ä#ǶÏ;ú9´ŽË½õæºí[C禯¬.Îÿb^AÉT·¤X@|ûêîº%õž‚[üUô"tðÛp¡ó±róMùbNìœø:.øb¿47ÆžmfY…/mmèÈ\ò!¥c¬âxií7}ª0Ë®ˆs§üšLEÞ93qÔÆNu{Ñ{Å®ô$üäq‘*•hŠºD·iä1Äh¶ÀK4OÑŸÓlz‹‹aZ BjÜEÌ3–@› çÀó˜Á§t*õ¬>*z€àv ¨¼ÀÙNebåÊiT Si–¬¥àKd úÒ(C6@î6*;©¸ X¬J?P,ÑtîW 3°æ eý'c‰ì +c7¹½Tz—ÆrMW$Ô™/ÕuÿuÚ¹­*a×—$È•&´Ui¿uh: ¸0ÕÀlàn R÷_ñúBvŽ¡x‡òÅ&5¾AÞKs@sÄÐÇø å2ô¸¥ò4ˆoB~ b5-²ÅIš¢ÐA«lñUš Ü$záãFÈ7"Ö™Tx_Q9çÝg©ö•(=¬³^!G>L“e~ê ;ä>Ð + øêÇ ?‡ÚãÓeØi7@ šŠþ6Myü­t«\F™ò µÈ‡h²ñM…¯&ÈÏbØR,[È'ŽR¹l¦R9öLr¸\úqrÛÈ#Û>-3 3“|ÆWÈÇíªãx ä}b/Š¡óväfŒ&8µÄòˆ«À)Ê7j(Sü²Û°O¿ƒ<| Hý94]QÍ +€|` 0Ê~R Þ‹iÈëaœ8óÉT±ñjûÉ[g‹ZJgŸb¯¾ìAûoA +쎸¦ívÀ4 ÿ¨Êx|9:FĤl<&œK*gr98Öã}œO:‡Øî 9ù|€Ÿ>ÇàØbî“À Ìñ2üýÀ&ðj¶É™«-®sB.b~ƒ*ÿ؟ܯd8G1§ÊEgÞæøzx}ñµwlûBÂüW1ç˜ÎG¿òǫƣÖǾ~—_86ÞœövÈïU”má¹™òЩ(×W9>}{ +úûaË\ÜAo£÷Ó^ÊVgò1óÁ·éóœý~¥>ªVgña´?‰=p²cà_£L£´è¥9ÆŧÊ,Ì_ƒ;döÅjÆ»bŠZûyìƒÄá^´e áË} köG#èjŒá}ç0ûí"xœÏ²}E¸æP³1üZŒ=: ò|n£o˜\¹z¼´L¼‚yôœ‡ [¼Èrô‚6ãL°Weú ªW:ÂS}g!÷Œ×áŸ7±Þø_ò‡ÚŸOcíSà£g°gNÁFrÌcŸåœƒl†1ºî±/ËÀŸE½ŽÒÔ:gÀ—:·”®°Î{Ž ÇÛcŸR~c{Ùoðb`(Ą۔x]úVº¾†wëâÜÑ1Ugϱg˜¾ñÎ+šïJìcŽ7ò;ocúchg„`ÇJÌ%É-ÃÆ·=ʶL~cŠY蛂}w÷ÃZÐ+4I@ÿ2 ‚}Ó€;ëøU÷rÄ‹5\ÿ'ð¿A®Œªýë‹¿•ÕÝ`Íï€Ç÷©Ü +ÿ}AÌñ}Ð> ¡Ÿª=rldû”mËT^¤ñüš÷Ðy°&2¸Ù +÷ôFÍÊò +¯Yè‹F̦@ ÇôG»Ë̦Hw8î +DÃhŽ„ÌhoxÈ …û‚¦Ü0¶‚Cæ ŽXæF+ÌÁ Õbñéÿ˜Æ–Uõ+|u%wöˆ Ôõw­ù­Ážá¾€õiz´—šnL×0›×Š­`·µÝÁþ€µ~ˆWl<Áýº_aê¡^Š’I•xGW\-¨m@­ µ¤Lò£­›ÊT[\˜B@z£ Žtm&ê½hÇ}gá £Î÷ ªù#ø7i£â£(A¥gÔ¢~¥eh\{m´~²-´Šêñ5à£:*¡;1&@(é:ðJû|jíM}èµ’jŸFÃkÌõW_ +>€–nín7•|Üÿ=ÊŸQ¥3¨ä¢àà‚j^‹Ö«È8Qü$ζ¶¿…5\ï÷OiŠHq +endstream +endobj +981 0 obj +<< +/Length 10170 +/Filter /FlateDecode +/Length1 18097 +>> +stream +H‰¤–y\UeÇ¿ç.›²¨(Ôˆç^–lQ -‘0Kq©,¬ ©$o†5i”fj暸›fš[‰¥3Ö”c›ûž‰Rš–ij.Ì”§ç^Éefšþ˜ó¹Ï9ïòœsÏï}ßß÷= €†(Ìž}ÚÄQ4F[¾ÖÈÎ-Ìq/p¾6`'À¯oî°"ËùX„ðŸ¤mùî…mÊÛûGùÀAÏä/x¡6] \¬ /g€oˆU´š®ÏK(пG/­o×ztAaQñ';Ýë´~h9hpnNóƒM×I)ú¼› +sŠÝ>­|ÉOh¾õdNa^êÄï"´þ +`¸?]d—"H›æéwÉs¯‹Ÿëк>ƒ¿À^ò!|à'Ê8Í(¸tåhd2ÆO$Ø×1õ7r:V–æ´Ô@ZÊ=)°»VöØ­Qblc„V_÷ôÑ–5žC= <ã4ÑñÓ’DÂ!Þ,Ô÷\9¨Ùâ- þ÷qéNþ¸Uá4£Y˜R<¦ÃÄ4G$Zè{8áB¢ƒXÕê‹ñÇlÄ&lÆlÅ6lÇìÄ.ìÆ|½øûPŽý8€ +ÌÃ\ÕùŒå"|¥ëà *Q…jÂa|ƒ#8ŠoñŽá{Ç ü€“8…Óøgpçpó1 0ZE6’ÞÏç¹M¤)'ðî”üJÒ˜ÇI\ÉÝbq2˸Wœ<È׸ŠûÄÅJNç»Ü/Q˜ŒWð†`ÞÀ«ì@ê°\d±¡„ñìJŒÀ(¼„§1ž}hÊq uRÁvG1•~¼/³a1‚·¸…ÛÙ–í13Ù˜MÂP¼À@‰‘pôDâM¶ä˜‚%lÄ0ôâ:®çpiÌ%,á§Ò„K9’Ÿë4-ãhnärZü€ñ„„J¼tf>Çûx¦#Agq½zËd-ùë6G$jíAdãYŒÐљ½º¢®—öº67ÊV9h˜†ŸÑÀhd„-4c¨1ÂgŒ7& Œ¥F™±ÙØa”›Áf¸av0Ížæ#f®9ÔœùRä¼ÈsV b…Y.+ÖŠ³ÚZIÖíVšÕÛr[#¬ç­ Ö +ë§³±³™ÓåŒu¶vör>ì—Ãìjä +s]çŠtÝìÊpe»òb¶Üð}M`MèE³Î¶kmÛ»"ý¼ +,Ä! )ÈBŽ®“ùªà ^pUð¾lͲ߀á0¼ +bUA‘*¥ +&“…Æ2c•±EÀ Qש‚$3Ó|ÔbNŠ,Qg-¨‚FV¸WA¼•xYA‘*(Qï{4­WéìS¯ TD\V0@«ñ­ixõ +hÿlרJû¤j)ÑxÖ#Ê^qÉtuǯ5a]çºÝÚ»Öiëu˜Æ­vœžoÒh®ýíëÞ¬K¨]×îÊ=‡KêÐêåžrÕ÷ÕÎêcÕWí¬¬N­ÚUµ«zxÕªªMÚs¡ê‚žZ]uÈ›É*Ge Py¦òTå‰ÊªÊΕV¥õUÊþòÀû³7˜¦WÑd­Æ^JoÕ%²LVëu­¬“õò™ì}RáMÅ¥[Dù,‡½%=‹çž³WÞZg+Zgꣵ–¼-·I¿§ß2êkqF|})õwyVŸð<»–]Wsè7¦„¨£=Dø³z~ÆIJ­:{*Ö)N(/F ]XŒñxUÝÛCWiúþ^ôÆ}PïW/oñ’)^Ù´Dý·^}½´Þ×ÉܦžVos»„aŒ2f¹ÇáJ¯1¼U ôŽRl§¬…2,A)¶R9æ¡X™rÌC±tåØ*%Ù>VŠK™Ò]I6ÝÃ2Îd;‰á?ÅG"$–9ÌœÈp—´“DþÀ‹<É_˜Â;ù/qÈurƒ´•|”nžb-?æ%DKI$>È~¢ŽKe·òkþ$¾ò'¹‘¹|JnS¹ÍÏø%³ø“ø‚º³?çiÖ1Š1Lã]|‘SùŸà´ËŽìÌŸÅOšËMϹ2r§ñaâÝÞ^b)7°œý˜Ï³B>À<'Â.ìÆ¬RçûK¤Ü,©Ò•¯rðinânf…„s,g):²FL&ò¼ÒEÒ9޳ù +çð2›O2’-Î¥èQç3\\OÒû”ªÏq!‡q-ÿÎc,qÒ@nç³ü†=y7{ËE©•:©[™CÃPv†>ºž}•¡þÊ P#È6•§ å=Y)sd•L“R*e2]fÊ,™-ïÊjõÍ\¥×Y*“7d‘,”¿Ê›²XÞRo­•ÔUËäuY!óåmY ïÈ}0\Ž H¿/B=A[w,ا5ÎxÂŽ·ÏËY¹`»í‚?ðÚÿwŒ×÷)Õ½ÕsýGï”úë }{·¾÷ïCt+Fáéqë«=Eé.=êŸ:‡Ñö ý6ó³²ò¶õÖè‹ô«îÌÐÿ¼)Òw+/Öp*â JùEÍôc®›´3íþØœœžýÈÃõÏz°ß½}ûôî•Ù³Ç=ww¿«[Æé]Óº¤¦$w¾ãöN“;´Ohצu«[ZÆÆDG¹Z„7 nàïçëð1 !n镞m•Åf—™±Q­<õ¨mȹª!»ÌÒ¦ôksʬlošumf²fæÿ[fò¥ÌäË™ ±:¡Ó¯|W]lY¾3cÏŒgCX›¨dˆµ©ý!eí¸^!‰¡Þýþ»œ²€Éùba¥\,à­¼@$&Þ#ðhoFV›§c¾h•@4Q½9ûût¢·w#¾|4‰F#¸ŸJõôt'#Zˆ¡µP7VÔ‡0ðÓÖëAíM6þ¹T¬s_÷ó.›ƒž˜{i|†·ø˜²6‰äZnî¶Dý¬/% tËÉÉÜR¬O°Ãý=WýÙa"ö!ùgô²ÓŠ–í°‹ù ÇkXhÛ».îO&ûñïKkÞ~ûÂÚéÕóאַ¾s~õó·¯]½}ûêµÛ8g³Ð|®‚~$ߋÂì(¾˜UÁ›c+ÄTbDH´ +ö nF´ ßòø§DžíÉu2®!Q¤s/n¾ÀáË©ªWWLþºôYFFòƒú†À(Uð†ùjìv¸ƒÝüˆ3¸—yü1§V?" q3È£îÌžçNó(Eyx%ó8u…j%ÑôHò–HlÄãé goÌÙëôõ¦8ƒáx*J7.`§‘6Ú…yJEž’Å0¾T¢Šñ&â`2E ¥ e0~EméöïwFöíD;ÓŸ;¹K;;7wöhÖq©Å.Œˆ|¤%p¹X‡MŠ´µjÖgëÞp‰øe -ns÷©{ÃÑÍkË'×ú{Gö&r©Åí»™ Ž]|=æ¤|1Q´ð¾¦^œ>_^#Ž¡æŒ–C®vÎÖqІ‚±n\‘ñ_Çc'T%ÄÁ¤RQÜpŸ¯¦À¢YO%Ÿ/”I(eè/³>› qc/ø\¬`c á¶;v7)cm"ãò½0ÆI‚ÍÇ +l2ÉsÃyXÞ6*SnuÝÚ>Åðm±Ð.Fžl·Ú÷¨nJµñ¬FJ§NIÇ¡ê*‘#ÿ‰šà\hr +2l0sÔ«©ÓÐ Kœö@t0ýƒƒýŒCœôpËËœç2ÇÏÞ{õÕ{³<'±ŽÝp°f(žÃÿíGÞÄdà„ú¼c5w“O¸üÔßÓ5°g/xþ;x†³æºÃiÛæ™hà%Àc›‹!¬’4çbžÜ÷e™vR‰‡×¿×%W/n.A…‘HEM;Ê77‹*ëg ‘„=›NÃ~…ª x2„Ép &f­á­Õ0tâ½ÂôŸÜVêÅfçÏÈ’õeu¿"IÖ¶¢VI&8"ôäÁ“IVÉFžáx«]Üü† z¼ôî»’YMã€ãS€Ã‚(„Ú3M¢¸'ÿÇ ¤s*@jž!ƒyÄš' ÀšÝèíuHàƒ ^¸6p«EãÝIMó¸j¯Ó¸l.|!¶?3ü•³ÔÒËg¦K…98΋üÒdbÔíšNÏ|æ¥â©_m3/ÞÄ ÷U“yò +cVDç–¦žk:Ò‚l(߈ת×Z?Ðp +ŽPÔ<èःòLm(ñÍ_½ùƒÑ¼sX鮕å…e¾d›0&ܸö“_x(Ñ1賯œõ­¾lc,¾!Y´ø #˜“‹À‰ˆ¼p²z!~©ÿî°µh"Þc­…ú+W¾½ôé7OÔb¼~å‡7^»ï¾qaŸ~ð©¡–Œ×™¯Ñû‡€–w¼m•Æq¨zêg°ÚЙKÿá»Zƒ›¸Îè>¤ÝÕîÞÝ•õX½mI¶d;¶°%$ÙcaˆlTÛ<ü˜¶)PH‚(¯M¡” öŸ6é$ü(éÏN'MÊL~§¯IhigÚfRÒ2S:Ì0“š4<¹ß½»²$Càݕ<³ßwÏ=ß9羌e¢|ï¾Ý¡½¦î¾€ïƒH¾N®o“«Ù…ºQzNjöùø€ËãÒ㾦°}©æwIͯÞ÷÷êêýŒ\Ë_’jE݇j*L ÔÈ´Ôâ‡ßx¿ñ[µm›>{uf¢·F6Zö‚:,à-´^÷¦Û’’ÇnIÞÆjð¶vPM5“ÙHDh¼‚`õdà°jÖ¼]w82Õ>£ø,GâìÏ!Õú‹·¬*:Š nAòGÏOïÜ9?^Ù8þ£}äºÿt©têT©túäôõÃÏüdj\¯OQæL­ƒ5:¨åÏ)Îák*­ª¼CðWé·ãÑñŠâ‹ÃhÃÔ“ŸþàqI9 ƒ—Ük¬¯´woé.’éCÙt:ûA¥ÞSP/H5ÃÄ9Cà +å¡xhG +N!:,ª¤ìÑÔ#…yGUhª‚öÉÁçW¿*ºçÄ/ÌIÊùýûm<3·zÆ_.k‚ÆK_ýú9vàÛÂ;Ü(å$ÍÙ;}ÈAEâ…€P©‘6{¯½¡ëö@°)ìR)²x3cÖjì ÞÃÇhaÏ5å³c}¹xßȶ±MLÂOV-²)Xjë)ìØø’=dQµ~a¼O´66»á]v Ào«u‹(šþÈö ûŽCǘÀŸ—Ré² ËÆø>|×*º,®qøÉï`€3íB>#‘1|x›Ìõ`q‰ìŒvC‡éS áÛ€!ìU~“¥×Ð>ðƒ«dÈóƒƒyòpÕ~åú5"FßÌoا¯“ç¹óóPÁ +.CÕp!;c3–0Ð{fI±q¶’Ì9óÆóMëà£w8çMµ¼ú +€sÐÉÝ3•æYUTÁB?gE†Ç‡W2¿.¨4 •”;')ë8»àj$ŠáÌœ~\9 Í#ù–ÍuPQ¬.sHÙã²ýCw¾ÒÀ«0¹·è.Y€DR>ú/cfc‹è¿3 +ÕF­¤:sž¨Ø€ŠþD×”í<Íó”ÇÏ·oã3Kòã&8PK†UN.˜FæIe騒©ÕÈÆY°Ò´A±ß¶¥¦Wu­Š¥ÖO²ç•èîÔ¾]’²^F,׸™õ¦õŽh1¢M仿GÉDÄCÓz§&sª}|[ù’EY„çÜŽ"ŸoŒàä?cú˜»”HÅñiKàŠ¡0%Ú¨"l!…‡À3³ºy¬²c ‚tjHq%–g̰ÎÑ«zÒ+{W&:f›ü¼U/`*Î(Rp,6{´gf÷È–ñù ÿá¦`À Ì|såSopþÄøéu°‹‹ŸÑ7‘Š‘äá( +ò› ÿOòˆÕDjòàèøÉó—N¬N#›áØöX~píúX‡lÑl¨©Wºtîì¼ÛŠ´„NÓÒШ22$±Î¤†¬²ˆ±>˜`ã‡N·¨Pá¢çC¢¯¦s`̆ÒËl!ÎÅ“Y»©Ý4µv&UšÝ¸Š")›ðÆ­iéêx ËJ뎾ÚyŸØÃÙ­žÃZðnÜ´iÌèèßô§À·¤2_5N©*ÙÊñ [q;àv¬Õs&&VØM@YB…p<ÝuöÂ÷Ží~ÊÊÈrù–áÑìL*Ѧº{-êå çæíilUÅÁ>ð(Àè'ís}=xÖ¸\„ÞIrJ§hi›DMœ7PtØiƒ5¿zøî@½He³õ{Fv¬.Õ|É`‡êݱkE Ù¼îHDKžDú¤ŒÚb¤ÄžÝ²EѺtK(¶FRVAÒ¹#‰Š1 + £º©H®¡‘ë,ú=|¼¨5ˆ‚\d ™»†ÿ× +¼¡ïi|š4°Ó³ÕÌÁVgV&GÁÎÙÆúûzGw<}dg{!ȱHV¤K³ú +G[v&ùa˜CjBgõþ'[S/>WšÝ2òàÉKôfý×ûÒh£˜‡mmŽE)ONv‹Á¢¬€rÀ<S·,f¹2u)ȘFø¶Ë±©C£’2ƒäæ–l†È>®/ûG‡Ê×HæÉ´Å"å×V +ï1÷œ8Üg´4ÙMsM[Á')§Lcý½¤0?†ú‚bš>ýopâê®+Yc¼k©'×)j +úbá$ÐîÚžð’‰ðU"IòEÙóz¶y#Xfv`ûSò$åϦ{AS·“à`§×ö±{¸7Uõ/²ëôc¡šðù*à|¸ŠäW_ÆÌŠ5§¨ÛpÔ:Ôª(2àë¶ëƒúÒŽg:Z$e4‰åŸ“Œö ч5Pá–¢ßË DHZžøõC1z|à7ÏaFàÿ`ÛøÕÞÜ<¬GÇú3‡¶­¼Á¹Û³m1½edMú¨ÖvЩ³’œtºQ£'ŸY?îàÑ¡s²Ómw©ÞÁta;t+.þýj ®ªºÂkŸsßçÜW¸7äEróàãm^<‰@†„b‚òª-…0‘0È`«µ­Œ­Óò§U¦3mUdê`1P[a +)Hi )"ƒ´¨C;Ä–Ü~kß}Â%Ä™Në¯Ö{æ»kíÇÙ{ío­½×ÙŸˆõړȪãª}¶œVÓ›’êˆæ·Ç,Sû•ó·?ÇÑd•VgjWùT~ 'ü›­ó'ɬš¢y½õ>£—ÝÖ‰²]As5·\ç¼jêæZæ´W&Ô«‘ {jYЧ{™ÅYØ3)"žØ¯Yž°ÃåøÚs-«fÞÚ‰s¬"xÇ—ÐÉÚFy¢ÕM*W d>€Ü_öϯ‹äéyl~D,³2‰ÇŒÌfN°É¼˜;÷¿ŒÃ£BfÚô2|¸ã“ÞáÔ}Zþy×mÛ­ÛBݸCô,Ç¢3dÓwÛܦa7<â€Ë´ã1ôœXŠ{„Ýt Õx ;%n]:¯7†35˜êàõNpÇ<ùž GŠÃíY7VÜ`)I $îbw\cFÞg˜•ƒ~¿ýi{h™Ï¯7·Á¦¹0®ã^Ýïë Ùûì~+Ê ¢Áð‹~7.Ñ4ôjâæÀߨ[ˆïBŸ?Ýžo3 +=9ž1Ñœâ°M+ +-3ÇŽ–åøƒq|ØØÚg†»|~ÇÂy¿ï«asË3f¸åö”}îàÀ‚nŸÛØ´É€ö_xPÚ¼ÙpÿÁ`Ëxʬöf;üé¾-僷™#“~EÅè—ÀïEÇ#黃fQq@ßï³ Ô¬Xj–Žq×2àÁW`ÉX­°ÔºØ—?Ýð¥+xóâ}|Üà¬!ÑkrùÌq¦·æzÒ\­°K¬3‡¿MǨÝ41W¥_ÝÚMÓ¼¦Ó}ý¢Ì#+}†Óýé5ŽPÓ;14D¦é­äâd†Þ—[³Vˆ>ú n¢2^ | 7Þ™ý¥eòs¦¢ÂbÝ‘ßè1lyzFöü”éΰèó¸œiï¤N—ìa Î-´óô±3‹ý÷|L†~5t„oÒÕ¸÷á´gÂ&´é‹Ðï×h{–²ÇT?âÖ‡u¥Rª” Øc‰ø«âç(0pªËìâ J³•cÝki +xTHÜ€~CÊ +YC¶Ýäâì˜ +§Šøa•£Ô ×|ƒZ˜[­ü +k/B¼` }À+äÓß'ŸÍ}´ºØr”B¶Røa܃+}!Eõàí ð•À©ô×oe~âoKnߌA€}7ƒ2¥?«•ºÉ&ö‚¯.r±ˆâ/{Pÿänà%à5`P à”‹£½_Æ“K*~ØŽÛâ…ão–Œ³"ŽŽasl¨x1² ÜYmOCQØÈx˜‚~éÒΣÀq¼³ýA`ôV9Û–˜³Å²é¶8†*~eì2¿Ü.ûp|cnljù,û˜#+FÙŽM+.™{Ë ¶vÇ&Ú`2G|Æy]b˜—øß¬y‡m^{ü¼5ž5Wò¸oPñ¾®Æo‚þX2·öyüïs9×HÐí ó4‚ƒÄŽÿö¼Í<Œà@ög{­==‚Ø6—íEû7 O*´3P÷¤Dò5Z,±]|ÖÜâîše¯´õ%å³îáx*O>Oäùq‹Û¿&Û©âj:ﮆìf@ßž4ßÙ;|¥Ú˜gÄ›žÌŸšçÄ>TmÌ+گƗ²çeÞƒ°áœŠç¹’#öi›ÿŒGèÿ(° ïüBJ¶íÙ‰³„çåyò‡ÀnÎ `9sè{ɯM¢í"êvoR@æ„ó*6N@_¡ò ûdeè]4I悳¨ûèôý'ô÷ÈÔ—B"i½”c A>‚üÅü_O¿„½µ—´%ðÖ¢Ç($N#®Ö¡®˜ñ@>0G]+ðŸ!joÙ¡s~X޶j*Õï§ä‚m5¹õ0dúHy:WŽ1N#ΙÃÈjΘ b†û¬Äû?–óÔé>Ô?Fn-ÿq{¯ôs"g".õÓà‡¹¹k æ×Ÿ/&ùz¼˜?&Š©FÇßåxÔ ÑwÆêŽ_ÕZ€e°q?Êíd“ëœ +{T¼É±v©=Á>aŸÇOIÞØŽ#ð_·2døDrÈqÄë²¾x¬-ȃ<Çò©<'ùüþ!ìRù_«GMöwÞc#æ-ë©4E7qöÚã°ãkò|vƒ—˜8¼º‡ðN7¾3ç¨<†ÿ×BÈ#~‹öyÀ·›‹i†xKú8&.‘C»—\ÈÛ1ÍDnºˆXù:Æþ~ç `Í¿ú1Ç€µwµ}XïßOã\„½?…¾xFžsgå¾}QH^¿`K—ÏTù,¶í±]±çØÛñªY½æáµË{X)/-›©í\¹nõªHSggO¤q]wq¤é_ÄWKOQ>gŠ¾Ú… Cîl\8㣺pk--! ^M #wZ&¶ñÎm(ø ,P“‰‹¶´ðZ]¹1úø eé¿Ð3C!FMX°ðNæœï|çÌw'™Ì¸ÂÉ;³–rˆvóºšs|=ïm]ÚO+Ž´}Ý“Ž+õyé(e—uÏ–%Ç÷Ãò¼tK)òÜx6ÍÍ[%kÑ-gKm™q|¯h-\N»Eq”ìo3]Ò­² ©ïXº%Ã¥_ÙÒº’–°K–|⇻ùsqtÊ.x°(À(Ðá:$áÜ ”¡~‰s¡LÑŠ,ªÒé»@€+çr O÷,eù½j—8â9b|BaElB’ìS¨P¢0çEó»du˜°¢ËŽt<òJ‘Š ž'Î%öð5rÈÁ8d!MÈ„[ôŒE’au–ðãH=©{¤cÑY\¦Z—°€IÊh¥!/¤ô¿žý÷é\"l#zçòz´Çýþ"=iÛQ"d²£ù%<‰:·×åÃzÛ{ü hÿ©Ÿƒ/ž'Ù³Eƒ))˜¾È¼²`åôyöm}g½»[{›d«+‚­5g V {)öðY6k f} vu +—¦ÞOi|Xg¹ ÁƒgFn +6J…#"²D$2dªs[K--ÑÂxk«Õnu[};d4¯…©æxSóš¦v³‰[MLl~ØÔf6—ÉínöÍÑR ì6Ðk`¼;nCÛ­c»Ž©úL][®c¢†[µv­[‹¥j¯áAÍ«a"ÀïÁn µÔƒT } p9ÀÔín·«ß«Z¼Êª7«±F“\毓KüUò%?mœâLj‹gøIã?nôs4€Çû±Ýd34ûŠá+ô ~€»æØ—ØÉ±ÎÀøÃ®u.äB›šxÐé_ëððþ'ÄwÓ«0”ë åîŽmo¥§Ç:"Ä@ô´i>òUÅ<~8”R•ž%Ó!ÙJÅD*3t`úªç÷cìåÀ<÷è—§U™M +endstream +endobj +982 0 obj +<< +/Length 6018 +/Filter /FlateDecode +/Length1 10759 +>> +stream +H‰„–yxUÅÆß÷;77KVšœ{‘€²6 0ŽºFgeûæÞ[i$umrưAF-#ˆ6ZÝ#ߘkÌ7 +·Œ÷Æ&c¿Qb·ÕµÕ·5°=mëdëedj˱ÅΈ}3ö®j†™Ñ¦ÓŒ7Û™‰fg³«ÙÝL1]f¾9Ù\hn0·;‘އÓïhãèíèëèïxÕ)N»³®3ÂílèŒu¶t>ïìLkv ùՊЊð6¯ÏWåóù×e_‡‰vèŒn€TLÀ:Õq”?H}ÕQ,{e¿œ0`Ø¿ŽxÕ‘­:¦«Ž…F‘±Þø@ul6¨ØÂTGCÕÑÙ–lb˴ƨŽ;&TG„Y߯#Áìô£ŽlÕQ :Šý:êÕèHvôqôs ¨Ñ®:ü¨c˜ê¸RXQûjtÐwßWá» ¿¡Š&hʱ¤ùVUÐëyÜÞÖÞ-Z[ä{Å×[ï½4…ø‚}í}}Jo¥¦Hï$o„w7üQÔ¹™@yxù{Ö³çj¹£üLyšçPyhyWÏaÏáòLÏfÏ­¹å¹¥yÉÙžcz‡GÔ˜íþÈ!âú9`IþbM©ñcu ?õðSÿ‡s3-L]mQ![}?3”êùuêôbT²D™‘‡çð+S*ÌVwÑ5zW½ß)x !êÿ×ÔÏýtr*Ÿ +•P覆‹j¼ÝFyµØò·R¢Ó˜¨þ¶\~Ùt°…Rl<·*Å ‹W’-W–Y$[¡,³HÖ^Y¶Riö w‹¡\é¤4›fñŒ3ØLs’WÔ'ìÅü'ðn“©ÇCtó0=lË,ãUÝe‚$L¢™ÌWù Ëù?â$X"%†/ñyþ…³“¸‰_ð¯±JBØ›ƒ$\iÃ|ŸÛù2˧˜Á|þ¿ãže}6dŸæ(%Ïù‚zö±%[ó4¿£WB9Žs,:ÒÅ)ìÏ•Nç9†Sù7î`öå·¼Àß°ñ"ÁŽÜÂ/y†×•aµ$VÌã\¦0•çNþƒŸñ>s8S¢¤>Oð2ŸÄ@Œ@2ÛgîYLÆÇÊòq4pÏ"³8ÔOéÖÒaÖ0’Q¬Í:ìÊÎ|–Ø“¯ð÷œÇ!\Ãu<À¥|ƒYœÎï%€Ë¸ZÙ¼_3 yœ—x[jóF ©»+µÓ¥‡sĉ“¹œÅ±œ-ceºŒ“’'3e¼Ì’ù2[rd®Œ’12ZæˆK2%K²ež,å’+‹e¡L•B™ ù2QÉ$™,2EŠd‰¬”iR*Çä¸”È 9)erZÎÈU¹,Wä‚\´\jÔ$Ü%7Õ @b¸#Üp„;X˜à-e+¹Y!;ªrÕòѾJýmãÕˆqÆwhŸ”˜P/:ÊžÜ8®eœ¦Ø†q­­…œÕö²H¼ÚÚ¯ =9Oûh2]ô­¬Ì3t¶cbXYJ +¬ƒG’ÏÅ|¹ƒZÚvhÑ1Ñ.ÑQ1¹qŽ¼Ì£r;>·¾ï¶*(ð]¹Ý:Ò·OcêVÇDÆ2:J›&EthßüÐùcyK +ØÚ$¾š%êA*P·i8š;›21’‰6ôÞNMçÄ‘YÞeåm‹‹e[Õ ´1EÿQKY­1ÁÐã_b¤_y6 ebÂ/u¨ñM[Î*Ë_ðÑèËÊô‰Léý´^Nõgú!ôC›ò\ÑÖŠ\ÉO6²ƒ_Ú·39;s”ZöÌ2ÿ=¸p•Íñqú¦HÎH½p—}Ì>@IDA5É>µP¸ Ð('De h¬çà†d÷¡ ɶؓM©s{žHk9³mbÇÈÙmã;âM‡†]:ðÓ#‡þx@Ê7Q‡¨¤CÞ2-­TK/ƱRëìÅ©™¶£#»ÝÍéá©æ–Ílnr¤mgˆ¿OÇâ~y„GK°e/W€ "C¦TÁM™ŠþvïšÝÞI‘plà0ÿöÔÐdSëfº†¿ÍæRC}3køtº·Õë%’¦ø­fŠ[ñÜ%7 UvZaTÂ÷uTL2ÞhöÈ¡7ßꈄ÷u96Vê*c¦Î¡êÚ¶ö†¦C±Q}jcmÝê¦mÆh}CªnMAgN^ÄW^›_VÖSÞà:«×Õ¯7: ¥±£ðg”—P¥Â‡¼Tüjÿu=Óé¯ígéþþ…S±`¯ã=48…Qz~f[”z·óGú·XLÊFÊ…¯˜x$Бbèã­^¿è‘Âìòå.F”Ž*«&]á–ØâæMUŸw »íkÝÉPy‚óƯí,ÈáK#»h–fÃ÷º9Nä.î1Æn‚»J-%F0#ÉŒ*…¬Î%sÂjî9>Z5^s<Õ×>êr”ø~zÆåNžIç粋üõïð‚ºùp÷LÆ?ØLM r‘c°h-çòjéµÉÞãë"…Z§½aÂïðâE–MºôÐÆÓ[´Ù9û{B^G°ÌH–´¨ƒ½zlÕ¢ÐÇA ï·Õè=åöÔ?7Å-œÙèMŒãnÿèÀÆWwåß]X©óКŽ#=Nâ¿K¯²lR€#1)3ì,“:*­½Ê'¥ }9¹?:bîm ¶ÿähÈîiO´[{üêàðä:gSj¤åé©`ÈÓ»×ÝXç¢C®‰ 6¯ÓQXœÑǃ¿OPóò¥< 3ï¢<‰CÒ„âãÙI}ù‹oŽÖl¶XW®>©;§o…²û_ËV·e›ªGºù˜¤‹ða½”#áˆéÑYJs¯…©,¨J:Û¢Ï VMV÷¿p Ö¤k §òWÖØí¥Þ‰ÀŒË7Ø;rz+òfìØs¦ŽÒôn]°î–¦í`G2ã\Áã¦8s³G©ÂüŤ…âwÉžv†ZTj.Åé[Mã¶HêDŠžv{["Nÿ_ªì«h ¸ù_ƒ…á0Jè*v5EÆe«ŠŠââK)ð‡t*Í‹ %U"Þ ó|–l‹,êLõqY¹fUñ‰ÔðíÃüWͼSËnò͹Ãýàh»K˜§¼í¶/i>SfRóë·Ý¥ÙÚ­Î]Ÿ“gpÚ×¹™oá"—«[­\ò]Ñ Í«ÄºœqXGõ]• ÁCeØËœ>R1\­ÕˆT U7¥Ò}YÖÉZ_$«fÊh„N’"GTÖ¦žãÏÑy¤l×['‹¶L{µT5¿=¨4Ý)jž¨‘F±M»¤8H¬ nþm:Ÿô,1Js ÛJ5ÖŠâå3V¬æGVŽ¿ŽN·?ÿ68û§ø§Û9>ö³„ô×Þ×l–þ¤Ü¼'¸PcÔ™£‚}Þ¥*cVч5fÕñ>åÐæpS×v ]Ù6ÊU™ü ß§ìâÄ®ÄàäÙtÞUöì~^Ýr¸o÷R °¹`³s‘YšA*EqºOÙž(È]]ªln¢ó›¶¬]1È¿…o²àç‹°g=⾸6,- ™•AL³¯T^}Ì¿pÔ8†ëúƒ«JZ×9Üþ@­eçFûX™¶|ØTë5”~Y´ÔtT—·®ÒšæRc‘ßÒ9š—Ӧ˱˜òKõ¢6á=Æžk¡4;=î0Å”çT˜è*9åh¼¸£òˆÎ–Èž´ö®}^û¼U[Tj±Uæî*îþ"”}â‡ÝŸµ +òÁçD,¦óRì«àÃìõŠ—t›Ò²•këÝ)NWRf«nLmçêš"¶º’ÿ¦ËZ^Òà¢=âÜÞL8€Ì“DÈëóú°…¨Ó2Š]¸ÃìðNOµ¹vT¥+·î±…÷„_(Zkl¤øÄðÍmcWÞóG‡VKñ0×ðúù¤.ðoò˜â¶¦+5­¿—Ú×çò7ù[+xŧȷǀL‰â²PFH– ½ï¢÷6¹ðÈñï½O¤# Uf€[~Øô,é%)¢g…[ضÎÓ_âþÏ$M4Ø"Òw÷1Îíemx¾^Å'!òaÏÜÀϠȪ€f`°ë'^zŽl`Â<ƒ–ÌOr˜†d³:„ƒ&Ö‡÷¤’¾D²nà1@C_Ðê€zŠXä~Fùä3T§Ï„q½ xH“À0<<-÷‹xRÆv|; `ßÇßW¼IâY6Gû}ÍrÛ³ì^¼È÷Ñÿó\|6´ì;ñ~û²÷Ëžµ@›r`°K:u’5€V²NáhÛ€€ðÉ÷qYëY÷&ÀtÑûެO¤<‡ÆxžGs ©ˆ;$Hïðlý÷Ø'Ð÷IhœTáÛŠÛ˜vÄÍ.|D/¢Èv:H(~CŸÀ;OèVb£Ä h#N@£x‡Ø'uàñ"4È# x๠€<x΃Çž‹à ‚# À à ‚§K´6º${DÛÚÁÛ.|À6 7XŠXYŒÔ³ß¡µjàÜ_#Vj&ú†ð76ŒgJbUt ½ |‰þ˜ÜnB[Û9eï¤O1K, \afÒÎÌÂ?Y%bTAÊèy´¸¦_O«üûÕZUÇ¿çÞ·™?!Môåv§‚¦›¸¡3 +{[s¶e5«•xŸïííâÞîÛ}×ÌІVâ?FhA*¤A HfA³ š…3fY´j6í‡:#æNŸï¹ç½º”"ú§íð}ßïýžs¾¿Ï÷œÉ~#‚îz7ò¹A\ °ÃÎ'(Ì|#ÌûWÞÓ†¹7(O¬CÏE¼z©ÌèAŒNÊS¢y:&1#T.Ðb± yx™f˜{i¾™õ·Òb…Uîå—€ÓºF¾ +c‚ºß¡® Bcᯇ×ó%j @öˆ×©1ÙÞzôƒõz¾BÜD â05ÐyªfÈ~kߌƒ¨)ÄPüN…"©loožñ±0·ø;½[å±Pbû"gÿÍüÃòbQŽs:@·‰óˆ ò Úå:§š¢ û‘1€þÐ')A¼yØË±•FXþL$_<~ x ` °PC1àvÌ/Wµ‘É/òärXfj:“®¥®y‚ë€sãzÒ5¤ê'·ft= Fï2pn¡» ð™@¯$¡óM¥‡m* ê3+3·¡_Õ ®?ާН9¢uÍèE>Ps™zËÔÛ¡ê+S[ÈÇNÕd`£H`ßYÿa“Ö“µaÚŸº2û3²!7„¬†Œ},° tG®Ÿ×œÇ>“\X¯tgbq•Ÿ9g-OÕc8ÈO®-¼^Ù˜9{Ÿ. +Þ'À ǵïÈÉà·àª ·‡ •g¶‡{ÁqR}¡M÷…H‹ìy߯Ï;r0Tairö| üàYн×͇žã¸æÎ_/O™9ŽãUúO:Q§ó²ú8œ3ø«|:ìc°öÀ«°qPa¶…u3ž¦u2æïZØÈýÙtÈ–oZòCø0Ö8Ÿ©]òŒê˟뼿£úyÐÓ9îuTjNA_à~ÌùÙL æqÈ9ú5ª2§?H¡_~À9“ÝÆ è/À½$aûag”¾›[äâî»»hžèG Éï=À÷ëx0ž./³¿æZœ+ŽÛ%ìE6Á† t·Y>FÿÝ»¨pšLs4öæã®M“°®Ø˜!û•¾íò ¸L…6¾‡»c7xx/–Ó|Q„÷Ò(œ³>ôµåò‚8Kebæ'¢€‰²W^€½¬¿ôÐ]r¿ñèN@>zJ?EÕûx$ò|> èF§€×îÜ4Ù¬Ä;tp YÆE²Ìûð.`_ù\Ö6²}Ê6¶£(Ð#Šx*c~;ã]4hœ3éŽáñ£Beôä¿8¶ªñuêÑ­FŸ…q§x c£x_taôe†1ÉXdxÆ.£Ë4Ëͨù Æóù[È +Õ„Ú1^ +u†úó +ô¸wx áñÿ$ˆŒsTE!z”FðWöO€Çßch{–ߦøêb ‰ø +èè͚ν[Ó#(Boc¥Ä×7$5-p~­iƒn6nÑ´‰;´HÓ!ÐušÎ½AÓ#h£q0â¦ÖxN¢Ù·Êæ”ε*ìßmµjm;aÕø±ÙV­sšœ¶ï€í6Y~³“¶šœ–¸åÅÛV9^> +stream +H‰¤–y|MgÇ¿çœÜlH‚†äÜ›ÅZ4B›„H©–Ø%il£T¬£†Hk©ª%¨}ß×­ÎhG ­Ú÷ªµ’К±«%3CFNŸ{¥–™és>÷9ç]žsîù½ïûû¾P™0ܪmÍè ýOÕ–4RÒú§¦/u~<`#À§]Ú°!V…+L|³´­e¯ôÞýýjžªøåŽS½û½Û+nZß· ÏWrŸž©=¼ËWÜ ÔñÒçÅôÑŸÇP­7ÔzDŸþC†½4z Ö»Å’û HK]|{VG ~“>¯jÿÔáéæ}ïs@ÒÍ·ÞIíßÓwRÄ6­ßÌjé±g!háîOÔ3}Ql•ÃZOÔ÷Û C1 ^ð‘m¢o>®dFúˆ8 SK ·’auÑœÊHLx=ñ€ýPŽÛ5id9 Xìîc|îþ7Ó³À=~@i?-I(âçÉBQÏ“ƒš-ž’àî4à‹˜‚tU8SçhæcFÂÙ0±Š0}'\G"¥Z½ñ>Fc |°;ñ5¾Á.|‹Ý؃½Ø‡ý8€ƒ8„Ã8‚£8†ãø'ð=NâNã ÎbªÎ?âC®Ä9]瑃\äá.âGü„KøþŽË¸‚«¸†ë¸›¸…Ÿqwp÷°S±ãUdII`GŽæA –2œÂ<"a<'‰ìÉ,nâ1±8Ù¹çâ, /(o½»œ{%Ï™w9¯oî‘<ÿ¼Æ¹GsæÊÝœ»W{ä>Ðó™ [r/x2™ëÈÉrnçÜ̹–“›—cåXçNŸòïèßÅQÉ,e”yŠ&[5Nh(sÕ‹×ÕikdlÑëVÙ.;d—–“rÖš³n‘Ó=%=‹ûž;OÞZg+BgªºQCË1ž–:FìóàôkFQ­–]Tjü\ž%üÏže×Óú•)êh7þ žÏÂD)¡ƒòP=Û•×”hŒ&ŽI˜¬îm©«´P}ßmÐ~êýQêåý2E+›Ö¨ÿv¨¯×ù:žÕÓêm’`LPƬw;\é5/*6*ÅŽ(Á”a1J±MÊ17Ų•cnŠ%)Ç6+ÉN2G\Ê”J²Ùn–q.ëJ$ÿ!^RN¢˜ÊÌàTþ•G¥®4àuðÿ;ŠCÊK%©-õùÓy“¹“Ç••%FbÙ™}øµ:®1›ñà¿Ä[*H¦q ÔQ¹Á]üž]ø{Æò=ugWöå-2œ‘Lä«|Ÿ3ø&ßæÏ´ņŒã}ñ‘ŠR•“¸ÐMFŽåLvc?ÞÖímgq7O±{ñŽo°7 ›ó0sÕù¾*Õ¤±4åd.bæ^žá>ž•~ÈyÊ…†Ì“ xO i"IœÈùüˆ Øý™ÂwÊ0†°œRô¯ò]®*"i¥êH.ç0nå_xY¤–“—8‚?²_c)‡R(ùb+shÊÎ@ÃK׳·2ÔWd”0 åiqùL6ÉÙ,3eŽÌ’l™-sežÌ—Oe‹úf¡ÒësY+–e²R–ËŸd…¬’Õê­­ò¥ºj,– ²D>‘¥²QÉznàgü€s¤¬DðnÁ6÷Ž‚¯Üû‰îe<{K0Ëê>6]w°aʼ‰Þh‹vèˆNxC÷.èŠ4ô@Oô¢FÉO(¡ßî`îX°oiÜv‡mß“;òÀN·ûü†×þŸ£¾OKôB7O­­F‡gúÓŠ®-ÕkÉhñÜç´Ñþvÿ:’‡hÝ»õ7¤½§m0†3Ôž£~‹²³˜ái{Y#õŸºó•êH¸¿(wëÚXf(ìd»+”Ë2 F˜á/¥¹\À¹ø¤”îÝÞìÚ¥s§öíÚ¶iܪå믵xµy³W’š&6iœ÷òKÆ6¨_/¦nÍ/T¯î + )PÜßÏ×ÇÛáeBTož”beG¥d›QáÍš½à®‡§jCêS )Ù–6%=›“m¥xÒ¬g3ã5³×dÆ?ÊŒœÉ_ø®¶Ø6Ž+:³$wùæ.¹Ôô¨¥†)”"‹Ñ”dqCqeɲb½l/» eÑ¢Ó‡ÝÖqáÔN•¸v :©Ó¢ à¯ÉO›ö²§n§uÑÂ…ûÕþ5@>‚¢V€|Ä@ÑJêå£’à–Ø%gî¹÷Ü{î gxiôõJ +‘´G9"ÝÅ‹³*´ßÈ‘¼¤­éíi½m ët˜!)m圤ႤhãçÊ¥{U›uŒŒ•¬}½¨jµAÓ-­›œ©âîQ¬7˜ne¨Ê ³ƒºÕ !¥¸¬Í̪JÎäuÓmiì˜Æé¶¤S3º&U{ïW^¿Ë£¥BÔ¾L–‹ÇTÍP„IƒR©|O¢ZÉi=/Ú!—´^’S´(cSsMX3…x"U¾@ž¬=Þ.)Ö%lˆÿÑ& ±™&Ð7Ú°Bˆ/ X®Ý•Ñt´ÕYµÖ—Ð’ï—Hîæ5¦@5÷ïaªYmhšÓ $@©R +õç\¹M[]’úz!ûú‚ô’f–N”éo±T!¹\-o ª&ç !ë±*Õ=ý0¾X€ NÑ4̪Z?9£‰$[‰rpj^Õ§Ô§i☷³ú,­_ÉQ\’R)äj©-2«~€â›ŸT’ïv%PžâÐZÆ€”°RQ—Ojß2ÔçIIõ49é˵”§,^ëùÜtú,ˆmÇèÆ`92K*ã3ä)[ Æá‹dG@Á]z—2š‘TìCaà¥>‚¶¶ÙŽ!46AU:ulÂÈjŸÿÉWÇd +iæ-¶x41ÕüüOhµÑP¤”r[n3jª¬[{:N†æ¢îf˜) •!+d ˜ÑE”Å6IC3’JJ$O †ä•ÆFs­ó;5O¦fUíz•,lëÕô{›ºzKcƠǣ¾§z¿Þov'v¨'j©b&Sój™Ô "©2©!(Yç^w¢¾~Ça{#ãE‡øñJñîæêR¥*Ë•3J¡r˜¬½ïøà §/¿Š'ÀqXt®¾µÜKw´[¼Än3Kþ“ ɯ޸ Fuæ2›Ÿãuˆm’d¾](G£hWI2ší+ ä>,kîtÿZœY$”Í8Í(€Ò!¥Rú×á°7«üçü¼Íâ0y'œ»á™E×ï6‹">-ræMVÓ‰³o¸$£Ý¸ö\æ¼Al÷ðŸÐ6Áòyqé%`Öyê„»Q'òË.·µ£ÔÎÓ‹ßU#"žî?þ`=ö@g#¨% ×¹ˆë)"ðF~EÜø£hâpŸÕåJˆÓ"<“ÿ_#v‡ÿÂi7±úÇ7Þƒìä ªLà;ŠÂ²Øâ)7Ã8F¤Ôa´8V [3t\§ÌÔÞ‘$:Àä¬å‡¸FõzóA‡Åeö˜88{ðHrÜ.ºáF5.Šÿr¤z¿òò÷]¼Ëä°צ<·_æ°Ø‹[EÈП¬ßX^:Oë>wAYáj-l9ðaƒ¶vÙ#˜PÙ¦/×ÄØ\ \s1´¶´Ph<•ë¦CŒe”ýÙàóAö‡_ùñ•›Åe´ØöŠÆîc½r!¾:6‘UZÅOçÞýù‡mÀ¦ßÃøKW|þä±áòEZc j…,z‘ëŽ×.˜J6£^äz…'RxKõpØ­~Æ[°q㨔[ó¿Í6—ƒ™aZ‘~ýn ü# @Üíw+"¨uÈÎN7ê)Yd7µ•‘³öF!ÖI¡%þïÂçôè"z""º-x¶ì¸ëk—”l*ù­¥âù}ƒSÙ˧ǟOOï+MŸ™ÏɃC®å#û–ü»§“GÊ'çR‹¾®™¡ÅS‡öííIJíé£ÃÏá¡Ôlzt(Ñ3ÜF9ŠV¾7EQ—,Pywg§-‡Mb{ÙIw©x½ˆt~pl0•¬cñlYs´žRt3§q4j¨#<-~4¼?×6+ÛÍ.“7ÚÛZ8ÁÕg‹_ßw*{þ;oá9Ñí||(;>¹ñ“NÖîð{8§ùÜ…6ßÙÒø‹éwßùõCÊÚüÛÕÛÔy…ïíû¾×¾ßvühÇIHÀÄ<¯CHâÆ£„Æå +Pl1¡ÊP‚6Ú¬Œú˜ÖNª4F@ñØè+ÆZ•J¨•ºjZ7ö ZÅ´BÕQuTjUâìü÷ÚÁ Hv®Éçñs¾óíèî䛣ôº4ÈÈ”zÃZšS•‰™ ÷1þ1®Äð¢ 哟ӿ¼8 +SÝT<j^ØÛÞ!+ŽÅÆ-H¥‘²Ö’5fÕxýº¦6t³¤¾ÞðµG£sj'ƶ>ѱ­¤©«:Ù»|QC_T¹šÚ)altí­ÕÝNÃï–| dÌæR×QÍÌ„ ì h@lAb‹1ãë¸Ël1Šú;º‹ªõÈPÜŠ:I™qb­u2ƒŠè/¨ ¨– ®¨ÀlÖb¯°`Èí Ûœ´(`ù«ìâšM³™sɵª›??¦©GyÃLæÆYùâžvìø §˜—‰ýÒÉ õÍ”}wÞ>ÙÏÖ̲àõú¬Ñ ¢aÊhí}GG ­Þ÷Eì{!þ+`_ûš.ØGùØXºxÔÌÉjÜÅg¯Âó¨ÂcG›.ÀãÞÛ0Ú0/ †éAà Âýá„N%WÐçÈż‡G2 ½dš„Ïq$Ñĉa9ñú  i‰Š0I]zê´ÆÞfw›[§ÖÙÅá?ˆA=Ù3@¶œJé‚ OR9EÐ_{Í☈cÄ¡SªŒŠ$tY&ø‘ßO…“ HR¼%0H$Ÿ5¶Æ,>Óêë +9&R1ŸôtÄcäžýØ7T;'±x_?=Üžøéž}­-Oczo^U¿½‰ô¶eßmnù/jXÐ8NMLÞ¥[è+ }ü Ùe¤xÖQH•S‚½}ò‹ç!LaR,Å`Ï +;uþxW—Ì»œŠÖ0wÿS;÷V-sÉD÷%å‹çÏ^ÆNE™eÐNyï³E÷©Ú(W§Åc=¹ºÈÀäê%@ ä!˜žz¤¢n~ÜJ²ýã«ÇÆ^Ý›Ï dH–¿:ý‹ +&}ÃHfÄ¢,a³«®äŒ0¦d0왆mÞznz¦?Áù¢0ž¤Pœè­Q²&‰˜¸ oÛºž| £]®äñÄøxâËÞq“Óšíb¸ÿãOóÖÇáýç+´*díàH’ +¡Pˆré¤RAªª R9G3ËUX­¸%ªl%IJöüØés'Ö/—ç}tÑðÐ÷4Ç ß?aAé‘}î—o”ÚÁ7<â}f¿ô®€HoªnvVá=Â¥ Ý—t çZØ y…f!wÓœž¡ívÑiÏÄg˜0Jbò}ÍRFrµ¥°ƒJÁ6yèÕV zXôÌöLfûŽÌÎí[_ZºüÅÍ?9»üÅÎçGFFGG~8úøÇvŽ­~ëÔàñÇs¬Uy¨T™}-yTAŠÂ¨¼ïþìÎ$0r0ÖÑ ÿlÙ½{Ë+ø¸™>BrÙõdß®¯0Zß‘8˜ýÚÊc“•‡Ÿ*§ŠriJràHiNˆ»Zù0ÐÎ…iÀ†°ç¿0Sú¢T>zà¥VV{2óŸ\è²oçÒý_²Íù‡“Vv£¿ †Š$1׿ó©çYz©5ñQ*”p…’²ÏÇyƒäò% _‘0gØi“®!„gØYëêÖ–ÝKŸàyn¡æf~>‚%:æ † [|Ù¯ežá ÷ùìFý ³Í¼G8± jË&qØœZmÈ…©hUª8ä\)G!jšÞ8ãpÊ]NShÍØjжLA|í¦(È^Öé‰v¯Œ-ì{,Õ²­cÕcÀŒèJšíݵíkWv?§Aè²58Ii‹—Ïó–—¤;šÒ±G€9"°ì<ñy•±¢²âÕ›IìnÀ0p±¨Ž¤`SÙ„}bj‘GÀ‚½s 8ð+Lô·l¹ÃL,­­)R/aš£•eõ·iï€üèLã7õbH¯Z•†Ç¨ö£ 1 ›U=k×ö ³ðiÝ÷ƒm ¢ÛàNEÑè\˜fcŒÄ •ÌKº<=Øç +«2û «¶«{å ++©­*|•UQ£‡4äÏÄût=2V›8¤‰àÇbÿ0ø©¡Jn¯R©T +5|”Ð:x4ß±©1{‡´Ôƒ^gº·zŒ|øRU˜75¡KÎ#ÃNEmRçðQ§¤·¨ðEmÕç¾§A¦ÀtFµ‰hŸ*Š*0Iµ&*ŒN”TÚ9qO•Ú„Tº,D¼ IqøB€× .yÊ'§æÃ`J"ÿMr'E‘9u†ÅN·t–=z³#fÏ ‘‘Xœýö-‹¹fOþ½K‹0ïµT,á/U¹×?»&Í›,¨\Êëçªf¥#@lvŸ“ Í®æöZnù[CO˜xNáÃ,Ú4ZÁ’ëÔ:¡Ûßí^ÝŸ^Ø5¯¹s‘•y¹dclã\m8˜h¯N†êwÎVV.~´W[ÔPƒ9=¨ÈŒK[¾2{á’²%ÿV¥wV‰;5ù%]Jß„&,…jªÓër^JyªW%û5ó·™ ô[Á±Öj±[4}Î;ÔÑÙmšëÖur\q3^ÒÛ—ê¼¼qÝÜì­Ú‰0 Äï a¿1ïÙû2Tèÿ¬WylW~oÆsìœ;³³Þ{½k{×öÚõµ¶×n|¬øŠœÔÛ-i›¤m¸ +iÔ¦9íÔ-i¢ÖVA*üQ…"Q! DUJ$õ‡B¨DKU(BÐF +Ј¶Qwù½7³öû–¿};³3ïýÞïú¾×Z¼¿ËðЛˆ–5ó"/DùuGm¥e7HÙ5%[säÔ“Gº:d—ÊÉ ©™É»S­r…**mò3§Ÿõ@i‡,VÉͪ÷ç4Ö">ñ€Lx 6€<’†¢ñ¼$ +‘PÞ_f‡S¢¥Ô¹NrI;<#Iù¡ùt×þlnöo5¹dVŒÏ¨ýÙªäYyèÈ®{N v€øF,Q‰EãÓÍ8äó^ù±mËßñŸ!‹b¨³jü^]GɰЭº›]Ï ~?™ßŠDâq ß šÙbrñP(N€˜â;Å$×´6ëžrûñüøû¼(¯;`vöÑ[åR}#“yÊÖM'Â~ί‡`@Vw xK`U-äa½~fªäÚàÛ²fŠíO\VÙJ>ÌjIƒMÚýO%Ÿ)µtÁg+ùº:¯ãÒ뢮duñwï‹ú¨OyÓqë®×ENÅ7þ&ŠŠö'zŠ™,ÞAEXѼ* ·š÷‘µÚ×zw9i“­Ñ|µ³ÖðTò*' .9Àõõ0s°Ÿ—}^F’b’ÌòÎnØNÈê6”ÈZmRBª”ª"yÍ-47å%E Á+%tùá ÉáMùRim‘>M’Ìðó÷ÏÆØ> V%&&1§F-pQŽ í§ù4ÒÞz—àÉ©Ž|o*œ±ÖóXBQ’ÇQÉ#…,ÜdÜV‰L-™û\Y.ÓÕc›sz!cÁz2¨.÷«nè‚J© nè€ÐyA–“¾[}øôéÃËËËúÊâÂÊÊÂâÊôÕ+W®ÚL×L'¡J›éª|y!Gôy¤•ª‘2ޝt–ñ9k@ÞÐ@‰ÉV,–›švÛ¿wÐzßàu‹ù–eņ—8¿í¦…=ñÙûi§þ @ žá#1è B8˜gy™®_Þ¸ÊZ•Ý«J…»N3¯XñÝ)B5ŸÉÎ W°Df®Ú,sݺ®¦™9>8†œ›KÖXÆ® /â"t$8ÉA?‚ò”úц0ö3¤‹—šxY…Pk:¢(†é2¤é'ŒÝ‘j7fÜ]&ÓZx‘TNøì<ì¹…hþ”_1Q¬ö¾ _•û˜2ju4ÿzÓð­+þMÌ¿®ö33¬¸ÁkKW5ä¾üОÁ~ERwgf>Ö²k[Ççõ0´é°É{“µþ¨k808í«PÕɹƒQ3êh›;µâGø sX7’ÕlÎÔ%™Gòõ¥BO¯R‚ó­ÜC#²NjŽqªÿÕq`¸ •ñ¤¬%ËZŠ×{Â:'ò¢R'¬à¤Ms'-ë8¡·?$¢ë’BªZ¡øŒ‚×nã;v¥ù%̧«ùHÉa›ÛNÆØ¨FÞ#–jŸ ­½‘ÄkVŸžÀ³Ðà~Þ•nÅGhåší°ž +‹þÖ “ŽªkU£i&/9)Ñsªe¶¤ä7)ežJø¿jªp‘áômº"œ;'(zFç˜ +ª&zð^áTÑS8ŽÏxD•SøÂˆG$»„­2d—MÐù rŠ Ç©F +ñ&EϧZN¶øÝÿíYâ5]åÏóz)yÓôf]ã:‡ˆ.Wõ6¸~ðü ø'&§ÉFá >nÈ0>‰ Yą̃!+>TxÞ QqÃǯÁ^¢Ì4wOT( R\òò +_N<`a&SW²Ó·‘l ëˆh«‚"xñ¼¢÷*÷¥G8]éÒ•¯]PD¡n{nœèþã ·¨âK/‰‚"ºWÿbœ ˆ—.‰X$ƒ"úX” Z1Â{ù€.•sqÚΔr&Îd¶>> (ÀÅ–‘h4÷ »5áíÐ8p²¦=‰–4~ÛÉ(œÄOÐþO^ßÞ ¶Ô‚w.Ó‚žb 9•ç½9’@`‡ÓSˆ,ÄäÀÕ‘ŽÛBÇi½Ôt[œ‚ÍÂh#s'ÞÇ•²Ë²ªUMÇ;¡B¬¸ª«…‹.©Ž2Q¾Œ~ +§:É—ÐiNsÁÕÖ6O5ñ<Í +ânpñ„»"ÆšzuDmc% _V+*¯™†e&žwÑ $]}µèòÍé½·‘̾wЯšFÎ’ñ·Ç¾þlq¼p“SØëpé"Ï"ú»ZŒ!ÄuÇ‹ +§ÐyÊþX  +•<ÊÛ@¢mª ð÷ ÊÂ¥ã+pÿº?Žî¦ï}3½W6î„y>|Œž¤s"ô Ý^î%À@0èpÐH3{Q3þò1T˸à™‚ï)fµ32R™qÔÊlC ø›¨°04¦]€^gL;¿S wÁšwQäy->Î~Õs¨ÆÝ†œq´ìš|or®§¶¸Oîu•½G®{Ëžk*»×Åj¨ü3Ï<…,LJsÎÄ1À ÈáPeà{óÜVØá tÝé`žÄÿÕâ£à÷I4Î<@ׯÄ´—âò8ïõ1ï ~ž?ñ+åD7êÄððÁšXxb°€¼ø³à÷àçð¾±×Q{µ’\üÎö5ÒyÈœc¨ d–пٯúئ®+~î{Ž“›$ÍXWú^ËÊÇ T Êh`+$N YBpI€9ñKâ5‰Í³SV ¬€²V¬t­¬}˜D“ª}hqÙÆŠTÄZµ]·ÚU Š„ETcý£´Õ$¶õ~çúÙ„­Òº?¦ÜürÏ=÷¾sÏýs?âUL𵑪•¨+€|àWhƒýRjGÞߪ„ÒzµõP»Ñßn×üýZ¢,%ò +µ(Ñtu„nWEÈãä¬_ ¥…ÄQäm30ʱŠ×åJ^:)äV*uF)ʙԠn¥ÖË~|Çß`|ƒHÒl± +•e´½@Åý$Þ#¯¸Jyâ4öJŽm׃oˆò•«èGŸZKñúžÂžEξ8±®RºUÖ 4¸ ø2°8Ï|nÀæyØ·;J±n‹×*`1CHíÌ­ø{l3üþ¹ú2ÖÐÞ:Áëeا2f¶^ ¦Þ“ܾçŽN…2ž•vŒšÈuÜ)êÀ#â@”~ ýÔYÞ6ë€& ýGe>er‚sÉÎöãº|áü«y6ƒs‡ó#c޽ì/âZ‘íã|BÆ̇bŒË‘~žÎà›×·CÉùØ·ôœíŸ®Ëcø!ó×Î]æ—ûåÎoÌ-ó8=sÆ?ö7“£ìÌM;/™{¹û +?³¹‰>ø× ˜°ó{àÍk¼¤>ÊÌ›õ)»öÔû{™¹&Ú…½`-ìm·ã³Žå‰\Ûç©s¼Ïå\“9Àxô¿Ÿåié=œzþ¼Ë¹çR©‹Sñeûó +ð2|¸`çs“äˆcZ›¶!×Íñ°ÏFŒØ‹o’²fÿØ®y^žÇcs¸:Í/îÐ5Ê8ö'æPÆšæà<ïé$•È;á˜/Bn·ïŽÉØ; T%ï‚סûè8Æ^€ü*yÔÙ¨;^š¥^‘r±R$ß9•ÊmØ[ÏR3Þ:^ÉLJ¸óÞÁ:6@W|q;€ú~€9jD½ßðòÎ{æé¯q?(mè›K•ê,jVç@îÀ·ÇQçb<ïOèñ®ºC¹v*i©8‚yêí9_ÃØû3Eiûê.ÔÍ8gìw¥ ý*Ý'm„8Îö‰¼T~þˆõ–‚‹ð3›Ï`í^pôCøy>º.ìAä#ƪó`kmê²R4ÁÇ¡½ëáuÞ†óÎÎ7i+dï Ž ÇÜ•:-yc™7ð…Xr bÂ:ɯË~H[;H“¶8ì˜Ês’ç‚Ïöý·çÜëâ½ûãœÏÄ[-Fÿ§Ð3ºáÇ×1—‚ût1|<üBúæáw¯ÐÐçÅž¼Š<é@}¼BÿR LÓ âPŽœ,oÂF%|ÿä¿@þrå¼ÜÛ ™÷»¼cû‡Ç3{WÙþÞÀÛÁ¤ñÔ}ÀB mp*÷mwÚGöOú¶TæEÏ#´Ôq–3µüoâþ»#üW•.÷PõÑ:‘-ÿNQú/J‹ŠªÔ({•ój¡TþO”·§.Ž”c3Ê󎓎+ÿ©’S³3眳~ç¨ó”ó×í®\ºŽ¸>ΕëÏý~î¸û^w§ûi÷«Ó¾ˆÒŒòÓiä•åmÊ;œ—Ê_ŸÿÝüÃjÁ× +¾Qp¤àR¡V¸±ðÇ…§< <1ÏÏ=ö.ó{MwüÛeîÍr³Ü,ÿß…nŒÚ@9ô0¹H!/þSÄý,ºœ'ÉÁ½”OÏÊš6iY¥;ÐJËÈ{mÙ ù—¶ì¢ZÜu* ‡­sâ ¶,pƒþÝ–¼X¾jË*ùp3§eä'mÙ ù¨-»h·ê© G†¬POoL_X^±H¯ ôÅÂúÊ@ GoŠïÖW†ƒ¡îPW ‚:Ü­ÇzCQ½;Ôgê–¹i0d™Q=b…–¾Ù +Åbæ€1­þP4Êû­pÿ [Û|m«Êjú[ÂËû;M«.ô†´™=ƒ}ëó˜0å|=0„+zÀbŸ{Bјi™A=f‚fÀz(ÊËšì%èS„†È¢õP/ÅH§…xjWÐ"Hulta@k%ZŒÒñ@ˆQî–º0¤u]è¡NC§£Ý Mè#’…¿›hm–¸/"çã¯N›¥C1¥j‹ú¥•hÖz7tah?ÛÇFj¥6ò«¨ŒjðM€¶ÈÑË!wJëuÒzvàbÆš°1(ÛÖ¤Öç·÷ß`ajÖæC@´ye½.ÇgâÖ#­Æä ¦ƒ€dJ/,zHF4ýÏŠš>#Rû¨…¦ø©N¹·>jhßÜbhVí<-2Ô|_Ò¾=¼@Þ½X«Þ]2cÅ®Am'”½=ÅZÔL ì +µtík m­?¨ÝùkoÑV·µV_‰ÖPÔêk mE]P[Žqu>C«­)Ñ|5ÅÚÐ#†vvT„GÅø¨ˆŒŠe#"126¢TœQ¼ =¡è‰êÄÛ‰Žß&D8¾=®xã"‹ÇÕê¸ðÄE$¾?žŒ«cûDõ>¡="ž1þÇÇüß2¶û/î;Œmþ‹ÛDb›XµMl|Nùþ ñ…þ> +stream +H‰Ô–w|UÅÏ93$!¡‚@˜÷€€ $Q()MP0† î:Ò›4C‘f!Š`Ã.Š€t&"¢HŠHqaƒ(›<ï%«»ûÇîß;ŸwËïÞ;oÎ{Ï÷ ,&ÀArÇ”†ñÓ ¿b-ßYz,-#5ó•rq›¥{¤ Ïòº"Z¦~™Od”Ä‹,ãFõ‹)·Å‹-®ß?=µoÄø/–Z¼ÉâÆý­!¼O©‹ ,®Ó?#kdt·ñèj–6 ”–Z³}ÍÇ€*_¥ò3RGf*€U@56Þ˜š‘¾º_óS[¿ósæ ¡YEH¶®ÝÁþÌ!é™Åz¼sA=py˜·ÚÌ  ši-ý‹KNC2ëFHQn˜äÚ/º” ¯WPuðþ¤–Zb3(Ô¡@Lpö²šM}y°¿(¤e‚ï =°²=ÅjŠE˜"C£PÒóçE­PMøïWñJ£žF†Í0ÛÖèY,Â<Œ@æÃÅbÔD,j™ü¨:¨‹8”B8žÂhŒC^Æ|ŠØd3ù [°Û°;°»°{ð9öböãâ —8Œ¯°K° Ã1…sp_ã(¾Á·8f;â8r‘‡|œÀIœÂi|3øgqçq?âo¸ˆKx3ñ<&Ú$.«à_ù x•£¹ˆ2ÀͪÅŽåb~d/ï).áz‘[8Kù‰Ä­œÌeüTfcb¦âÌ`}\Eþξ¼ˆ1ô3E…ñ¶s31×ð~ÕvpW´†qlŠHDa. +mL"‘ƒ—Pø ßáû¬ÍzX€…Œ`i–bÆÚâ”â5tÄ`¼ˆå¬ÉXÌÁ +†3¸‚+ù$¯0›øæ<â6ÃùÂÕ\À.çËÜÇŸTN7²û3‰;á†'Ú*>o +ÃPÃj¥m kbÇÛ}‡Pu5±ý¸SŸë¸ã:N§¢ãÔw’œaÎ8g¦3ÛyÉÙï|íöqÓÜaî¼Ø©±/Æ^ñ¢¼ +^Œç÷â¼Û½/Ñkî%y½Lo®·ÚûØWÊWÉwƒÏï‹ó5ðuòõöËæ/ï¯èñßèõßâoãÌŸ^wO½sQÑ×Ý¢@ 0í¶ˆ:9¦îKþ¦ª¦n½vh·Ž:pœȺ8S—eꦘº¹Î +S÷qwˆ›;ÁÔ]ö`ê*zUCê⽦¨Ë2uëCꪔ¨Kö¥”¨‹6uÕþP××Ô-/({%êø5P8XVl¢¼7LÑmEXï‚@ï@Š•Bm×þì?9 ÈÎ_¬çË÷åÏOÏ;•ß<ï µl·t)ï’åûO<“w$8*÷jîõÜóVžÉ=•›Ÿ{871·znõcŠêÕ+¬†sQ?ý‹ÃÒ(d+XΩje ÇïÔ±•Œw!+·(æGVÎË7»ãÜ î4wàfûܹîÂÿ»ÄÒžPmWIË6wGIíÈÿ Èÿ7?*˜{ƒîÏ2ÏÂTs}y;ǽW¹ÏØ0 +÷á~ö5÷Ï0§6CK\1wEgtC¤ùüIóí;! +ùCÙF¢•æáy%n`\šô±Ñ “™`>ºù*³èc}£ÕhãÕfói-£IŠñjlˆXKŒYAbÝaÌZjÔú„[%ãGS£Öä ·8•u%GùU;òQþ…cø +×(Z1ÜÏã<À\6äü†gÍiá*¯ÊLfodWq-¯+BU…ÝØ†¯rogc¾ËÏø-Ïñ*ÍNì£ +FX¯ócvg[ÞÌ Žc>È/˜Ïª¼‘ñ¼‹6³ñ«óÞÆc<ÏBEr$ŸR™œÈžlož?ÉÁœÄ7¹­ØÅuŠ­Ù•_ñ4± ?à6~Ç ,R”jÊã(Îdg¦ò-näÛÜÄkÆiª¤ø5Ïð&á÷Š•Ã9#8ƒ½Øð!Vbe–e9#æNîe:ç–P3ÉÚŸ³ø8_`÷ð’ÊðGUe?ngs&ò^ÑQ}£ƒúVÇôr•§ zA/j¹rô’^Ö +½¢•Z¥Wõš^×zS«õ–ÞÖ;zWïé}} •¯:©S:­ïõ£>ÒZ­3Ê}¬OŒÄŸj£6i³>ÓmÕ6m7úíÔ.#àcô^íÓ~ÓyÑ:«¹š©š­L Õ`ÍÒei˜†kŽžÑR”_µUGu§zª¯›t³–èݪÛÔ@ u»)^ ºCwª±‘ö.5U¢îV35W Ý£{ÕR÷é~%éµRkµQ[-Óƒj§öê ‡ÔQÉê¤ÎJQuU7uW=¬žê¥Gô¨z«*[S4_Oi‚ÆižÆk¢&i²èY=§©­é£«§5JÓ¸Ïq(§Ø×Ëgù<ÖÏG¬ žŽv +F…NÊH–±SyŽÇIx­ÐmТڣÂOh„x$à܉Æh‚»Ð‰¸ÍÐ-p†bü‚GñRÐÝÑ£'zᤡ/ÒÑÏÎñ‘AxÉþÜ¢‹†±p !Ú]ÏR<³ã‹ëbaÅxÍ,þÇGÖÔ´ÊÝú”oV€ÒÎÙ`|è|RLqÙöl`pÑE7ÖÙ:‚õÏ,g_À3ˆ¶ $¹¿³_î±U—gÞßi(*Ö *Š0ãªrZJ¡å¶!ÇRí±ŒU…Ĺéi{BÛÓþÎ"nºà¨ +Y–]`Q36·,n^Â̼$&^þØœËtsk;q:–ÀD7!:¶xöyÞßû;ü躙¸d,í/ß>Ïû¼·çö>ï{>1ü±æ5ÇTþ^Ä„Sf=årH®„N±4OûaYd.’E‰òyâú”išuN€àÚ5ET/w= K‚° ,3À +°Ìó@½9! f­\džiæ!ðšøæÇr™yù#ržyEªÍÖZ%3A3X –•¥@=Xæ‚9 &‚üDfƒ ÿÍ\ÙXº'áIME³Ô@›;º$ÖV>éÚM#ÈUV›§í…±qɘl.¾I5`9˜ ¦8¤Á'Áõ`1¨ù7Xéüµë®2\Òf;>¹R.§} 1 ½€«k­ÂÜÍ~wÛ=uÞµÞÖG¾Y˜›ðm쯚9×b:úLÇ׋ä0‘GétŽX-h0; ;¤!Q#µšw¦q„uúˆuŸŒÿëì„ÓÇòc,.æÁ}‡£Õ¼')ot<&­ÞBÚ×qX߯]Øwùnä‚Uà6|~ÜÑ¿Ó÷;üû’Œ÷VÊ*Ö«òÞ”óÍŸäL³{ÿ€ož’:ë¿Û¡o€ÇAÝ÷BÿL)¢¸ •þjvÁÓö^g,Ôöë4ßã3?+ó½j|pD&xcåªÞ„Äi´á×åÌÄzb0OZÔ~ïI©ó`×aJï³ø?iL²ô¶úÉ[&“-rÄŠ8˜çeªÍ+Ì9`}|þ˜Aþ\¬>å¹ü xÞ¼+J^ûÁÀ­ }ô?¨¹ÅׯÃÅÈÆ§š\s1Ñ\Òü!gª4l¬£>ò©œCªw,glnø)¡Ðز7•³t€ýSÀï;àsvÕiÀåçîXNºumjºüSj¿…ê¦{Þ^Þ÷Ú²={cy†6¿¢Ü"¶?„ê¸lC§ÝŠ“ö—JÑ>eÊ6–ŽGó£µYƒË¯´‰5z¡[ð›ãvž<Ä›óhmf§Ž§ÿýÈÃí ÏZéè°OaÛÃí|ÊêøvÙ¡qýŽ8Ü¡@v«E¼~Œc«‹Æá¤>(ëˆ~Þ8çŸ÷Êy5󔳎ÿc><ÓíËNÍÛ¯+à·´OÜO‘OÉŸÊS|®ÿÎðE}öì •þÛÿU°‡=ãò1m}¡ù®uïqç Œý¸ŸñßRjë¯êæI•ÝCm…ZßBG­Í߆¶PßUç'Ñç§2{¦•G­^\hkòu.æK‘ë¹=[ƒ2Ö{„:D-öš3ER‰©Ôµ$üR9ÛÛÆØ¿eZâÓÐc.WϹ~ŽÚ³ìáÎÁ/Ŭ-¼MÞGö}0€/?}¨}o†µÊÚ«÷œÖçuðZŸß ï©÷¶K‹×XÎñ–Cý2Cåæ‡Ô®—ÿ²\a +Ôç½nÏ .v…¹aï†ýrµ÷kð[¹Úü;€|»¬ÆŽTùÎ"÷UÔÔ‰Øzºnó±qí$õv+þ: ¿÷ÛÞ?äœ÷%ÆÝ‡>Çdù2Ž8´˜Á œmí|{\nil466ÍŸAòs󑯹ºúE;†˜Xj^iŽEw°Æ¹’·ƒž!=+Ó-έ'çî_}çïAü¥ñÖüŽâ}§,õúär6ÌEvù*{¢ÓV°¼Ã[â5î¸C¼ùî§ÿkøã wºÑؽ +>`Ì~ÞVí]\gzó j|Ö®sº—–ï&ø{¹ô­üoåî=…”+È‘ç ýÐÛ¬½³™ÿ(ë4AŸÁRe}ªõRõÝêguS=tOö‘CøzÔR±ïìÉóF¿ÑoôýF¿ÿÃψxG¥Y*ä©ÔVùÏ Óö8ÙY–÷Y¹½d­¯€ÿŠãÇÀÿÈñ•Ò(?c¤©8Ö>3ÖñFðV yÞh¼ŸB>! ¼ZÇWÀw:~ üw_)ýÞžÆ|ï-A®£³è×ÕÎið—eºŠù?ÉtøÍŶ?o˵çÖeŠ9Äùv¿Ø™+øí¹®¬dû6ä‚lÁï rùÀßäŠÅlß› ºs…‚oòÝÿ²bªµµuÅÊY©îÌæ|OS÷ÍÙ ±mucu³êðqûb{$ýLO›¦3~&P%;r…b6ȶùÅ Ó–íÎë jÇpµðožß·H 9éN)ŠÏ]^+sx}ø¼¾2Ò…,/=´Ò´2Œò‰vQÚ¤ÆÊòp9iëè-BÃÑyd>íN$8Ñ%Y¸€ÿ}²¶rÚ×k÷Ïóß—M–/òeí:½Ð@ºí*…òêíÈòH?ZÇoBýVð»`­nú7ÛÑMð7ÛÕ¹šÿÕÌŠü°šž´ìb|ð±Wù_ÏÙIø ’6ç/•ûÖ®(Ö¿E»nÖŽ+Âeà²vï@ÖÛH…Qý(Ÿ'ÂÃ^ú†¬’‘þþ)À.,h +endstream +endobj +985 0 obj +<< +/Length 18212 +/Filter /FlateDecode +/Length1 51515 +>> +stream +H‰|–yTW‡·«ëuÛ´È"¢TuCµŠF‰8Œ:ĸ“D%£câAP•(š(dĨ¸á†».í.*‚â‚F\ÙÔ»LLÇö¨1ÍØÐ=¯‘c&˜wνïÞû¶z_Õ»¯@š# "‡ëÒµåcÓyÄÊ%*&1:if™)@@˨˜YÉòÁâڀπMKš˜Ø}W«Ž€¿pË›˜0'®oHrЮˆO˜=Áöáˆ2 ½‰ûá“xÀ+Ìo<÷Çp?xRbrʈI'¹Ù> 8*aZL44Sò©w¹•’仼ÙaàxOÞIž;r†9”û£Íê¤i3“!ÈN?rµ'͈MÊ9úà!pFè»@S&DhÅMbßEÛ×µP‰8•—VT¹1µÊUÔuèì<‡”Ñ|–f\0|p?½!;ëÅjÇǦéEG{ƒœN' 6‰§\«Á‡k\ü8Î[äÇ…¡)Èk•ÊÕç÷…7 +j‘i´Ítnúæî-<<½¼[ú´òmÝÆ¯­@ $ŒAÁŠ©]û!;½Ó¹Kè»]úý)üÏÝ{ôüKÄ{½Þïݧo¿þþuÐß>øð£ÁC†F~ü÷aÃÿ1â“OGŽ=fì¸ñQÑø,fBlÜÄI“ã§$$N–4}ÆÌäÏgÍN™3÷‹/çÍOMûêŸ Ò~½hñ’Œ¥Ë–¯X™¹jõšµë²Öoظ [¶nÛž“»cç®Ýæ={÷í9U²j¤ê $ f ©B†°LÈÊ…êæê¡bq„8^\$fˆ+ÅËâCñ9ógNMª6_{Fë ˆ˜p!àZ€305p{àÏ’ KŸH#¥ÑÒXiž”/•HÕ’Ez"=—r Ù(›äP¹›ÜSî%÷—ÇÉIò9U^+ËO ¢ÁÛàk0L†Î†!†á†q†tÃ:ãÊÈŒ-Œ^F£ŸQ2v0v42FcƒTAAŠJÑ+JK¥µâ¯+”nJ„’ ¤)éÊbe™²FÉQ(G•"å´R¢\WÊ•ï”û¦SoS_S”)ÆgšbšÖ)µ³¯Ù`^d>d.4;÷ÇþBvf÷µ‡Û#ì½ì}ìýíçíÎú˜úç ï7ÔŠ–Ñ:KÅtŽÎÓ7äK­qyÔ†üè•P[ò§ +¤‹t ÿÅ+ü€{$‘L2ÒeºBWé•Òu*£DÁ¤‰Ê©‚*©Šªé&Ѝµ§‚ñÝbl)[Æ–³l%Ëd«Øj¶†­eëX[Ï6ˆÁl#Û„Ýl3Û¶²ml;Ëf9,—í`;Ù.¶›™Õñê)lÛËö±ýì;ȱÃ,aGÙ1–¯NP'²ã¬€²ì$+b§Øiv†eÅì;ϾaX »È.±Ëì +»Ê®±Rv•±¬œU¨ëÕ j‡Ú)B$Q% +¢ZE&jD­ØLÔ‰n¬’U±jv‹Ýfß²;ÿ£¹N¿²º®8Ž?wŸçœ³÷Ý÷>ç^PdRPQ4µÆ(*Îó<ĦjÕjÒÄ4®jbœ‡ÄÅyÈ ÆÙ˜Ä¬Õtµ]mš¤™cŒ" ⌊ˆ‚€CŸµºúì7¿{}¾ª@W…ê‚*RŪD]T¥ê’*S—ÕuU]S×Õ uS•«[궺c±ÎZç¬ë¼UÈÚÓ¾ŽÐ‘:JGë:FÇê8ÝH7Öñ:A'ê&º©NâHŽâh6\¹”/q_æ+|•¯ñuçSíÔ8Z§Î©w9'ÎS7àZ.èf:Y§èT¦ÓusÝBgÈnÀ1z^¨éÅz‰^ª—éåz…^©ßÖïèUzµ^£×êuz½ÎÓôF8(Ñ›çôf½Eo ÿ´íáß¶SïÒ»õ»ú=ý¾þ@ï Î +ų"½WïÓêýú€>¨éÃúˆ>ªéôqý±þDªOp,ÇqCnÄ9ž8‘›pSNâfœÌ)œÊiœÌ nÀîÁ¹Øs°'öÂÞÁ×0û`_ì‡ýqÄA8‡àP†ÃqŽÄQ8ÇàX‡ãqNÄpR0Ÿ›s ÎàLnÉ­¸5gñ ¾Éå|‹osnËÙ¸×àZ\‡ë17àFÌÇM¸·àV܆Ûq‡éfž7ÝM܉»LŽé‰»]á]HðÞ°*¬»V¥uÁºgUY÷­j«ÆzhÕZuV†Uo=²[O¬Ì°Ö¦+‚l«%08àB xàCDB”Õ +¢¡ÕÚÊ‚ˆ…8h 1ÄC$†Õ·*,–$«ÕšYÙ ) +iÍ¡d˜^ÜŽÛs!_à"¾Ã|—+ñkÈ„–Ð +ZC´¶ í =<ð?ø Ì…yð¼ óá-X a,†%°¿…e°¿ÃïñüŸñ$þ‚§ðW>Àj¬Á‡X‹u°VJOúXd„ŒÄÇøDFÉhÙ@ÆàS +E ce +’$EšˆlbrdCÙH6–ñ2A&’K!2äÉ&²©L’ÍȧФ(ЦC±G ©5¦xJ DjBM)‰š¹ŠR(•Ò(šS Ê L™,S¨%µ¢Ö”Em¨-eS;jOÏPêHÏR'™*Ó¨3u¡ßPWzŽºÑóÔzPõ¤^|«¨7åºÚE—\Ûeס>Ô—úQ@i ¦!4”†ÑpA#iv]7ä×3ýL3À 4ƒÌ`·‡›ãöt{™!f¨f†›f¤eF›1f¬÷¦7ß{Ë[à-ôy‹½%4†ÆÒ8Oh"½@“è·ô"ýŽ&ÓšJ¿§i4ïÓ úͤYô½L¤WèUšM¯ÑŸèuX k`-¬ƒõ`#äÃ&~›a l…m°vÀNØ»iWs ?ä÷ø}þ€÷˜\ojè‹Ð¿C_†¾ +}͵p‹÷ñ^þ÷ó>ȇø°Ì6I¦™I6)&n{S¸î˜4¨ Åb±T,+Åj±Vlùb«Ø.—½â€8$Žˆcâ¸8!þ"þ&þ)¾߈L†im²MÓâ´(E¢T\7ÄmQ!*¡îA܇P 5ðPv”ÏÊN|„ò1®çGü˜ŸðS'`žƒZ¨ƒzxá <a B½€2]fÊβ‹ì*»…ï»ËÙKæÊ¾r n©±"QN”/Ê©r†|I¾*_ir®œ/ÈEr‰\&W„Kk•\#×É<¹Qn’[ä6¹CdÈ]ò]¹Gî—‡åGòÓ°è>—•ÿ_„›ì;ù“<)ZÊSò¬,”%²L´‘×d¹¬U²FÖ˧J(­Xå«H#ÊUœŠW‰ª‰jª’T²JU骅ÊT­T–h¯Úªvª£ê¤ºªnª»Ê¨zª^ª·ÊU}T_ÕOõWÔ@5H VCÔP5L W#ÔH5JVcÔX/¨Æñqþäÿû[°pþ·¯&©ÉjššÎ'p”c‡%á4p: N’“ê¤;-œL'Ëiva§›“ãä:ýÁÎpg´3Þ™äLv¦93œ™¡’PYèZ¨÷ÉýÝû¾ç=ï9Ÿ{¾f dÞ5ƒíjvu»†aGÚQvMûe»–]ÛŽ¶ëØ1v]»ž]ßn`7´ÙíX;Î)uÊœÓÎç¬sÎ9ï|ã|ë\p.:—ÄqWÜß;Gé Ú! +¬£ü +í¤]ôߢ?QqŽÑ :Dù²»ì!{Aé¦:è(/äEÎqni:óm™.ûȾ²ŸL“½Ãß ?~0<3¼8üPx–²}‡è —;ìnåHG‰b5…ö9h~œ€t¼°Ò°²°ÓagLÓÕÍpšƒ³ƒs‚™ÁÞ`1ß¹L+é¦TȯÓbnÏ¿á%\ÈòxÚÍyNe·–[Ûvë¸1n]·ž[ßmà6t¹ÝX·‹É4Yæ=·«ÛÍMvãÜx·‰›`²Í“c†šaf¸aFºMÝfns7ÑMu{¹ino7ÝMr[¸}Ü·»ÛÃí||Á }<ö„'=åiÏò^%¯²g{Ø‚çzážñ‚žçUñ^òªzÕ¼ê^ /‹ô¢¼š^f0ŸRqpnp^° 8ßk³Æ‹cÎè©/}Î~ç€sÐ)v‰ãâ„øZüEœ§D‰(eâ´8#Ίs⊸*®‰ë↸)¾·ÄmQ⼤Aõ¥ËhYGÆÈºàÌ`©³Àžº§Ny2ô@=4ꦓu +øqDÕÇÀSºD—‚GcôX=d¥Gë\ÙH6–±2„š¤óôdÐi•Fͳ¦ÊxÙ¤Z"dSÙL6—‰2I¶¯€2+ÌJ³Ê|lV›5f­Yg~g>1ŸšõfƒÙ([ÊWå?ä²³|Kv‘]e7·¯™`&Ê<9ÙÔ6uœc¦šifº™afšYf¶™còÍ\½—"1¢ô&ŠT +Ò/ǸSq åøw*~¯¸Š{P”»ÿ;ˆ6Ó8Úð æÇT¡ƒö ªNP ú%­†š[JùdÑÛ¸3zÁ4î/åH¿ˆšAçIŒ<Ûzo/Uçÿ.´ßlyoÍ&—êR{ê Ÿ“ýqÔŸ®ª™ÔŠ’¡#Gó4¿Ÿ¿È/ô7 vöÈþ r( + +u0•øõEÿo”€7–¡¾®raåôV™†'×@{®’ûÙþÏð  +´„¥P ‹xÌžEåÁy²#fYïo÷à©—iôì*h³–ÜYÄèþ~Š_BÕ±Æ̺’v€)»“ýt™Ãôcƒÿ˜"© uÁ~Ѝ”‹eèÅôÐ눘F”b©5~Eè8†<$Fé0„ïëoýóT•)Þn›·ù)ºOôŸò˜êäw pÄeIE´é(4i:áÜýí(±V¾O•°b",“rï˜ý +zí]è£ËäzµU=³j…®ùá8‘†ô1­¡CPºP¦cx×MÑQd ³¹!—ª-êl`vý …´•žrþ§ò¯yçq>µúõ4ßíEo1 ÝË™+÷«°45FÍDUÍ·î„ú…Ž„Î„žúIþJE>L‡÷Ëh-v¶‡Ê ‰.ÑUºÁš‡ÕáNçI°) ð§¼™·pV9Í7ø.TÄ~µ@P 5+:}X=ñ¾îl5XRA“ûâ'YCÖE·”må¯ä(x•/?€í”×U”*ƒZM‚-×ëÐ{lÕ‡õc+,0£U:õ|ý‹¸WBšZÚ*ò¯S5œa¢Mmáý ØPœ÷rdÜé‡!vQÇí8‘É࡜ËÉY¼Š7þÛ÷m¼QºÀà³ RásSè‹¢ì‘%rÑaŠ"ñ­øYÐ[YMÆ¡vÈ,9VN”Ëåvy +]Ý ù£|ó•­¢U]ÕPÅ«Î*CSkU¹*×ýA¼[–m°æX»­¿^ ´ ô ¤@©î +œ¯4°â›‡/ߟéþøzÎ7åNZ$Z¨Hð¸ùœA™2E SÅfž+&s‘¨¯'XmDîNUCÄú˜X‡~²LánœFCEâf³ªªÏqi«¾¢jöVŠ™'XaÔ@]Ýòd’ŠÁu*¨ÒLÛ…êÞ ´—)¸ÌIF^¤ƒ«`+À … ÊA÷ÅJ©Èê-vS¶gP‡H õ¢·ýÏh¥ŸM#ýBJòý≮™nÑbÚ̳C“h4ÕFå\ádÝI”éN~‚(—DšXþÿç‹h7àºÛ†Úé/©@] 4zÝ_àƒìn ®¤w©+}‡]>Ä +oÉbjê.¾ð;ÉÑØïUJõ7ùÑlÓ8õ }´1 iP g¼Ïb¿“(KôòÇʬPâ°QxÑþÌS¹j¦ú‰ü‹ñj‹â:Ãgf¯6¯¹{Ls¶‡5„µ1”|‹=ñzÌâ;3Æ4»^Û˜[p J ­Ô õ …*BÔêC…"å,ð°F©JóPžòÚ¾µ©b¤>µ!â~ÿ™Ýõ.½¨ëùæœÿþŸþsfŒ=çÍmì›;Ø9Øûæ‘K?:wöù3oŸ>uòÄñ¹c³3SG­Ããc£ozÃìíy½»«³£}ïžïïþÞ®m;Z[¢Û_ݶµ9²E|7Ì_ùÎæ&£±aSýÆ ë×­­ Õ®Y]³ªº*ðû¼]c-q‘HqÙœ’Þf±+Ñ" FºŒ‘’¬D¥Žä)¥Æ+5Mhξ¤iºšfIS ñnÖÝÚÂã‚ËÏûÏkCæ×û…ÍåWj~PÍ¡æ«1‡aÀã›æú¹ÔR<.ïÎ9ñT?ÜåVUÇDl¦ºµ…åªWaº +3Y/æsZ}¦&z}¼3§³àj$%E\6ˆ~Ê@z"ñô´²âýF8l·¶H-–S’‰>YU*,¦ÂHLT~œVîò\ËCçZ>ĦRÑši1ž´¤'mSŒº(âöËú K›VH8_³.—K ßtœé8—¹¼=d•KÃt·mø€­I¤œB_C“#ÑôK¶%µKÉi%´*w}3"NœÔ .«DŸ˜sN¤ðhɆχï66š‹Ë_°Æ8wF-–½†°ÓýM¹õÌ>¯Áä •’Ö–\¨Î-lnMmaR³º|2S’©™R§Yr¸TY2hÉ3™Xkj§ÛL;s2íPÃÏÖ`%§ñDŽËªXÊ uŸì¥/ÜyÊÐâ«'•œtㄞ2šRŸ”Z òâ\F£rûvj‘@ Ï9ö(zOkË»y]ˆùÇ€ò±AÔ6mw¶¡üá0=à«y“MÙ!Ë¥9›2î2³-jK=E’‡EɆ1’d‹’’yJ “ï3 ÇÌl.]µ¡ëâsRÛø?Ä3®<9"’C;©Bm“£”+o/É +3¹.fy ½0Ó ’¢)'KÊDX5ÒÁåWM=Ñ•Š£ñ„ ¥ö»w»:þ?òËÿ +5¬˜Ò”ÑJº«‚®H¯Æñ a¼\“£ŽS]!C«¹ +:žZa“l ;3‚+¿ü°`ÒDÉb¤€þsY²BÑ(Ìmü¨;[[8è'!xÂI9éürvJðpõÏôÏœùxªØ8ùåW ™¸f£VsZgk‹ ‰ãLç˜'‚0¦‘ÓÔdoìª-ߌÚBNEEXX3XK®“Õ„GS1ÌtÖ—Ú•¡œ©]™°CŒñ+£Ö]]Óc©>;·2k‘3f*®N\bÁ‰`I ¥¹«•¾±h2–UR¯b(:“טâ‹?µjt'9‚$au»Q]&æd(5!ß? Óêä¸8SHŽÓJ9¶¯ÉvŽ?ªdÆ-÷N"­¥ žl™*êM色¦ª¯î5ÑRŠöÓb´³ˆF§Nfþc4d/µ#tW—J?÷n|¼¥Ý Î¤3~ Ë͸È5M¶ò€L>T™hêå”Á7Á,í%N‡ŽIq §ŠªQS£s@ħ¡AÀKwV˜OÛ¤%hÓPãÿW%­L‰^$ʹê*RZr·¯#U’s%2AÀ7Jd‡{L`-jˆå Cž²£%•4­ÙÁÞî¤ Þ©Œ÷Rxíì“ÙL)â}3`ƒ[SnéEíЗS& 3ªr!’|;Zág‚†# +Žh92;ÈS6Oá цPlƒKF>‹Ï'‘¦scÐ]Ï  ig¶Œ›!8ÏfÓ3‚WIýîVŸrô";6bIf8Ž@!ÅHÊpß,ýÍ4àšŠô }ÙÍ҇݌ûÉtUuÈ›a*zDÕ…ÃF›¢[Æ¡ïÆ£©(*Qç¬ux‡ƒ g•·93žÂ¹ÆC<ÁÕ£N P„¢l8r«"¤{u5ËÓÑÜÑ@d…£®3QW9¨¼ª9XT ¨ “w¢R¯o‡¯ O¨÷Ï@yMt•AÖØE£…׆k?@¦Fñ¹fàØÅú=Ñ® –Ÿ„“rmrøˆÂ¶ªCŽùðÇ<,ÀúîëÚ’?×ošë˜Ï»äaÕï’Æ‚~ß’îùÿVi7µlS4ôM÷‹îC¡¯»¾èf½˜‡žã¶kg¸.\ÁMÃ!ûœ{>7}쟌{2õ•`lßððÜ[µÝOƒFPý/³ðåÖí4þñý¥'Ï>yq,Ä‚C « O@ çÛC,bÏ>yv!äú)ûÕÝðXôhRÿû¡÷Û 6³ûÆ™¥]fúö3‚g33½³³Ð½ú ŒÈúcÀ_€n`h,ði`„hè.’-|Ì“5žcÁWØßøò Ä»á{Äf[˜/x¿d¿ñw°Ó  »ßyÛK:°¹á¿Ã>ÿ#È3àÝÂhþæ“°ÛY˜W®³?ø¯ÂÏÕÂz·z~Ï^óž[þ+ÖbÃçàÄĘ’ÐY‡±¸¬=bW´GË cdÿ2ñþ¸~.AÞ »- /bÞˆ<ük0°Mÿ˜uèëÙ§Û°þÃîºGlŽÖ\Zò/äôïpsL–1 ½cù1ƪ²Ü^ÆÅ—0àÙͲO0¤ÎN{À4Ôë¦ï1óÐwT§?¯{§Ù!ÐòñÝg¿$8¨pnù…÷#vÛó5k‡ì‚ÿÖ1zï¾amúÖê°Ÿ£¿úáÿ=à|þMõÃ4Eüw{«ú¸†X/Ö‰jú=<×aÄzNûö#À><—,pŠòAü6ª9=wmüÛè.Ag’~½ÖN=I6d_‘B.¬Œl:×Q×/0z ”CªÏ +€ìðÓøÍÀà1°œ:$° ±âzT¿¢gþÅ}¹WU]q|Ýsö9÷òš$txQ ˆ „_ÔDÄb+"$±‘ÔÔZ>ð1Ú±ˆâ(B†¢¦À Э¨È05íÔR´âÔi°TÚi~ÌØ–Ééoí{N¸œ€—Pû¥wæ?ÿ³÷ݵ×^¯­¶iíÛðv¢Cd³6›>C“½Ï´Ï¬ ×Ò}Îó7KmˆótMõµYdy5Z[}Jm&bkßµÖî?ÕsªMu1¾gÚeªÊ`}ÛŠXý™Õ +i°¼YêÕfU¾ˆU/jkV'øDÈ“2Î:ÖúìŠ|-´õúˆ#]tñ²ž5çú·SÖH™¹OÊÜgåVów™âŽ”1ÞXú8c_qÚå†T«Œç.§ÓþQŒW)’m‰y^+çlAŸmò":½Ç´9ç›¶„絟x’Øåµ8ØïnG¢5ýŸ²"ó¿žöŸ œ=^ 1³%8äµçY®>‘lOŒçFLÿOA¸05*±*U›Ø–¬\_ä¸Ë”Èå^‰\BÒ)6Äy|þ +ï/ò¶»Œ»n öR~×9¬‘,[œFb{9{¤^¡ëÃwgØÑI6·¥ˆ#{³ÆüЦ†Á>þ·;ÄÁÇÀQìèZlræÏ6?£ÁâÐ^çuÙç.y ~2²Ï˜Î‹Ùg߸]ÆÙæâ{ä§ìõDt~ã4FjœÓ8sÆü¥N3v¬qø}©ýúüåȸ?ô}â0÷]~i°Ñßlrû›ü"¾¼`#ºx°+§Þt†ùtd”KÓýÒ'Ê£ÞxYƳõ6Þ–6VZùzù[äQ¯ƒ{'Zyׄ>ˆ>‘»ÖÌEç«å)Î1È]‚?Òf«Nì]ˆ Ô¼ 9Ñ]‰ž5-“z÷cê;^òl¾(–*dßeûÈ©ÊÚçUÉ:¿]ŠL±¶Ujô®ô*Þ}ê~é—* N´É8óÆHoÆ­±:(‘Ö.tn-µºHÞ&Ilö:Æèzkíœéêc½Õ…O-¢6¬º`M¿@n°õD»üØ«*|hm²NÖúø\lb—˜W®²0o°Í×+å&ü«ØÔ@ÌkÿÕA‡ÛÂy$®·µÈ@¯ÖÚ³O1é»DýÇm–¯«ø+‰ÃZO¬”¥f”\ã×Ê2ú–QŽ`ß'é{ÿ…ï>ÁüaaÜö~‚~[¬µŒÖê/Éà×Ù:@¬ Z§°¿û‰¬uË¥;¾*µ=,’ÑrF¿`sš˜Án7W~_⌗Ù¡ßšCß2É÷L¥¹ãðÝ<m~‹¯~&Ï»92Ǽ+Ï›mò”¶ÍáòºÚ#€ŽµóûÉT…ú®ÂÙÊ{ tý?A®QœÐ«LT½º«ÓÿG÷ÝKü~oœÙ-WÁÃáËá™pyÄ™>÷Ûx_KN5&æãN·æÿðwÁNð«ÿõ^ ÁVA.ð÷Q‡SG¶QŸÜ$õ"lj%Ÿ_ 6‡fÁÑGöî úñGßwáE:Žò½þ¶4Ç ‘5a]9ˆ¾7¹©p½™éù¿ù÷°%=¿£Ìãû€|ÞñGøçð*ÆbÞãð/ÒÿŸCû°ƒv;íùàF¾Ÿ à‹ÀПù +­Gº½C¿t>õûãL™šå6äo‡Ž¿!Θ£ûÌÂñ·FtÿÙØ ßÝ9­ÞLû©û^É|û|Ñ'bî³3¦"8NMÙWëh­eµ~¶õcÈöýfëXöÉyziýªµ³Ö¯°®¿Ä÷¬<È5×ÊæÌØš8"M  ¹–1Ÿ9ÃÝÄžìû(o£õ +ÚØ˜T¦|@îÊ!×½MÜ= +¿O{(|4ÊiQlíc³ä´/»ÝÓy9µ(ÄœN×áÒÓñ\ÜSdËÝgËO“£3óôÛŽò|„^WJ‘"YlWÄëÒnu@–v¶:·§íxÝÑãv¬.‰Úqtû?n{Q=3Xw!æw=…¾-Ìë'jÿH†¸wù[ØFG×d‚80"Ì¡ëÀ?‰C9*XNû‘ÔçR”zYŠh7òbP jô?xbb™ˆs,8Nû‡´sÍûvì!j²ÙsÜnµ>·õ!:³qð•_.W€þàU° ºk}C²÷Ÿ²®¾sMupÔì±0+O{ÀË´shç‹óý<âv‰lä{1ÜîM|Ÿn'–_ïí Žûß·cÊù¯ÔÜ'eÄù;Mk~IL_`:%'ÙW–;ëÉ¡Ãø¿‘¹ ´ àÉse=ëlcþ“šüÃäÁ*òa/Íì[)M –±ÓÍaYáö‘)¬ShH~Èc½¹Eó•?Fr5çÑ7aùµñl™ŠYo’æ·ù+sÉ?N¾ìp¯“f³,d½Wz7KS¯Ò”ª‘ÒÔ£Òè7K£û‚ÔÓ÷BòiyÁ%Kt(¯jNŒ¾)¦É¡6ç/ =8äÉÑ™ã5•o¶|“¼¼.sßh^ª”\z˜ó³·Êš­¶!Ç/5œÃÀÇâû©Žœæà7i–;Âÿ@Wί”ÙÈY¬:µº-3ÜGx÷iN×ý7À{äf³„:ŽËí…^ŽŸ®Šj¾«@™Þ³¹[íÊÚRÞ'ö¾¦éyýðá½ÿà-ÕŃŒwdùÀ†TNö5T9{߄މ¯`ƒæ9j¦fy<cƒ vÞ|;oŠ?#×íÌkž€,:à ©¥èKïÏÉÞ‚:ï±×e’cõw/2=%³Ì\ê!‘ÁèQÏ=ÐŒ _ísàþÁC´ íÙC¶º*a^ŽL³g¤¦rLjð_ʽBë+ôŽM¾)¥Éìµ”z¯I¡{õK+±î«Ü]9÷š#õî~9Ç\*·¹yR£H”»í0•ºÂ9Dÿ^øYÚK¤ÚùHnF_u`>Xʹ;,Þ¥VøËÝ!¾£pšçñÿŸÀ·Ãï¡éoú.“7,¢5šeCìÎ +öž,5Î6öXƒ,ìãæâ10çÖ#Â}¦š*|ìd\s•/Žƒ~å âûÿÃz¹ÀfYqüù¾÷Ö–réB œnЍW¡ k  Lê̆Qq…¡NdsÑÁ-*‰ '°â6/›lnâ%Ù Ì$ré»ßsÎy¿~ýÚÒ,ò%¿<ß{ÞsyÞsyžÿ)Î…rµ%¹P^ÒŠmÕk˶ÊûåBy¿óàG[ýöÉ…ò>çðo\.”û?ühkžûæByßsø1!Ê'äúA|âÛ¸»éì;.ß„e÷5þÿÜ/â™îùWïP «à$”8ˆyñtêÔ`?†0©‰ÆØ Åü’qâ•0&Û±´mãN;¶ÁÙø’möyì«9Ï_…#v<3¶ÆÞØ>°Ú}ßR7îVë{ãʦúÚo4í¶6{p#í{a+šhüµ%Þƒ}Á~ç—þïéæC¿ùeí«).È)51ã6ru÷¨ÎZÿ>obîÍrÕ÷M<ü—l2ñ.&ö]%ÃÃŽèg¤DuƒÆðàSyPEnô ZÁè…%ð÷JQpX¦ûwÉuÞvtñõÄ[Æð.7kß·UsxËä(×FÜÔ\8Ž˜[S°Íè— ¨ÓÝoÀßU²›;ÛÒ`ФhFCx~‚¼¾N~Ü' óæÉîð8¾”™ä«^át¹"xHÆ$wÛpžä_A8›W+3¢¯S^'½ý#ra~ ºîM)gξ™Œh-?’î”ëšírûÎ ‚ñÆgüE‡ùþ ôšÉäëï0'UÆŸ š?ýÍâ{‹E‚OÈÝce@”ö*Kó{ÈÚð3¾#D§’>™1Ñ^ô‹î”aAô *Y£Aèæ3Ï7IAb‰í»£ÓâÓh·uþ,£»úõÒÃhrWÆ&}ÔIm°XeO ÉÕ5‰ŽÊhŠÀ¬qe2Fæ{°š?3ßïl–Þ0óNy™_(ƒ‚Böº£…u>E…²‰ºË=í–²ÈÃn”™á¥"¸yé&Ñé]/=TŸE‘Ñuó4G_ E+¤k3¸SÄs€óOug¼šõû;ÜÂa¼Õ•®yÜòJ×–÷ñì=ÃÔá]ü÷´£ÊÖѶgßsõ54:>°˜{Hïljô¨ÕÖÍmF×›ýSÚ®ÍÑŸmY=Ãì‘n=œèÉ–v%vVòŒÎû€3ú$m{C˜èè\KݧÐ(‹¬5ÚPígŸÓ½¦Z/×ftu¶-ýš¥cí9K¬ÕÕäØ[í—èëölF7³qìž;eôz{v²äÝélôñ šXWÞ9ˆ-îOÙÖ¬‰xNǪ~/cÞ—ùkÑ¢ç@÷>ÄhÎdÅ[!ßmL¢Ds›ãt~›„?¥äõÊ%>¡àóƒ–øiÇ1ǯ/%¢ø+r‰OôîÖ +á3Œ yƒ-Ñ‹Ñÿç€9ˆLš×ÕØPsá9Ae(Ñ'Žå q¬$óžÌc2/|Û¾{VÆçd|×ï—]Ç/».çë»Ïå{6œÉÃØPiÕoÖÇp¢ñ‰ºÝ!óºêá5ÇJ…³R̹ýÔ»ƒýÙmZìƒÇ¸›*îYÏ¢¢ì¢öpG:j‘©­ÍOt‡ÝQ;OÁi¹Ýi¯Ã|GG}}óËe‰“¥—Æò®žóKüßÉÌæš/®`ßéÙ OÔïÜ#¥é?ÆÏ ‰ ÇãWƒÐÀXKk­ö‹_Ä^iæù +y»9î¶=­cód¼ÁémÕ± , ¶¼É¯$özŸó§¥Huƒ­ý2[j È;Æ{ôß°Ô»]FiÎð.G[¡?T/˜³ ÒÍkéȼ”{›²Î÷ YâW2O šÈ¬Ó>r€ÖßgÚ»¸8@Çò¾GWz¥Qw´[ª}Ûd¡ê"E0‘}1‰º“â·¼ZìÇçpþN–Ùé%2Ø›)ÃÓo¢w +)¿æó¿¶3L…5p¯ 3å§Ù'§¨žÏóŸ°TÁðôŽG-ú>U"UéíR…&®¢?[ï ic ¥*õ{3V•WBÔKsSòP^¡ûòþÚíF¸Ñ_ê˜í˼Kêä7Õ‰î—Ò‚™Rê=ŒŠŽïH}$WùÓ¤ kÚ.c­_w÷½7½ÌVü,ϯ¥_’éŠ÷‘”VÇ;¼þàlð¼ÌFÊàà,úàûàC¹*øLž®‘a9yl‹,¬Ÿ_ŸaßU¤Ư§6áKá)Ìß+׳†’§uM×6Uiò‘°§%ÅmKêmŸÜ;ccPê A4y]ˆ%øXb¬ùŸèbJ‰ä§/å[VZ¼Îñ“:Ù˜å ýýFãùׯ«‹¼b¿ÒoÛ”~Ÿ: 'ᨠç,처»Ùf“›NÙ8ib!qZÿëÝÅÝŸ:ë$^ŒjO/9mYŸcw&¶=]èÚÔ»6-ëO“ +ÿuöÉZÖNsò~¹8˜"2÷.‘Kuþƒ#æ¾2†÷ªAšt¾æ<Í“fX£J4Ñ1IåÞ üƒR¡k\+]5w1Oûàí,;Ýbò´Îcº¬€¼;ÞŒAŒ£ÿBöéIç§ÞOŠØ§Ë3w¿ä.—Ü5DFøÏÊzïN´Ð%2Æåû]Y÷ÛõŠî³à€lÐ;›ZÊþL½16o˜²Þ„·à?ð78$rö¬é—Ì}è—¢}¾b¾öI~Þx) +wX½â-–©™¦àÛ*…ò­꤈Ð[ +#`L†ëœ%æÊXç«™ïj¹ÅëŠ>(gŸ”ÊHž‡ñ¤?Z½?åÕhéÉMØJ¯ˆy¨&7V}=\ËüEÔÊúV³þ÷Ie°Wæ•Áç²1¿L6b×xiŒ’õ|ãmþ)Õ{º¢&]À}­Z&:¡}Q_Œ?Ô×wæÜÞMN[&µþÞ5`çCyl(ÏÇ¥6uTj½jÖ‰:Þ.Ê÷óþcì0ÞÏsö]Êæ. Þ{²ÂŸ%yáTbÎ|ÉóçB'ér§"ÎÜBߢÍ03N9qwÕeä1ãÓe²ÿθ†úÎëÏ}‡M£ ÝwÄ’ÛqF—ˆ™°U‚úŸC\É{d%8M>ñm¹Vc´é´þ`³_·€ N^X fƒ*'Írš<Ç>ï—÷¤OÈæ™á`Cenž>Þ>”fÿ–ºC{¥îÎû(öOÃ{lÆå¶Á¾ŒA P8äײÙM-Yó/ç^ÿ+Ð.Fä#¦9G=Æ%êq—¯—Ân)Í% ÊÁdPì0Ë)+uÒ^0sä6òŽä;À>^ø )9öƉ²ñޱû†‹sc½L ˜[qâ\çíµ„ì1&ã^8Šø.{“Öº°Y Æ]˜Ípýº.&ý}‚ŠØ ²/êÛìýu{Àób Té)„KwåãWÊàÌÉP É2·ŸÃÅâÿî¸ÑÞßeLñÓô›K;H¥ÆIÄ"‡œø#I«ñ.­ƒ| ébÏó4FÑD±”ž5¾O½ž~Êhª«lö>Iž±41¯÷ìYÄ!ˬ„܃ø+†½Š˜š‘ñ÷4:¡Ý޽y~%…Xjî•>!߇ü| 1ÌÓt/bíËh¿Žc'—Ï>Ëq+Ú{€ã"Ø +‰:˜Ÿ}ÊÛ`ÉI•Øgu×¼YÏ“Ûuqðœ™G“Ü+ð;@³²òê9Ë‘™ü¨”ïг`˜œ–öw==ìÚ·Â ÄÅ'd,rb豌~«ý†çü£Ð?O3í2Ö0Kö½¸ÿZrßqú~ø 0”ÎyWå¾Ó†=#Çpö˜Ì9 “Á*IÇ—,ÿ¹ ò +äQÐåè¼×÷¥¥=Ì-_ÉávÔýä "ßC?¤UàÝ6çìç´Í˜A39Ìv?Eß»õ:Ĉ÷€\¹0KçùA}íVøÁrçÚ@e°±NÜOEÆŸ°ÿ~Ž“{Ý'õCˆ~yû;Eße i¯~íõX´{º{¶>´ÆØGOÈïvRŸÈÃ7ߣ/í7Œ8[lk;õŠÔ@y±Óü¥±±N'ô0…t6'ÒrãQú”À›OÜŒþVЋˆI#®5v·k§ýœ{*Mu]°èS¨VôÓFÄ•½úÄÑý êµ7!‘o´8eÐñ&ìßBºéÎt9âŸÔ¦-Hot²÷èöKÚ>¼Pî>M£dÓi­¾I~Ãímûœv¢ZÌe¯L·ÛïéqŒçmŒñˆ<ûßuo§i^7EãUòyÏÑFÉ«i™_„ï’4ÅK™?»1÷¸¤­îh-ãýÍeÄËØÿ/_ïݵøn+}:soÀÞYø©jãû¤¶÷ŒØCäݲ¾§ÒyÏ ä‹Ä8>+Dý^¼)ߢyb ƹšfk;ù<:þ=ÇßóÛ ãít¢Õk —fÞu°]n8ça¼›˜[°™ÃÞtl›7×þгò «¨Á; r*5x^€o¼x½„ý[1|ãH‘y?¥ï¤mîGð¦MÒì!pËÀ‡’G ú$Õø3<»¨NÖOÒhñ3ú0ŸÃ'o™·áyºßx{fÍÔê;ï—ç¨ßåéd¿žy;êAªÑÞ†KÛ»úXøý:êÕž¦åŒ®aî™GñŽî]°õYìóS´És{ö!ìƒÔ#÷‚¾ñ¬ìË$ØZ¬ÝC'¸LÿÞ‚Ì×ä›p1Æ·I´á\žA;N£”Ú0Ëy´-4NL§g°f½Ø ÿD¿m.“ëÓqo ¯V¾«ŒÕ¸kª°A!4úP‚ú.Œ«ßÁ.÷‰%DZEòŽ"œÛÆvëÿæ¹¶ï#]¾{+hžñ;Äþ¼N;éI¯ V1ZKWkÛƒ#9ˆ½v‰DU|/¢?'ò¡ únûMϸ ‘´¯Í4BÞ«Iªu¿ý4|ÙßìwƧo—ÞŠº­XÃK´kÔš%XsF·°-ãô]PÖ90¨Dû1ÖpîÏWì¯Ã¹_²oü¬ÂšØïê'Á9¼+ȯŸ48€ñsœ_¦¦ ük‰ë}Øk‚Ý—èqaÒãh¿{›m®äwŽœ ÌMQZ+Ây+º^/ w'Þ‰Ø\‡ãìÂýØs¡OO@¯U`Tš¿HôÁf° º…:ÇÒwÞ`»öú­7EøOÿE +…B¡P( +…B¡P( +…B¡P( +…B¡P( +…B¡P( +…B¡P( +…B¡P( +…B¡P( +…Bñ†‹hôWÝG©†Î‡Ü4Š>C›‰Œ Æd MTHÏãWäžÄ¿R÷P)—L“«Àý˜£kT¨}ÉÑuè;]@ßïèªÐŽ¢¦KσÍí÷Žî¢)Bwt7 +ÓÑ5ä—9º½ÁÑô6G÷P«ØHýdR%•SUCk¦² )FQ¢nŠËœH% ó¯ù!Y£ %ó1'È&äµãû%eÊ‚´P{~ƒ²fþë‘jE®E둳LZ¢ÝL;K`½¶»`Ç„Ýl†(==޲ÄP;æPïË© +ZéPªšæÈ>øa!Žº&Úõ£¶ °S·©äriú˜ÏCHŽ#rÃþ´É¹0©éV”p®_ÎĵcLÛ‰9#5e+]( Èñrª ¶×ãÛ„ÌéB­ œ9ù™õXŒ>ñì„äwQ9·wËï-YâN´É3”¿¦Ó£L]Sæ'‘ÃóZÁ«ãàòz—IÌÂ|Y3=¢Ì(ü²O¼‚²EîsXŽ®í?Ù=ýfeyEµÙÜa™±h,շ̱D<–ð§B±h™9?1›Bí©¤Ùd%­Ä:+XfÔ[­ k½¹,nE›ù›%þîXWÊŒÄÚC3‹w'ø“Í—W™¥,ªç˜MþH¼Ã¬÷G±@¹ ±Ž¨YßLrKÍ¡¤ɶÓK˜µ¡ÖH(à˜N‹¨C£f2Ö•Xm©õþ„evEƒVÂLñ87›KB+š´î6“–eZ­V0hÍH:× ZÉ@"çÊ6‚VÊŠ$ËV-Z±À×xÛüDÈilþ¨”Ü¿™JøƒV§?6cm7žÇÿò)!Q'ý夯¢E´kïCë·eûFô„Wø_´—lçÇß÷=×¾$ vAÖŠmÅšoÅ=aö¨È–‘Þæ‰WG…o+uöxí ÛGñó™ÇóøæÉ‡À‡/hËh'™Ç ‚°=`\K üP·aàãÒaã  ƒÓ` øåp–#G«C¼,&¯¼øØ<ö6ì­¬Õj i!3ô¸2®Ñ`”öE+Q–$‘nÙᵡLë§¾¨ÿÇõ¤&UÃγqqëfÏWý¸{·núcטå©UôG$êÃÊ£ÛˆA;à·’¢lo!š*üf¢±×à®¶‡]cŸ¡+ÄQSü¶öþ‰VfÿªÍò?éeuù‘ymŠ¿£ãoÅË*2WŒ2…›Ñ¥tZÛÊ__Ò3(\tù)á¦øwµ^þŒ& £^a_-+Èwƒüqœ/£àVçœâ=Ú>¾ÃSmÇLñ‚é…ìZMv‹"óßòä“É2=d­ \ä}G‰Àº@k€ZÍ•jX ©+ÔÕZUUýªOe*QW–+‹–‰ÿ¤d¥?$þRÂúdbеâÖGU†w)ç+JŽåö¤iιzäèÎß÷ÄÊ´v× ó@,MpŽäÒÎV3WTv;I3çú¿™Ÿ¤ô|Y‡}¿LÉ@¾L+"u¶Ù ïÌOJÎ>×,üÃgŸ+HSäDOSO¸»aÛW3÷1#UkÞÝšî‰[ÒÎ…Üž¼»åÕW[Ò'!ãJqÎùá}(?M?§Ÿe3Óô–p…ü´ÒM?Ïîy¥;S(äÊt¯ÔÞ‚Kç–Ô©xJ ÑÕ¨§»èé:pµByT‰ã¯W6¯‡_Ÿ€O(q+lp…%yšäuµðgøòY æ?‘$Ò +endstream +endobj +986 0 obj +<< +/BBox [39.75 504.786 573.969 713.931] +/O /Layout +/Placement /Block +>> +endobj +987 0 obj +<< +/K [726 0 R 704 0 R 727 0 R 705 0 R] +/P 682 0 R +/S /TR +>> +endobj +988 0 obj +<< +/K [728 0 R 706 0 R 729 0 R 707 0 R] +/P 682 0 R +/S /TR +>> +endobj +989 0 obj +<< +/K [730 0 R 708 0 R 731 0 R 709 0 R] +/P 682 0 R +/S /TR +>> +endobj +990 0 obj +<< +/K [732 0 R 710 0 R 733 0 R 711 0 R] +/P 682 0 R +/S /TR +>> +endobj +991 0 obj +<< +/K [734 0 R 712 0 R 713 0 R 714 0 R 715 0 R 716 0 R] +/P 682 0 R +/S /TR +>> +endobj +992 0 obj +<< +/K [735 0 R 717 0 R 736 0 R 718 0 R] +/P 682 0 R +/S /TR +>> +endobj +993 0 obj +<< +/K [737 0 R 719 0 R 720 0 R 738 0 R 721 0 R 722 0 R] +/P 682 0 R +/S /TR +>> +endobj +994 0 obj +<< +/K [739 0 R 723 0 R 724 0 R 725 0 R] +/P 682 0 R +/S /TR +>> +endobj +995 0 obj +<< +/BBox [34.5216 241.731 485.884 434.808] +/O /Layout +/Placement /Block +>> +endobj +996 0 obj +<< +/K [690 0 R 691 0 R] +/P 683 0 R +/S /TR +>> +endobj +997 0 obj +<< +/K 1163 0 R +/P 683 0 R +/S /TR +>> +endobj +998 0 obj +<< +/K 741 0 R +/P 683 0 R +/S /TR +>> +endobj +999 0 obj +<< +/K [692 0 R 693 0 R] +/P 683 0 R +/S /TR +>> +endobj +1000 0 obj +<< +/K [694 0 R 695 0 R] +/P 683 0 R +/S /TR +>> +endobj +1001 0 obj +<< +/K 742 0 R +/P 683 0 R +/S /TR +>> +endobj +1002 0 obj +<< +/K [696 0 R 697 0 R] +/P 683 0 R +/S /TR +>> +endobj +1003 0 obj +<< +/K [698 0 R 699 0 R] +/P 683 0 R +/S /TR +>> +endobj +1004 0 obj +<< +/K 743 0 R +/P 683 0 R +/S /TR +>> +endobj +1005 0 obj +<< +/K [700 0 R 701 0 R] +/P 683 0 R +/S /TR +>> +endobj +1006 0 obj +<< +/BBox [0.0 0.0 612.0 792.0] +/O /Layout +/Placement /Block +>> +endobj +1007 0 obj +<< +/K [702 0 R 703 0 R] +/P 684 0 R +/S /TR +>> +endobj +1008 0 obj +<< +/K 1164 0 R +/P 684 0 R +/S /TR +>> +endobj +1009 0 obj +<< +/K [745 0 R 746 0 R] +/P 684 0 R +/S /TR +>> +endobj +1010 0 obj +<< +/K 1165 0 R +/P 684 0 R +/S /TR +>> +endobj +1011 0 obj +<< +/K [748 0 R 749 0 R] +/P 684 0 R +/S /TR +>> +endobj +1012 0 obj +<< +/K [750 0 R 751 0 R] +/P 684 0 R +/S /TR +>> +endobj +1013 0 obj +<< +/K [752 0 R 753 0 R] +/P 684 0 R +/S /TR +>> +endobj +1014 0 obj +<< +/K [754 0 R 755 0 R] +/P 684 0 R +/S /TR +>> +endobj +1015 0 obj +<< +/K [756 0 R 757 0 R] +/P 684 0 R +/S /TR +>> +endobj +1016 0 obj +<< +/K [758 0 R 759 0 R] +/P 684 0 R +/S /TR +>> +endobj +1017 0 obj +<< +/K [760 0 R 761 0 R] +/P 684 0 R +/S /TR +>> +endobj +1018 0 obj +<< +/K [762 0 R 763 0 R] +/P 684 0 R +/S /TR +>> +endobj +1019 0 obj +<< +/K 1166 0 R +/P 684 0 R +/S /TR +>> +endobj +1020 0 obj +<< +/K [764 0 R 765 0 R] +/P 684 0 R +/S /TR +>> +endobj +1021 0 obj +<< +/K [766 0 R 767 0 R] +/P 684 0 R +/S /TR +>> +endobj +1022 0 obj +<< +/K [768 0 R 769 0 R] +/P 684 0 R +/S /TR +>> +endobj +1023 0 obj +<< +/K [770 0 R 771 0 R] +/P 684 0 R +/S /TR +>> +endobj +1024 0 obj +<< +/K [772 0 R 773 0 R] +/P 684 0 R +/S /TR +>> +endobj +1025 0 obj +<< +/K [774 0 R 775 0 R] +/P 684 0 R +/S /TR +>> +endobj +1026 0 obj +<< +/K [776 0 R 777 0 R] +/P 684 0 R +/S /TR +>> +endobj +1027 0 obj +<< +/K [778 0 R 779 0 R] +/P 684 0 R +/S /TR +>> +endobj +1028 0 obj +<< +/K [780 0 R 781 0 R] +/P 684 0 R +/S /TR +>> +endobj +1029 0 obj +<< +/BBox [0.0 0.0 612.0 792.0] +/O /Layout +/Placement /Block +>> +endobj +1030 0 obj +<< +/K 1167 0 R +/P 685 0 R +/S /TR +>> +endobj +1031 0 obj +<< +/K [790 0 R 791 0 R] +/P 685 0 R +/S /TR +>> +endobj +1032 0 obj +<< +/K [792 0 R 793 0 R] +/P 685 0 R +/S /TR +>> +endobj +1033 0 obj +<< +/K [794 0 R 795 0 R] +/P 685 0 R +/S /TR +>> +endobj +1034 0 obj +<< +/K [796 0 R 797 0 R] +/P 685 0 R +/S /TR +>> +endobj +1035 0 obj +<< +/K 1168 0 R +/P 685 0 R +/S /TR +>> +endobj +1036 0 obj +<< +/K [799 0 R 800 0 R] +/P 685 0 R +/S /TR +>> +endobj +1037 0 obj +<< +/K [801 0 R 802 0 R] +/P 685 0 R +/S /TR +>> +endobj +1038 0 obj +<< +/K [803 0 R 804 0 R] +/P 685 0 R +/S /TR +>> +endobj +1039 0 obj +<< +/K 1169 0 R +/P 685 0 R +/S /TR +>> +endobj +1040 0 obj +<< +/K [806 0 R 807 0 R] +/P 685 0 R +/S /TR +>> +endobj +1041 0 obj +<< +/K [808 0 R 809 0 R] +/P 685 0 R +/S /TR +>> +endobj +1042 0 obj +<< +/K [810 0 R 811 0 R] +/P 685 0 R +/S /TR +>> +endobj +1043 0 obj +<< +/K [812 0 R 813 0 R] +/P 685 0 R +/S /TR +>> +endobj +1044 0 obj +<< +/K [814 0 R 815 0 R] +/P 685 0 R +/S /TR +>> +endobj +1045 0 obj +<< +/K [816 0 R 817 0 R] +/P 685 0 R +/S /TR +>> +endobj +1046 0 obj +<< +/K [818 0 R 819 0 R] +/P 685 0 R +/S /TR +>> +endobj +1047 0 obj +<< +/K 1170 0 R +/P 685 0 R +/S /TR +>> +endobj +1048 0 obj +<< +/K [782 0 R 783 0 R] +/P 685 0 R +/S /TR +>> +endobj +1049 0 obj +<< +/K [784 0 R 785 0 R] +/P 685 0 R +/S /TR +>> +endobj +1050 0 obj +<< +/K [786 0 R 787 0 R] +/P 685 0 R +/S /TR +>> +endobj +1051 0 obj +<< +/K [821 0 R 822 0 R] +/P 685 0 R +/S /TR +>> +endobj +1052 0 obj +<< +/K [823 0 R 824 0 R] +/P 685 0 R +/S /TR +>> +endobj +1053 0 obj +<< +/K [825 0 R 826 0 R] +/P 685 0 R +/S /TR +>> +endobj +1054 0 obj +<< +/K [827 0 R 828 0 R] +/P 685 0 R +/S /TR +>> +endobj +1055 0 obj +<< +/K 863 0 R +/P 685 0 R +/S /TR +>> +endobj +1056 0 obj +<< +/K [829 0 R 830 0 R] +/P 685 0 R +/S /TR +>> +endobj +1057 0 obj +<< +/BBox [0.0 0.0 612.0 792.0] +/O /Layout +/Placement /Block +>> +endobj +1058 0 obj +<< +/K [831 0 R 832 0 R] +/P 686 0 R +/S /TR +>> +endobj +1059 0 obj +<< +/K 1171 0 R +/P 686 0 R +/S /TR +>> +endobj +1060 0 obj +<< +/K [833 0 R 834 0 R] +/P 686 0 R +/S /TR +>> +endobj +1061 0 obj +<< +/K [835 0 R 836 0 R] +/P 686 0 R +/S /TR +>> +endobj +1062 0 obj +<< +/K [837 0 R 838 0 R] +/P 686 0 R +/S /TR +>> +endobj +1063 0 obj +<< +/K [839 0 R 840 0 R] +/P 686 0 R +/S /TR +>> +endobj +1064 0 obj +<< +/K 1172 0 R +/P 686 0 R +/S /TR +>> +endobj +1065 0 obj +<< +/K [866 0 R 867 0 R] +/P 686 0 R +/S /TR +>> +endobj +1066 0 obj +<< +/K [868 0 R 869 0 R] +/P 686 0 R +/S /TR +>> +endobj +1067 0 obj +<< +/K [870 0 R 871 0 R] +/P 686 0 R +/S /TR +>> +endobj +1068 0 obj +<< +/K 1173 0 R +/P 686 0 R +/S /TR +>> +endobj +1069 0 obj +<< +/K [841 0 R 842 0 R] +/P 686 0 R +/S /TR +>> +endobj +1070 0 obj +<< +/K [843 0 R 844 0 R] +/P 686 0 R +/S /TR +>> +endobj +1071 0 obj +<< +/K [845 0 R 846 0 R] +/P 686 0 R +/S /TR +>> +endobj +1072 0 obj +<< +/K [847 0 R 848 0 R] +/P 686 0 R +/S /TR +>> +endobj +1073 0 obj +<< +/K [849 0 R 850 0 R] +/P 686 0 R +/S /TR +>> +endobj +1074 0 obj +<< +/K [851 0 R 852 0 R] +/P 686 0 R +/S /TR +>> +endobj +1075 0 obj +<< +/K 1174 0 R +/P 686 0 R +/S /TR +>> +endobj +1076 0 obj +<< +/K [853 0 R 854 0 R] +/P 686 0 R +/S /TR +>> +endobj +1077 0 obj +<< +/K [855 0 R 856 0 R] +/P 686 0 R +/S /TR +>> +endobj +1078 0 obj +<< +/K [857 0 R 858 0 R] +/P 686 0 R +/S /TR +>> +endobj +1079 0 obj +<< +/K 1175 0 R +/P 686 0 R +/S /TR +>> +endobj +1080 0 obj +<< +/K [859 0 R 860 0 R] +/P 686 0 R +/S /TR +>> +endobj +1081 0 obj +<< +/K [861 0 R 862 0 R] +/P 686 0 R +/S /TR +>> +endobj +1082 0 obj +<< +/K [875 0 R 876 0 R] +/P 686 0 R +/S /TR +>> +endobj +1083 0 obj +<< +/K [877 0 R 878 0 R] +/P 686 0 R +/S /TR +>> +endobj +1084 0 obj +<< +/K [879 0 R 880 0 R] +/P 686 0 R +/S /TR +>> +endobj +1085 0 obj +<< +/K [881 0 R 882 0 R] +/P 686 0 R +/S /TR +>> +endobj +1086 0 obj +<< +/K 1176 0 R +/P 686 0 R +/S /TR +>> +endobj +1087 0 obj +<< +/K [883 0 R 884 0 R] +/P 686 0 R +/S /TR +>> +endobj +1088 0 obj +<< +/K [885 0 R 886 0 R] +/P 686 0 R +/S /TR +>> +endobj +1089 0 obj +<< +/K [887 0 R 888 0 R] +/P 686 0 R +/S /TR +>> +endobj +1090 0 obj +<< +/K [889 0 R 890 0 R] +/P 686 0 R +/S /TR +>> +endobj +1091 0 obj +<< +/K [891 0 R 892 0 R] +/P 686 0 R +/S /TR +>> +endobj +1092 0 obj +<< +/K [893 0 R 894 0 R] +/P 686 0 R +/S /TR +>> +endobj +1093 0 obj +<< +/K [895 0 R 896 0 R] +/P 686 0 R +/S /TR +>> +endobj +1094 0 obj +<< +/K [897 0 R 898 0 R] +/P 686 0 R +/S /TR +>> +endobj +1095 0 obj +<< +/BBox [46.2816 244.131 548.516 480.562] +/O /Layout +/Placement /Block +>> +endobj +1096 0 obj +<< +/K 1177 0 R +/P 687 0 R +/S /TR +>> +endobj +1097 0 obj +<< +/K [899 0 R 900 0 R] +/P 687 0 R +/S /TR +>> +endobj +1098 0 obj +<< +/K [901 0 R 902 0 R] +/P 687 0 R +/S /TR +>> +endobj +1099 0 obj +<< +/K [903 0 R 904 0 R] +/P 687 0 R +/S /TR +>> +endobj +1100 0 obj +<< +/K [905 0 R 906 0 R] +/P 687 0 R +/S /TR +>> +endobj +1101 0 obj +<< +/K [907 0 R 908 0 R] +/P 687 0 R +/S /TR +>> +endobj +1102 0 obj +<< +/K 1178 0 R +/P 687 0 R +/S /TR +>> +endobj +1103 0 obj +<< +/K [909 0 R 910 0 R] +/P 687 0 R +/S /TR +>> +endobj +1104 0 obj +<< +/K [911 0 R 912 0 R] +/P 687 0 R +/S /TR +>> +endobj +1105 0 obj +<< +/K [913 0 R 914 0 R] +/P 687 0 R +/S /TR +>> +endobj +1106 0 obj +<< +/K 922 0 R +/P 687 0 R +/S /TR +>> +endobj +1107 0 obj +<< +/K [915 0 R 916 0 R] +/P 687 0 R +/S /TR +>> +endobj +1108 0 obj +<< +/BBox [0.0 0.0 612.0 792.0] +/O /Layout +/Placement /Block +>> +endobj +1109 0 obj +<< +/K [917 0 R 918 0 R] +/P 688 0 R +/S /TR +>> +endobj +1110 0 obj +<< +/K 1179 0 R +/P 688 0 R +/S /TR +>> +endobj +1111 0 obj +<< +/K [924 0 R 925 0 R] +/P 688 0 R +/S /TR +>> +endobj +1112 0 obj +<< +/K [926 0 R 927 0 R] +/P 688 0 R +/S /TR +>> +endobj +1113 0 obj +<< +/K [928 0 R 929 0 R] +/P 688 0 R +/S /TR +>> +endobj +1114 0 obj +<< +/K [930 0 R 931 0 R] +/P 688 0 R +/S /TR +>> +endobj +1115 0 obj +<< +/K 1180 0 R +/P 688 0 R +/S /TR +>> +endobj +1116 0 obj +<< +/K [933 0 R 934 0 R] +/P 688 0 R +/S /TR +>> +endobj +1117 0 obj +<< +/K [959 0 R 960 0 R] +/P 688 0 R +/S /TR +>> +endobj +1118 0 obj +<< +/K [961 0 R 962 0 R] +/P 688 0 R +/S /TR +>> +endobj +1119 0 obj +<< +/K [963 0 R 964 0 R] +/P 688 0 R +/S /TR +>> +endobj +1120 0 obj +<< +/K 965 0 R +/P 688 0 R +/S /TR +>> +endobj +1121 0 obj +<< +/K [935 0 R 936 0 R] +/P 688 0 R +/S /TR +>> +endobj +1122 0 obj +<< +/BBox [46.2816 315.289 575.981 504.248] +/O /Layout +/Placement /Block +>> +endobj +1123 0 obj +<< +/K 966 0 R +/P 689 0 R +/S /TR +>> +endobj +1124 0 obj +<< +/K [937 0 R 938 0 R] +/P 689 0 R +/S /TR +>> +endobj +1125 0 obj +<< +/K [939 0 R 940 0 R] +/P 689 0 R +/S /TR +>> +endobj +1126 0 obj +<< +/K [941 0 R 942 0 R] +/P 689 0 R +/S /TR +>> +endobj +1127 0 obj +<< +/K [943 0 R 944 0 R] +/P 689 0 R +/S /TR +>> +endobj +1128 0 obj +<< +/K [945 0 R 946 0 R] +/P 689 0 R +/S /TR +>> +endobj +1129 0 obj +<< +/K [947 0 R 948 0 R] +/P 689 0 R +/S /TR +>> +endobj +1130 0 obj +<< +/K [949 0 R 950 0 R] +/P 689 0 R +/S /TR +>> +endobj +1131 0 obj +<< +/K [951 0 R 952 0 R] +/P 689 0 R +/S /TR +>> +endobj +1132 0 obj +<< +/O /Table +/Scope /Column +>> +endobj +1133 0 obj +<< +/O /Table +/Scope /Column +>> +endobj +1134 0 obj +<< +/O /Table +/Scope /Column +>> +endobj +1135 0 obj +<< +/O /Table +/Scope /Column +>> +endobj +1136 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1137 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1138 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1139 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1140 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1141 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1142 0 obj +<< +/O /Table +/Scope /Row +>> +endobj +1143 0 obj +<< +/K 740 0 R +/P 1163 0 R +/S /L +>> +endobj +1144 0 obj +<< +/K 744 0 R +/P 1164 0 R +/S /L +>> +endobj +1145 0 obj +<< +/K 747 0 R +/P 1165 0 R +/S /L +>> +endobj +1146 0 obj +<< +/K 788 0 R +/P 1166 0 R +/S /L +>> +endobj +1147 0 obj +<< +/K 789 0 R +/P 1167 0 R +/S /L +>> +endobj +1148 0 obj +<< +/K 798 0 R +/P 1168 0 R +/S /L +>> +endobj +1149 0 obj +<< +/K 805 0 R +/P 1169 0 R +/S /L +>> +endobj +1150 0 obj +<< +/K 820 0 R +/P 1170 0 R +/S /L +>> +endobj +1151 0 obj +<< +/K 864 0 R +/P 1171 0 R +/S /L +>> +endobj +1152 0 obj +<< +/K 865 0 R +/P 1172 0 R +/S /L +>> +endobj +1153 0 obj +<< +/K 872 0 R +/P 1173 0 R +/S /L +>> +endobj +1154 0 obj +<< +/K 873 0 R +/P 1174 0 R +/S /L +>> +endobj +1155 0 obj +<< +/K 874 0 R +/P 1175 0 R +/S /L +>> +endobj +1156 0 obj +<< +/K 919 0 R +/P 1176 0 R +/S /L +>> +endobj +1157 0 obj +<< +/K 920 0 R +/P 1177 0 R +/S /L +>> +endobj +1158 0 obj +<< +/K 921 0 R +/P 1178 0 R +/S /L +>> +endobj +1159 0 obj +<< +/K 923 0 R +/P 1179 0 R +/S /L +>> +endobj +1160 0 obj +<< +/K 932 0 R +/P 1180 0 R +/S /L +>> +endobj +1161 0 obj +<< +/D [23 0 R /XYZ 34 494 0.0] +/S /GoTo +>> +endobj +1162 0 obj +<< +/D [24 0 R /XYZ 34 229 0.0] +/S /GoTo +>> +endobj +1163 0 obj +<< +/K 1143 0 R +/P 997 0 R +/S /TD +>> +endobj +1164 0 obj +<< +/K 1144 0 R +/P 1008 0 R +/S /TD +>> +endobj +1165 0 obj +<< +/K 1145 0 R +/P 1010 0 R +/S /TD +>> +endobj +1166 0 obj +<< +/K 1146 0 R +/P 1019 0 R +/S /TD +>> +endobj +1167 0 obj +<< +/K 1147 0 R +/P 1030 0 R +/S /TD +>> +endobj +1168 0 obj +<< +/K 1148 0 R +/P 1035 0 R +/S /TD +>> +endobj +1169 0 obj +<< +/K 1149 0 R +/P 1039 0 R +/S /TD +>> +endobj +1170 0 obj +<< +/K 1150 0 R +/P 1047 0 R +/S /TD +>> +endobj +1171 0 obj +<< +/K 1151 0 R +/P 1059 0 R +/S /TD +>> +endobj +1172 0 obj +<< +/K 1152 0 R +/P 1064 0 R +/S /TD +>> +endobj +1173 0 obj +<< +/K 1153 0 R +/P 1068 0 R +/S /TD +>> +endobj +1174 0 obj +<< +/K 1154 0 R +/P 1075 0 R +/S /TD +>> +endobj +1175 0 obj +<< +/K 1155 0 R +/P 1079 0 R +/S /TD +>> +endobj +1176 0 obj +<< +/K 1156 0 R +/P 1086 0 R +/S /TD +>> +endobj +1177 0 obj +<< +/K 1157 0 R +/P 1096 0 R +/S /TD +>> +endobj +1178 0 obj +<< +/K 1158 0 R +/P 1102 0 R +/S /TD +>> +endobj +1179 0 obj +<< +/K 1159 0 R +/P 1110 0 R +/S /TD +>> +endobj +1180 0 obj +<< +/K 1160 0 R +/P 1115 0 R +/S /TD +>> +endobj +1181 0 obj +<< +/Length 2867 +/ID [ <42C8B09E433CA24390DAC708942C4FFA>] +/Info 7 0 R +/Root 1 0 R +/Type /XRef +/Size 1182 +/Index [0 1181] +/W [1 3 0] +/Filter /FlateDecode +>> +stream +xœ×eÀUe¶à½öQ1±°¥»ADéîî’”iDJé.„‘iÁQgLæŽ:Îèèx[ééûÜÏŸoŸóîõ®µÞõž/I’$’äöHÒÿ÷m$w}ÉÝ…xÞß¿‰$[–H(Ã|Gòà€HÊÆHIöì$ÇòHrVga$¹²G’ût$y»D’ïëH +4‹¤à¶H +ç¢+k#©6:’ê3#©á]5ûERki$µ=¯ÝžS‘Ô©ÊÎHÕ‰ä‰Û"iU4’ö¾Ó%"é~c$½7FÒïb$ýK±%’}"Ü3’¡Ó#Ö$’«"y©i$[?‹äe‘lkÉ¿·FrvNDÖì¹¶F佑¯&SøGDþ»hÍ2>(òO±‚Ÿ# +¡k8Q(͘ÌÿD¶Fá:Ìàóˆ"7PlçbDÑ e7W"Љ¡˜Š}Q<õy–müQB¼%¼£Ä ìãdDÉrôÇš%F”G©,ᇈÒ÷Òû-ýaÄ#·S›1ü eª3‚Mœx´2Ï#eïÁ÷ËNàc®Eš«Isë7×#Íõb‹2Èi‹•|ÑòÄÕÒZ-õIKynõMÐ+­ì¥•z¶këŽÌEM[«Uûo£_Ú¼ØÛê—¶ÓÑ/íôK»'ÐÓíþÑ>'½Ëöç#:襞u°ÇŽbïØuêx:¢SE죓¾ê¬¯:ÛCg9ïlO]ä´Ë8ì§«žïêÛCïôÐ;=¬ÛS¯õ”—ž¯óKD¯ñý^ ø6¢·sß» æDo½ÐÇœè3˜]˜3O9{OÆþžò¼¯^é«WúŠ»¯^é'WýôJ?½ÒO¯ô×+ýõJùí¯W¨Õ€vèýïáü4GŠqà"œÙAæÕ û¤Ç9Kƒôø`ûܹ¬§†¨Ó½2D1¿†êñ¡#QÛ¡g"ž–Û§!î§Å=L? ÓãÖcßÃÅ5Ü;‡[o¸ZŒ·ö9BO§‘â©#åqäáˆQÎÈ(ye_£Ô`´xF›A£ÕgŒúŒQŸ1ê3Ƴg<{F}ž1ŸÆz6Ö:cíc¬gÏ>DWäëYsaœuÇé—qÎæ8ý=¾úo¼y7ÞÚ²Ò’iü3â¹ûÑÿÏÍÃ÷'ÞwOT¯‰úo’zN²þ$õœ¤žÏçÃLzþe¼òÃØ÷äõˆx¡â{afÑówJsœË)â™jþNÏT÷ÄÔ¯#¦åB¼ÓÜ-ÓôÛtý1]OOÏtçe†xfˆg†{c†x^Ï‹úëEñ¼(ž—ô×KfßKÏ ?3oÆ™é ÎtïÌrféYúc–þ˜¥?fËçl³b¶Í–ƒ9z~Ž?ÇÝ3G_ϱ¹æü\s~®Y³Ìû–½±ÜóåîÓåzf¹³·ÜlXQÏWx®ub¥;z¥9³Ò¾WzÇËÕ0;^vW¾’¡fÂ+fè*³q•Ù¸j*ŸE¬óêF˜[«ÿ±F_­i…:­Q§WÕéUçáÕ¸ÖúîZwõZwõ:wõ:9_§GÖýñçb8Óv¯¿æÞ­«p~׋y½Ú®WÛ j»A68sœ¹âÝ(ÞÖÜ¤Ž›Ì‘MÎÛ&wÎf¹ßìöÈDZ¿){åz¯~Ûë{ûœ‰}î¶}æÂ[fý[zëmsÿ¿‹Þug¼çÞzßlúP/}$¿ûÕàïî†O̰oõíwúæ€Yý½þþÁýø£>ûI^¾1âŸùõˆß̦ßÝG¿‹çwçì œt.ªÃA;¨¦‡ìý8¹[9¿‡äê°ótX_6“ë£ÃÖ?bžé†Þ<"GÌ¿£à7ÀQy;jÇÌØcúí˜<3;ŽÙËq1÷ï¸Wïã_Fœ§æÏ ½qÂ<é~;é;'}ç¤ûå”ßt§Ä~J~N™/§½ç´÷œVçÓzéŒ9ãœQ“3fëwÄYçâ¬øÎÊ×Y9Qâ8'–s~óžsîëóÞ{ÞþÏ»;Î;ãçíõ=õG=œ‘?Ôûï»`ž]P“ ~·^p®/èÑ‹f÷Eý|ѹ¿è,]t–.ÉÉ%sï’»ä޹lÿ—åþ²Þ¹l6_¶×+~#\ÑÃWÔçŠü^1®ê…«~Ç^õûæª=^u]³çkúõš^¿¦—®Œ4)@{fñ>#’tg1ŸDšf(GVñŸH3·RálàûH¯»‡Œc;#½>Í™Â[œŠô†‚t`6p)Ò,¥èÁ>ôÆë(ÏVóU¤7ÝF F°‘"½ù^2žŠô–œ´`*át¤·¢#søË‘ÞVšž,å³H³^O²†¯#½=+5É&~ŒôŽûhÄvr8Ò;sÑ’i¼Í™Hï*L'æòW"½ûz±ŒFší*2ˆWùo¤÷ÜN-F±™Ÿ"½÷~ó»8é}¹iÅtÞál¤÷¡3óø˜«‘úÿ1} 7Ëù<Ò³P‰Á¬å›HºƒÚŒf ?Gúðü‰‰ìæh¤ÙóК¼Ë¹Hs¥ óÙϵHs>JVðK¤¹ª!ϹÎGš[?äöŽ<·àïy†±ž‘æÍF}že[¤ùä?Ÿuòý-ÒüÙQûüöPÀú&ó×H vÃ{ +ù[k«·& tñ‘~ g_æˆLö•\ˆLŽºÌç§Èä,ÍL¾L®ŠLå—ÈäÎKf°?2yn¢ +cÙÇÉÈä-ÅÞá\dò•¥/ë9™ü…èÅZ¬Y O²˜¯"Sð>Z!–‚ÿŽL¡»iÆl>‹LáÜ´f_D¦H6š#Æ"E¦hêð {¹™båÉ.ÄT\LÅÅT\LÅÅTBL%ÄTBL%ÄTRL%Û²ÏKy^ªËùßÈ”~ˆvÌâÓÈ<’•ÆL䯑ñÓ4S¦ãø W"óh†°•‘)[€¬AÎËCW^æËÈ<~-˜Ëç‘)w'MQ—rŸD¦|Q|¾¼Ï—?™ +jY¡#ËðùŠ>_Ñç+ú|Å÷"S)ƒõ*Y¯ÒÇ‘©|#j^y2ïs)2U*0ŠÝœLÕbÈSUy­jÕ*1†7Ñ3ÕË1‚œL {ª¡65¦°‡ß"SÓ>jÖd0z­æ?ð®Zú¤–ý×Z‚Ö¾uòn¦öjôBÛ¨wÔÙÈ‘©{/ ÏNE¦^I¬UÏÞëý+2õo¦:j^ ¿F¦Avì¿ÁtÔ¨øŠ¡¡º7\Áÿú#_ +endstream +endobj +startxref +216076 +%%EOF diff --git a/public/PDF/testpdf.pdf b/public/PDF/testpdf.pdf new file mode 100644 index 0000000..3c52035 Binary files /dev/null and b/public/PDF/testpdf.pdf differ diff --git a/public/bootstrap/5/css/bootstrap.min.css b/public/bootstrap/5/css/bootstrap.min.css new file mode 100644 index 0000000..3993414 --- /dev/null +++ b/public/bootstrap/5/css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control-plaintext~label::after,.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.form-floating>.form-control:disabled~label::after,.form-floating>:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin:calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/public/bootstrap/5/js/bootstrap.bundle.min.js b/public/bootstrap/5/js/bootstrap.bundle.min.js new file mode 100644 index 0000000..04e9185 --- /dev/null +++ b/public/bootstrap/5/js/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!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}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..e69de29 diff --git a/public/dmxAppConnect/dmxAppConnect.js b/public/dmxAppConnect/dmxAppConnect.js new file mode 100644 index 0000000..015ebfc --- /dev/null +++ b/public/dmxAppConnect/dmxAppConnect.js @@ -0,0 +1,10 @@ +/*! + App Connect + Version: 2.0.16 + (c) 2024 Wappler.io + @build 2024-09-30 10:56:31 + */ +window.dmx={version:"2.0.16",versions:{},config:{mapping:{form:"form","button, input[type=button], input[type=submit], input[type=reset]":"button","input[type=radio]":"radio","input[type=checkbox]":"checkbox","input[type=file][multiple]":"input-file-multiple","input[type=file]":"input-file",input:"input",textarea:"textarea","select[multiple]":"select-multiple",select:"select",".checkbox-group":"checkbox-group",".radio-group":"radio-group"}},noop:()=>{},isset:e=>void 0!==e,array:e=>null!=e?Array.from(e):[],reIgnoreElement:/^(script|style)$/i,rePrefixed:/^dmx-/i,reExpression:/\{\{(.+?)\}\}/,reExpressionReplace:/\{\{(.+?)\}\}/g,reToggleAttribute:/^(checked|selected|disabled|required|hidden|async|autofocus|autoplay|default|defer|multiple|muted|novalidate|open|readonly|reversed|scoped)$/i,reDashAlpha:/-([a-z])/g,reUppercase:/[A-Z]/g,__components:Object.create(null),__attributes:{before:Object.create(null),mounted:Object.create(null)},__formatters:{boolean:Object.create(null),global:Object.create(null),string:Object.create(null),number:Object.create(null),object:Object.create(null),array:Object.create(null),any:Object.create(null)},__adapters:Object.create(null),__actions:Object.create(null),__startup:new Set},window.Element&&!("closest"in Element.prototype)&&(Element.prototype.closest=function(e){let t,s=(this.document||this.ownerDocument).querySelectorAll(e),n=this;do{for(t=s.length;--t>=0&&s.item(t)!==n;);}while(t<0&&(n=n.parentElement));return n}),window.NodeList&&!("forEach"in NodeList.prototype)&&(NodeList.prototype.forEach=Array.prototype.forEach),"function"!=typeof window.queueMicrotask&&(window.queueMicrotask=function(e){Promise.resolve().then(e).catch((e=>setTimeout((()=>{throw e}))))}),window.Node&&!("isConnected"in Node.prototype)&&Object.defineProperty(Node.prototype,"isConnected",{get:function(){return document.contains(this)}}),window.Element&&!("toggleAttribute"in Element.prototype)&&(Element.prototype.toggleAttribute=function(e,t){this.hasAttribute(e)?!0!==t&&this.removeAttribute(e):!1!==t&&this.setAttribute(e,"")}),function(){var e=Object.prototype.toString,t=Object.prototype.hasOwnProperty,s=/\w*$/,n=/^(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array$/,r=function(e,t){return e===t||e!=e&&t!=t},i=function(e,t){for(var s=e.length;s--;)if(r(e[s][0],t))return s;return-1},a=function(e,t){return("string"==(n=typeof(s=t))||"number"==n||"symbol"==n||"boolean"==n?"__proto__"!==s:null===s)?e["string"==typeof t?"string":"hash"]:e.map;var s,n},o=function(e){var t=-1,s=null==e?0:e.length;for(this.clear();++t-1},set:function(e,t){var s=this.__data__,n=i(s,e);return n<0?(++this.size,s.push([e,t])):s[n][1]=t,this}};var h=function(e){var t=-1,s=null==e?0:e.length;for(this.clear();++t{const s=function(){e.constructor&&e.constructor.apply(this,arguments)};return t&&t.prototype&&(s.prototype=Object.create(t.prototype)),Object.assign(s.prototype,e),s.prototype.constructor=s,s},dmx.ready=e=>{"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>{e()}),{once:!0}):e()},dmx.Config=e=>{Object.assign(dmx.config,e)},dmx.Component=(e,t)=>{if(t){const s=t.extends?dmx.Component(t.extends):dmx.BaseComponent;"function"!=typeof t.initialData&&(t.initialData=Object.assign({},s.prototype.initialData,t.initialData)),t.attributes=Object.assign({},s.prototype.attributes,t.attributes),t.methods=Object.assign({},s.prototype.methods,t.methods),t.events=Object.assign({},s.prototype.events,t.events),t.hasOwnProperty("constructor")||(t.constructor=function(e,t){s.call(this,e,t)}),t.type=e;const n=dmx.createClass(t,s);n.extends=t.extends,dmx.__components[e]=n}return dmx.__components[e]},dmx.Attribute=(e,t,s)=>{dmx.__attributes[t][e]=s},dmx.Formatter=(e,t,s)=>{dmx.__formatters[e][t]=s},dmx.Formatters=(e,t)=>{for(const s in t)dmx.Formatter(e,s,t[s])},dmx.Adapter=(e,t,s)=>(s&&(dmx.__adapters[e][t]=s),dmx.__adapters[e][t]),dmx.Action=(e,t)=>{dmx.__actions[e]=t},dmx.Actions=e=>{for(const t in e)dmx.Action(t,e[t])},dmx.Startup=e=>{dmx.__startup.add(e)},dmx.debounce=(e,t)=>{let s;return function(){const n=()=>{e.apply(this,arguments)};t?(clearTimeout(s),s=setTimeout(n,t)):(cancelAnimationFrame(s),s=requestAnimationFrame(n))}},dmx.throttle=(e,t)=>{let s,n=!1;return function(){if(s=Array.from(arguments),!n){const r=()=>{n=!1,s&&e.apply(this,s)};e.apply(this,s),s=void 0,n=!0,t?setTimeout(db,t):requestAnimationFrame(r)}}},dmx.keyCodes={bs:8,tab:9,enter:13,esc:27,space:32,left:37,up:38,right:39,down:40,delete:46,backspace:8,pause:19,capslock:20,escape:27,pageup:33,pagedown:34,end:35,home:36,arrowleft:37,arrowup:38,arrowright:39,arrowdown:40,insert:45,numlock:144,scrolllock:145,semicolon:186,equal:187,comma:188,minus:189,period:190,slash:191,backquote:192,bracketleft:219,backslash:220,bracketright:221,quote:222,numpad0:96,numpad1:97,numpad2:98,numpad3:99,numpad4:100,numpad5:101,numpad6:102,numpad7:103,numpad8:104,numpad9:105,numpadmultiply:106,numpadadd:107,numpadsubstract:109,numpaddivide:111,f1:112,f2:113,f3:114,f4:115,f5:116,f6:117,f7:118,f8:119,f9:120,f10:121,f11:122,f12:123,digit0:48,digit1:49,digit2:50,digit3:51,digit4:52,digit5:53,digit6:54,digit7:55,digit8:56,digit9:57,keya:[65,97],keyb:[66,98],keyc:[67,99],keyd:[68,100],keye:[69,101],keyf:[70,102],keyg:[71,103],keyh:[72,104],keyi:[73,105],keyj:[74,106],keyk:[75,107],keyl:[76,108],keym:[77,109],keyn:[78,110],keyo:[79,111],keyp:[80,112],keyq:[81,113],keyr:[82,114],keys:[83,115],keyt:[84,116],keyu:[85,117],keyv:[86,118],keyw:[87,119],keyx:[88,120],keyy:[89,121],keyz:[90,122]},dmx.eventListener=function(e,t,s,n){let r,i;const a=function(e){if((!n.self||e.target===e.currentTarget)&&(!n.ctrl||e.ctrlKey)&&(!n.alt||e.altKey)&&(!n.shift||e.shiftKey)&&(!n.meta||e.metaKey)&&(!(e.originalEvent||e).nsp||Object.keys(n).includes((e.originalEvent||e).nsp))){if((e.originalEvent||e)instanceof MouseEvent){if(null!=n.button&&e.button!=(parseInt(n.button,10)||0))return;if(n.button0&&0!=e.button)return;if(n.button1&&1!=e.button)return;if(n.button2&&2!=e.button)return;if(n.button3&&3!=e.button)return;if(n.button4&&4!=e.button)return}if((e.originalEvent||e)instanceof KeyboardEvent){var t=[];Object.keys(n).forEach((function(e){var s=parseInt(e,10);s?t.push(s):dmx.keyCodes[e]&&t.push(dmx.keyCodes[e])}));for(var a=0;a({identifier:e.identifier,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY,pageX:e.pageX,pageY:e.pageY});e.$data.altKey=e.altKey,e.$data.ctrlKey=e.ctrlKey,e.$data.metaKey=e.metaKey,e.$data.shiftKey=e.shiftKey,e.$data.touches=Array.from(e.touches).map(t),e.$data.changedTouches=Array.from(e.changedTouches).map(t),e.$data.targetTouches=Array.from(e.targetTouches).map(t),e.$data.rotation=e.rotation,e.$data.scale=e.scale}if(e instanceof KeyboardEvent&&(e.$data.altKey=e.altKey,e.$data.ctrlKey=e.ctrlKey,e.$data.metaKey=e.metaKey,e.$data.shiftKey=e.shiftKey,e.$data.location=e.location,e.$data.repeat=e.repeat,e.$data.code=e.code,e.$data.key=e.key),n.debounce)clearTimeout(r),r=setTimeout((()=>{s.apply(this,arguments)}),parseInt(n.debounce,10)||0);else{if(!n.throttle)return s.apply(this,arguments);i||(i=!0,s.apply(this,arguments),setTimeout((()=>{i=!1}),parseInt(n.throttle,10)||0))}}};return n=n||{},window.Dom7&&1===e.nodeType?(Dom7(e)[n.once?"once":"on"](t.replace(/-/g,"."),a,!!n.capture),()=>Dom7(e).off(t.replace(/-/g,"."),a,!!n.capture)):window.jQuery&&!n.capture?(jQuery(e)[n.once?"one":"on"](t.replace(/-/g,"."),a),()=>jQuery(e).off(t.replace(/-/g,"."),a)):(e.addEventListener(t.replace(/-/g,"."),a,{capture:!!n.capture,once:!!n.once,passive:!!n.passive}),()=>e.removeEventListener(t.replace(/-/g,"."),a,!!n.capture))},dmx.fileUtils={fileReader:(e,t)=>new Promise(((s,n)=>{const r=new FileReader;r.onload=()=>s(r.result),r.onerror=()=>n(r.error),r[t](e)})),blobToArrayBuffer:function(e){return dmx.fileUtils.fileReader(e,"readAsArrayBuffer")},blobToBinaryString:function(e){return dmx.fileUtils.fileReader(e,"readAsBinaryString")},blobToDataURL:function(e){return dmx.fileUtils.fileReader(e,"readAsDataURL")},blobToBase64String:function(e){return dmx.fileUtils.fileReader(e,"readAsDataURL").then((e=>e.substring(e.indexOf(",")+1)))},arrayBufferToBlob:function(e,t){return Promise.resolve(new Blob([e],{type:t}))},binaryStringToBlob:function(e,t){const s=Uint8Array.from(e,(e=>e.charCodeAt(0)));return Promise.resolve(new Blob([s],{type:t}))},dataURLToBlob:function(e){const{data:t,type:s}=dmx.fileUtils.parseDataURL(e);return dmx.fileUtils.base64StringToBlob(t,s)},base64StringToBlob:function(e,t){const s=window.atob(e);return dmx.fileUtils.binaryStringToBlob(s,t)},parseDataURL:function(e){const t=e.match(/^data:(.*?)(;base64)?,(.*)$/);return{mediaType:t[1],base64:!!t[2],data:t[3],type:t[1].split(";")[0]}},parseMediaType:function(e){const t=e.match(/^([^/]+)\/([^+;]+)(?:\+([^;]+))?(?:;(.*))?$/);return{type:t[1],subtype:t[2],suffix:t[3],parameters:t[4]?t[4].split(";").reduce(((e,t)=>{const[s,n]=t.split("=");return e[s]=n,e}),{}):{}}}};{const e=function(e){const t=history[e];return function(){const s=t.apply(this,arguments),n=new Event(e.toLowerCase());return n.arguments=arguments,window.dispatchEvent(n),s}};history.pushState=e("pushState"),history.replaceState=e("replaceState")}window.onpopstate=function(e){e.state&&e.state.title&&(document.title=e.state.title)},document.documentElement.style.visibility="hidden",dmx.ready((()=>{Promise.all(dmx.__startup).then((()=>{if(dmx.app)throw Error("App already running!");history.replaceState({title:document.title},"");const e=document.querySelector(':root[dmx-app], [dmx-app], :root[is="dmx-app"], [is="dmx-app"]');if(!e)throw Error("App root not found!");const t=dmx.Component("app");dmx.app=new t(e,dmx.global),document.documentElement.style.visibility=""})).catch((e=>{console.error(e),document.documentElement.style.visibility=""}))})),dmx.extend=function(){var e={},t=!1,s=0,n=arguments.length;"[object Boolean]"===Object.prototype.toString.call(arguments[0])&&(t=arguments[0],s++);for(var r=function(s){for(var n in s)"__proto__"!=n&&Object.prototype.hasOwnProperty.call(s,n)&&(t&&"[object Object]"===Object.prototype.toString.call(s[n])?e[n]=dmx.extend(!0,e[n],s[n]):null!=s[n]&&(e[n]=s[n]))};se.dirty=!0)),e.checkValidity()},dmx.validateReset=function(e){},(()=>{const e=[];window.addEventListener("message",(t=>{if(t.source===window&&"dmxNextTick"===t.data&&e.length)for(t.stopPropagation();e.length;){const t=e.shift();t.fn.call(t.context)}}),!0),dmx.nextTick=(t,s)=>{e.push({fn:t,context:s}),window.postMessage("dmxNextTick","*")}})(),dmx.requestUpdate=function(){console.warn("dmx.requestUpdate is deprecated.")},"app:"==document.location.protocol&&dmx.Startup(new Promise((e=>document.addEventListener("deviceready",e)))),(()=>{ +/*! (c) Andrea Giammarchi */ +const{is:e}=Object;let t;dmx.batch=e=>{const s=t;t=s||[];try{if(e(),!s)for(const{value:e}of t);}finally{t=s}};class s{constructor(e){this._=e}toJSON(){return this.value}toString(){return String(this.value)}valueOf(){return this.value}}let n;dmx.Signal=s;class r extends s{s;constructor(e,t,s,n){super(e),this.f=n,this.$=!0,this.r=new Set,this.s=new p(t,s)}peek(){return this.s.peek()}get value(){if(this.$){const e=n;n=this;try{this.s.value=this._(this.s._)}finally{this.$=!1,n=e}}return this.s.value}}const i={async:!1,equals:!0};let a;dmx.computed=(e,t,s=i)=>new r(e,t,s,!1);const o=[],d=()=>{},h=({s:e})=>{"function"==typeof e._&&(e._=e._())};class l extends r{constructor(e,t,s){super(e,t,s,!0),this.e=o}run(){return this.$=!0,this.value,this}stop(){this._=d;for(const e of this.r)e.c.delete(this);this.r.clear(),this.s.c.clear()}}dmx.FX=l;class c extends l{constructor(e,t,s){super(e,t,s),this.i=0,this.a=!!s.async,this.m=!0,this.e=[]}get value(){this.a?this.async():this.sync()}async(){this.m&&(this.m=!1,queueMicrotask((()=>{this.m=!0,this.sync()})))}sync(){const e=a;(a=this).i=0,h(this),super.value,a=e}stop(){super.stop(),h(this);for(const e of this.e.splice(0))e.stop()}}dmx.Effect=c;dmx.effect=(e,t,s=i)=>{let n;if(a){const{i:r,e:i}=a,o=r===i.length;(o||i[r]._!==e)&&(o||i[r].stop(),i[r]=new c(e,t,s).run()),n=i[r],a.i++}else n=new c(e,t,s).run();return()=>{n.stop()}};const u=()=>!1;class p extends s{constructor(t,{equals:s}){super(t),this.c=new Set,this.s=!0===s?e:s||u}peek(){return this._}get value(){return n&&(this.c.add(n),n.r.add(this)),this._}set value(e){const s=this._;if(!this.s(this._=e,s)&&this.c.size){const e=[],s=[this];for(const t of s)for(const n of t.c)if(!n.$&&n.r.has(t))if(n.r.clear(),n.$=!0,n.f){e.push(n);const t=[n];for(const e of t)for(const s of e.e)s.r.clear(),s.$=!0,t.push(s)}else s.push(n.s);for(const s of e)t?t.push(s):s.value}}}dmx.signal=(e,t=i)=>new p(e,t)})(),dmx.signalProxy=function(e={}){const t=new Map,s=(e,t)=>dmx.equal(e,t);return new Proxy(e,{has:(e,t)=>!0,get(e,n,r){const i=Reflect.get(e,n,r);return"function"==typeof i||"string"!=typeof n||n.startsWith("_")?i:(t.has(n)||t.set(n,dmx.signal(i,{equals:s})),t.get(n).value)},set(e,s,n,r){const i=Reflect.set(e,s,n,r);return i&&t.has(s)&&(t.get(s).value=n),i},deleteProperty(e,s){const n=Reflect.deleteProperty(e,s);return n&&t.has(s)&&(t.get(s).value=void 0),n}})},(()=>{class e{constructor(e={},t=null){"object"!=typeof e&&(e={$value:e}),this.data=dmx.signalProxy(),Object.assign(this.data,e),this.parent=t,this.seed=Math.random()}get(e){return void 0!==this.data[e]?this.data[e]:this.parent?"parent"==e?this.parent.data:this.parent.get(e):void 0}set(e,t){"object"==typeof e?dmx.batch((()=>{for(var t in e)e.hasOwnProperty(t)&&this.set(t,e[t])})):this.data[e]=t}del(e){delete this.data[e]}}dmx.global=new e,dmx.DataScope=function(t,s){return new e(t,s||dmx.global)}})(),function(){var e=function(t){if(!(this instanceof e))return new e(t);if(t instanceof e)return t;if(!t)return this;var s=t.length;if(t.nodeType)this[0]=t,this.length=1;else{if("string"==typeof t)return e(document.querySelectorAll(t));if(s)for(var n=0;n0?a[e.substr(0,s)]=e.substr(s+1):a[e]=!0}}));var o=r.indexOf(":");o>0&&(i=r.substr(o+1),r=r.substr(0,o)),t.push({name:r,fullName:n.name,value:n.value,argument:i,modifiers:a})}}return t},remove:function(e){Array.isArray(e)?e.forEach((function(e){dmx.dom.remove(e)})):e.remove()},replace:function(e,t){e.parentNode&&e.parentNode.replaceChild(t,e)}}}(),dmx._CACHE=new Map,dmx._OPERATORS=new Map([["{","L_CURLY"],["}","R_CURLY"],["[","L_BRACKET"],["]","R_BRACKET"],["(","L_PAREN"],[")","R_PAREN"],[".","PERIOD"],[",","COMMA"],[";","SEMI"],[":","COLON"],["?","QUESTION"],["-","ADDICTIVE"],["+","ADDICTIVE"],["*","MULTIPLICATIVE"],["/","MULTIPLICATIVE"],["%","MULTIPLICATIVE"],["===","EQUALITY"],["!==","EQUALITY"],["==","EQUALITY"],["!=","EQUALITY"],["<","RELATIONAL"],[">","RELATIONAL"],["<=","RELATIONAL"],[">=","RELATIONAL"],["in","RELATIONAL"],["&&","LOGICAL_AND"],["||","LOGICAL_OR"],["!","LOGICAL_NOT"],["&","BITWISE_AND"],["|","BITWISE_OR"],["^","BITWISE_XOR"],["~","BITWISE_NOT"],["<<","BITWISE_SHIFT"],[">>","BITWISE_SHIFT"],[">>>","BITWISE_SHIFT"]]),dmx._ESCAPE_CHARS=new Map([["n","\n"],["r","\r"],["t","\t"],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["'","'"],["`","`"],['"','"']]),dmx._EXPRESSIONS=new Map([["**",(e,t)=>Math.pow(e(),t())],["??",(e,t)=>null==(e=e())?t():e],["in",(e,t)=>e()in t()],["?",(e,t,s)=>e()?t():s()],["+",(e,t)=>(e=e(),t=t(),null==e?t:null==t?e:e+t)],["-",(e,t)=>e()-t()],["*",(e,t)=>e()*t()],["/",(e,t)=>e()/t()],["%",(e,t)=>e()%t()],["===",(e,t)=>e()===t()],["!==",(e,t)=>e()!==t()],["==",(e,t)=>e()==t()],["!=",(e,t)=>e()!=t()],["<",(e,t)=>e()",(e,t)=>e()>t()],["<=",(e,t)=>e()<=t()],[">=",(e,t)=>e()>=t()],["&&",(e,t)=>e()&&t()],["||",(e,t)=>e()||t()],["&",(e,t)=>e()&t()],["|",(e,t)=>e()|t()],["^",(e,t)=>e()^t()],["<<",(e,t)=>e()<>",(e,t)=>e()>>t()],[">>>",(e,t)=>e()>>>t()],["~",e=>~e()],["!",e=>!e()]]),dmx._RESERVED=new Map([["this",e=>()=>e.data],["true",()=>()=>!0],["false",()=>()=>!1],["null",()=>()=>null],["undefined",()=>()=>{}],["_",()=>()=>({__dmxScope__:!0})]]),dmx._SUPPORTED_TYPES=new Map([["Boolean","boolean"],["Null","null"],["Undefined","undefined"],["Number","number"],["BigInt","number"],["Decimal","number"],["String","string"],["Date","date"],["RegExp","regexp"],["Blob","blob"],["File","file"],["FileList","filelist"],["ArrayBuffer","arraybuffer"],["ImageBitmap","imagebitmap"],["ImageData","imagedata"],["Array","array"],["Object","object"],["Map","map"],["Set","set"],["DataView","array"],["Int8Array","array"],["Uint8Array","array"],["Uint8ClampedArray","array"],["Int16Array","array"],["Uint16Array","array"],["Int32Array","array"],["Uint32Array","array"],["Float32Array","array"],["Float64Array","array"],["BigInt64Array","array"],["BigUint64Array","array"]]),dmx.getType=function(e){return dmx._SUPPORTED_TYPES.get(Object.prototype.toString.call(e).slice(8,-1))},dmx.lexer=function(e){if(dmx._CACHE.has(e))return dmx._CACHE.get(e);let t,s,n,r,i,a,o=[],d=0,h=!0;for(;d1?e.slice(d,d+t):e[d]}function c(t=1){return d+t="0"&&e<="9"}function f(e){return e>="a"&&e<="z"||e>="A"&&e<="Z"||"_"===e||"$"===e}function g(e){return f(e)||m(e)}function y(e){return" "==e||"\r"==e||"\t"==e||"\n"==e||"\v"==e||" "==e}function _(e){return"-"==e||"+"==e||m(e)}function v(t){let s=!1,n="";for(d++;d{const n=dmx.parse(s,t);return null==n?"":n}));e=e.slice(2,-2)}if(!e)return;let s,n,r;try{s=Array.from(dmx.lexer(e)),r=function(){const e=[];for(;;)if(s.length>0&&!(a("R_PAREN")||a("R_BRACKET")||a("R_CURLY")||a("COMMA")||a("SEMI"))&&e.push(l()),!o("COMMA")&&!o("SEMI"))return(1==e.length?e[0]:t)();function t(){let t;for(let s=0;s0){const t=s[0];if(!e||t.name==e)return t}return!1}function o(e){const t=a(e);return!!t&&(s.shift(),t)}function d(t){if(!o(t))throw new Error(`Expected ${t} at index ${s[0].index} in expression: ${e}`)}function h(e){const s=Array.prototype.slice.call(arguments,1);return()=>dmx._EXPRESSIONS.has(e)?dmx._EXPRESSIONS.get(e).apply(t,s):e}function l(){return function(){const e=function(){let e=c();for(;o("LOGICAL_OR");){e=h("||",e,c())}return e}();if(o("QUESTION")){const t=l();d("COLON");return h("?",e,t,l())}return e}()}function c(){let e=u();for(;o("LOGICAL_AND");){e=h("&&",e,u())}return e}function u(){let e=p();for(;o("BITWISE_OR");){e=h("|",e,p())}return e}function p(){let e=m();for(;o("BITWISE_XOR");){e=h("^",e,m())}return e}function m(){let e=f();for(;o("BITWISE_AND");){e=h("&",e,f())}return e}function f(){let e,t=g();if(e=o("EQUALITY")){const s=f();t=h(e.value,t,s)}return t}function g(){let e,t=y();if(e=o("RELATIONAL")){const s=g();t=h(e.value,t,s)}return t}function y(){let e,t=function(){let e,t=_();for(;e=o("ADDICTIVE");){const s=_();t=h(e.value,t,s)}return t}();if(e=o("BITWISE_SHIFT")){const s=y();t=h(e.value,t,s)}return t}function _(){let e,t=v();for(;e=o("MULTIPLICATIVE");){const s=v();t=h(e.value,t,s)}return t}function v(){let e;return(e=o("ADDICTIVE"))?"+"==e.value?x():h(e.value,(()=>0),x()):(e=o("LOGICAL_NOT"))||(e=o("BITWISE_NOT"))?h(e.value,v()):x()}function x(){let s,r;if(o("L_PAREN"))s=l(),d("R_PAREN");else if(o("L_CURLY")){const e={};if("R_CURLY"!=i().name)do{const t=o().value;d("COLON"),e[t]=l()()}while(o("COMMA"));s=h(e),d("R_CURLY")}else if(o("L_BRACKET")){const e=[];if("R_BRACKET"!=i().name)do{e.push(l()())}while(o("COMMA"));s=h(e),d("R_BRACKET")}else if(o("PERIOD"))s=a()?E(h(t.data)):h(t.data);else{const n=o();if(!1===n)throw new Error(`Unexpected end of expression: ${e}`);s="IDENT"==n.name?dmx._RESERVED.has(n.value)?dmx._RESERVED.get(n.value)(t):()=>t.get(n.value):"METHOD"==n.name?h(dmx.__formatters.global[n.value]||(()=>{console.warn(`Method "${n.value}" not found in expression: ${e}`)})):()=>n.value}for(;r=o("L_PAREN")||o("L_BRACKET")||o("PERIOD");)if("("==r.value)s=b(s,n);else if("["==r.value)n=s,s=w(s);else{if("."!=r.value)throw new Error(`Unexpected token "${r.value}" at index ${r.index} in expression: ${e}`);n=s,s=E(s)}return n=null,s}function b(s,n){const r=[];if("R_PAREN"!=i().name)do{r.push(l())}while(o("COMMA"));return d("R_PAREN"),()=>{let i=[];n&&i.push(n());for(let e of r)i.push(e());try{return(s()||dmx.noop).apply(t,i)}catch(t){return void console.warn(`Error calling method ${s().name} in expression: ${e}`,t)}}}function w(e){const s=l();return d("R_BRACKET"),()=>{const n=e(),r=s();if("object"==typeof n&&null!=n)return n.__dmxScope__?t.get(r):"map"==dmx.getType(n)?n.get(r):n[r]}}function E(s){const n=o();return()=>{const r=s(),i=dmx.getType(r);if("METHOD"==n.name){const t="__"+n.value;return"map"==i&&"function"==typeof r.get(t)?r.get(t).bind(r):"object"==i&&"function"==typeof r[t]?r[t]:dmx.__formatters[i]&&dmx.__formatters[i][n.value]?dmx.__formatters[i][n.value]:dmx.__formatters.any&&dmx.__formatters.any[n.value]?dmx.__formatters.any[n.value]:()=>{null!=r&&console.warn(`Method "${n.value}" not found in expression: ${e}`)}}return r&&r.__dmxScope__?t.get(n.value):"map"==i?r.get(n.value):r&&"object"==typeof r&&n.value in r?r[n.value]:void 0}}},dmx.BaseComponent=dmx.createClass({constructor:function(e,t){this.$node=e,this.parent=t,this.children=[],this.listeners={},this.__disposables=[],this.__childDisposables=[],this.updatedProps=new Map,this.updateRequested=!1,this.isInitialized=!1,this.isDestroyed=!1,this.props=new Proxy({},{set:(e,t,s,n)=>{const r=Reflect.get(e,t,n),i=Reflect.set(e,t,s,n);return i&&this.isInitialized&&(this.attributes[t]&&this.attributes[t].alwaysUpdate||!dmx.equal(r,s))&&this.requestUpdate(t,r),i}}),this.data=dmx.signalProxy(),this.seed=Math.random(),this.name=e.getAttribute("id")||e.getAttribute("name")||this.type&&this.type.toLowerCase().replace(/^dmx-/,"")||"",this.name=this.name.replace(/[^\w]/g,"");try{this.$initialData(),this.$parseAttributes(e),this.init(e),!1!==this.render&&this.render(e),this.$node&&(this.$customAttributes("mounted",this.$node),this.$node.dmxComponent=this,this.$node.dmxRendered=!0),this.isInitialized=!0}catch(e){console.error(e)}},tag:null,initialData:{},attributes:{},methods:{},events:{destroy:Event},render:function(e){this.$node&&this.$parse()},parse:function(e){return dmx.parse(e,this)},find:function(e){if(this.name==e)return this;for(var t=0;t{if(t==this.$node){if("bind"==s.name&&this.attributes[(n=s.argument,n.replace(/-./g,(e=>e[1].toUpperCase())))])return;if("on"==s.name&&this.events[s.argument])return}var n;if(dmx.__attributes[e][s.name]){this.__inChild=t!=this.$node;const n=dmx.__attributes[e][s.name].call(this,t,s);n&&this[this.__inChild?"__childDisposables":"__disposables"].push(n)}})),this.__inChild=null},$parseTextNode(e){if(3===e.nodeType&&dmx.reExpression.test(e.nodeValue)){const t=e.nodeValue.replace(dmx.reExpressionReplace,((e,t)=>`##split##${t}##split##`)).split("##split##"),s=document.createDocumentFragment();t.forEach(((e,t)=>{const n=document.createTextNode(e);s.appendChild(n),t%2&&this.$watch(e,(e=>{n.nodeValue=e}))})),e.parentNode.replaceChild(s,e)}},$parse:function(e){if(e=e||this.$node)return 3===e.nodeType?this.$parseTextNode(e):void(1===e.nodeType&&(dmx.config.mapping&&Object.keys(dmx.config.mapping).forEach((t=>{dmx.array(e.querySelectorAll(t)).forEach((e=>{e.hasAttribute("is")||e.setAttribute("is","dmx-"+dmx.config.mapping[t])}))})),dmx.dom.walk(e,(function(e){if(e!=this.$node){if(1===e.nodeType){var t=e.tagName.toLowerCase(),s=dmx.dom.getAttributes(e);if(e.hasAttribute("is")&&(t=e.getAttribute("is")),dmx.reIgnoreElement.test(t))return!1;if(this.$customAttributes("before",e,s),-1!==s.findIndex((e=>"repeat"===e.name)))return!1;if(dmx.rePrefixed.test(t))return(t=t.replace(/^dmx-/i,""))in dmx.__components?(e.isComponent=!0,e.dmxRendered?window.__WAPPLER__&&e.dmxComponent&&e.dmxComponent.$parse&&(dmx.reIgnoreElement.test(e.tagName)||e.dmxComponent.$parse()):this.$createChild(t,e),!1):void console.warn("Unknown component found! "+t);this.$customAttributes("mounted",e,s)}3===e.nodeType&&this.$parseTextNode(e)}}),this)))},$update:function(e){console.warn("Component.$update is deprecated.")},$parseAttributes:function(e){for(const t in this.attributes){const s=this.attributes[t],n=t.replace(/[A-Z]/g,(e=>"-"+e.toLowerCase()));let r=dmx.clone(s.default);if(e.hasAttribute(n)){if(s.type===Boolean)r="false"!==e.getAttribute(n);else{if(r=e.getAttribute(n),s.type===Number&&r&&isFinite(Number(r))&&(r=Number(r)),s.type===Object||s.type===Array)try{r=JSON.parse(r)}catch(e){console.warn("Invalid attribute value, expected a JSON string got "+r)}s.enum&&!s.enum.includes(r)&&(r=dmx.clone(s.default)),s.validate&&!s.validate(r)&&(r=dmx.clone(s.default))}this.props[t]=r}if(e.hasAttribute("dmx-bind:"+n)){const r=e.getAttribute("dmx-bind:"+n);this.$watch(r,(e=>{void 0===e?e=dmx.clone(s.default):s.type===Boolean?e=!!e:(null!=e&&(s.type===Number&&("string"==typeof e?e=e&&isFinite(Number(e))?Number(e):dmx.clone(s.default):"number"==typeof e&&isFinite(Number(e))||(e=dmx.clone(s.default))),s.type===String&&(e=String(e)),s.type===Object&&"object"!=typeof e&&(e=dmx.clone(s.default)),s.type===Array&&(e=Array.from(e))),s.enum&&!s.enum.includes(e)&&(e=dmx.clone(s.default)),s.validate&&!s.validate(e)&&(e=dmx.clone(s.default))),this.props[t]=e}))}else this.props[t]=r}for(const t in this.events)e.hasAttribute("on"+t)&&this.__disposables.push(dmx.eventListener(this,t,Function("event",e.getAttribute("on"+t)),{}));dmx.dom.getAttributes(e).forEach((e=>{"on"==e.name&&this.events[e.argument]&&this.__disposables.push(dmx.eventListener(this,e.argument,(t=>(t.originalEvent&&(t=t.originalEvent),dmx.parse(e.value,dmx.DataScope({$event:t.$data,$originalEvent:t},this)))),e.modifiers))}))},requestUpdate:function(e,t){this.performUpdate&&(this.updatedProps.has(e)||this.updatedProps.set(e,t),this.updateRequested||dmx.nextTick((()=>{this.isDestroyed||(this.updateRequested=!1,this.performUpdate(this.updatedProps),this.updatedProps.clear())})),this.updateRequested=!0)},$initialData:function(){Object.assign(this.data,{$type:this.type},"function"==typeof this.initialData?this.initialData():this.initialData),Object.keys(this.methods).forEach((function(e){var t=this;this.data["__"+e]=function(){return t.methods[e].apply(t,Array.prototype.slice.call(arguments,1))}}),this)},$addBinding:function(e,t){this.$watch(e,t)},$watch:function(e,t){const s=this.__inChild?"__childDisposables":"__disposables";this[s]||(this[s]=[]);let n=!0;this[s].push(dmx.effect((()=>{if(n)t.call(this,this.parse(e)),n=!1;else{const s=this.parse(e);queueMicrotask((()=>t.call(this,s)))}})))},$destroy:function(){this.dispatchEvent("destroy"),this.beforeDestroy(),this.destroy(),this.isDestroyed=!0,this.parent&&this.parent.$removeChild&&this.parent.$removeChild(this),this.$destroyChildren(),this.__disposables.forEach((e=>e())),this.__disposables=[],this.$node&&(this.$node.dmxComponent=null,this.$node=null),this.parent=null,this.data={},this.destroyed()},$destroyChildren:function(){Array.from(this.children).forEach((e=>{e.$destroy()})),this.children=[],this.__childDisposables.forEach((e=>e())),this.__childDisposables=[]},get:function(e,t){return void 0!==this.data[e]?this.data[e]:this.parent&&!0!==t?"parent"==e?this.parent.data:this.parent.get(e):void 0},add:function(e,t){this.data[e]?Array.isArray(this.data[e])?this.data[e].push(t):this.data[e]=[this.data[e],t]:this.set(e,t)},set:function(e,t){"object"==typeof e?dmx.batch((()=>{for(var t in e)e.hasOwnProperty(t)&&this.set(t,e[t])})):this.data[e]=t},del:function(e){delete this.data[e]}}),function(){dmx.pathToRegexp=d,dmx.pathToRegexp.parse=s,dmx.pathToRegexp.compile=function(e,t){return n(s(e,t))},dmx.pathToRegexp.tokensToFunction=n,dmx.pathToRegexp.tokensToRegExp=o;var e="/",t=new RegExp(["(\\\\.)","(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?"].join("|"),"g");function s(s,n){for(var a,o=[],d=0,h=0,l="",c=n&&n.delimiter||e,u=n&&n.whitelist||void 0,p=!1;null!==(a=t.exec(s));){var m=a[0],f=a[1],g=a.index;if(l+=s.slice(h,g),h=g+m.length,f)l+=f[1],p=!0;else{var y="",_=a[2],v=a[3],x=a[4],b=a[5];if(!p&&l.length){var w=l.length-1,E=l[w];(!u||u.indexOf(E)>-1)&&(y=E,l=l.slice(0,w))}l&&(o.push(l),l="",p=!1);var $="+"===b||"*"===b,k="?"===b||"*"===b,A=v||x,C=y||c;o.push({name:_||d++,prefix:y,delimiter:C,optional:k,repeat:$,pattern:A?i(A):"[^"+r(C===c?C:C+c)+"]+?"})}}return(l||h0&&"\n"!==t[n];n--,r++);for(;n>0;n--)"\n"===t[n]&&i++;throw new Error(e+" at line "+i+","+r+" >>>"+t.substr(s-r,20)+" ...")}function o(){return n=t.charAt(s),s++,n}function d(e){return t.charAt(s+e)}function h(e){for(var t="",s=n;o();){if(n===s)return o(),e&&"'"===s&&"'"===n&&0===t.length?(o(),l()):t;if("\\"===n)if(o(),"u"===n){for(var i=0,d=0;d<4;d++){o();var h,c=n.charCodeAt(0);n>="0"&&n<="9"?h=c-48:n>="a"&&n<="f"?h=c-97+10:n>="A"&&n<="F"?h=c-65+10:a("Bad \\u char "+n),i=16*i+h}t+=String.fromCharCode(i)}else{if("string"!=typeof r[n])break;t+=r[n]}else"\n"===n||"\r"===n?a("Bad string containing newline"):t+=n}a("Bad string")}function l(){for(var e="",t=0,s=0;;){var r=d(-s-5);if(!r||"\n"===r)break;s++}function i(){for(var e=s;n&&n<=" "&&"\n"!==n&&e-- >0;)o()}for(;n&&n<=" "&&"\n"!==n;)o();for("\n"===n&&(o(),i());;){if(n){if("'"===n){if(t++,o(),3===t)return"\n"===e.slice(-1)&&(e=e.slice(0,-1)),e;continue}for(;t>0;)e+="'",t--}else a("Bad multiline string");"\n"===n?(e+="\n",o(),i()):("\r"!==n&&(e+=n),o())}}function c(){if('"'===n||"'"===n)return h(!1);for(var e="",t=s,r=-1;;){if(":"===n)return e?r>=0&&r!==e.length&&(s=t+r,a("Found whitespace in your key name (use quotes to include)")):a("Found ':' but no key name (for an empty key name use quotes)"),e;n<=" "?n?r<0&&(r=e.length):a("Found EOF while looking for a key name (check your syntax)"):i(n)?a("Found '"+n+"' where a key name was expected (check your syntax or use quotes if the key name includes {}[],: or whitespace)"):e+=n,o()}}function u(){for(;n;){for(;n&&n<=" ";)o();if("#"===n||"/"===n&&"/"===d(0))for(;n&&"\n"!==n;)o();else{if("/"!==n||"*"!==d(0))break;for(o(),o();n&&("*"!==n||"/"!==d(0));)o();n&&(o(),o())}}}function p(e,t){var s,n,r="",i=0,a=!0,o=0;function d(){return n=e.charAt(o),o++,n}for(d(),"-"===n&&(r="-",d());n>="0"&&n<="9";)a&&("0"==n?i++:a=!1),r+=n,d();if(a&&i--,"."===n)for(r+=".";d()&&n>="0"&&n<="9";)r+=n;if("e"===n||"E"===n)for(r+=n,d(),"-"!==n&&"+"!==n||(r+=n,d());n>="0"&&n<="9";)r+=n,d();for(;n&&n<=" ";)d();return t&&(","!==n&&"}"!==n&&"]"!==n&&"#"!==n&&("/"!==n||"/"!==e[o]&&"*"!==e[o])||(n=0)),s=+r,n||i||!isFinite(s)?void 0:s}function m(e){function t(e,s){var n,r,i,a;switch(typeof e){case"string":e.indexOf(s)>=0&&(a=e);break;case"object":if("[object Array]"===Object.prototype.toString.apply(e))for(n=0,i=e.length;n "+n+"\n (unquoted strings contain everything up to the next line!)":""}return s("}")||s("]")}function f(){var e=[];try{if(o(),u(),"]"===n)return o(),e;for(;n;){if(e.push(y()),u(),","===n&&(o(),u()),"]"===n)return o(),e;u()}a("End of input while parsing an array (missing ']')")}catch(t){throw t.hint=t.hint||m(e),t}}function g(e){var t="",s={};try{if(e||o(),u(),"}"===n&&!e)return o(),s;for(;n;){if(t=c(),u(),":"!==n&&a("Expected ':' instead of '"+n+"'"),o(),s[t]=y(),u(),","===n&&(o(),u()),"}"===n&&!e)return o(),s;u()}if(e)return s;a("End of input while parsing an object (missing '}')")}catch(e){throw e.hint=e.hint||m(s),e}}function y(){switch(u(),n){case"{":return g();case"[":return f();case"'":case'"':return h(!0);default:return function(){var e=n;for(i(n)&&a("Found a punctuator character '"+n+"' when expecting a quoteless string (check your syntax)");;){o();var t="\r"===n||"\n"===n||""===n;if(t||","===n||"}"===n||"]"===n||"#"===n||"/"===n&&("/"===d(0)||"*"===d(0))){var s=e[0];switch(s){case"f":if("false"===e.trim())return!1;break;case"n":if("null"===e.trim())return null;break;case"t":if("true"===e.trim())return!0;break;default:if("-"===s||s>="0"&&s<="9"){var r=p(e);if(void 0!==r)return r}}if(t)return e.trim()}e+=n}}()}}function _(e){return u(),n&&a("Syntax error, found trailing characters"),e}if("string"!=typeof e)throw new Error("source is not a string");return t=e,s=0,n=" ",function(){switch(u(),n){case"{":return _(g());case"[":return _(f());default:return _(y())}}()}),dmx.Flow=dmx.createClass({constructor:function(e){if(!(this instanceof dmx.Flow))return new dmx.Flow(e);window.Promise||console.warn("Promises are not supported, flows can not be used"),this._execStep=this._execStep.bind(this),this.scope=new dmx.DataScope({},e),this.output={}},run:function(e){return this.output={},this._exec(e.exec||e).then((()=>(dmx.debug&&console.debug("finished",this.output),this.output)))},_each:function(e,t){return Promise.resolve(e).then((e=>(e=Array.isArray(e)?e:[e]).reduce(((s,n,r)=>s.then((()=>t(n,r,e.length).then((t=>{t&&(e[r]=t)}))))),Promise.resolve()).then((()=>e))))},_exec:function(e){if(e.steps){var t=this._each(e.steps,this._execStep);return e.catch&&t.catch((t=>this._each(e.catch,self._execStep))),t}return this._each(e,this._execStep)},_execStep:function(e){for(let t in e){if(e.hasOwnProperty(t)&&dmx.__actions[t]){const s=dmx.__actions[t].bind(this),n=e[t],r=t+Date.now();return dmx.debug&&(console.debug("exec action",t,n),console.time(r)),n.disabled?Promise.resolve():Promise.resolve(s(n)).then((e=>{dmx.debug&&(console.debug("finished exec action",t,n),console.timeEnd(r)),n.name&&(dmx.debug&&console.debug("set data",n.name,e),this.scope.set(n.name,e),n.output&&(dmx.debug&&console.debug("set output",n.name,e),this.output[n.name]=e))}))}throw new Error("Action "+t+" was not found.")}},parse:function(e){if(null==e)return e;if("object"==typeof(e=e.valueOf())){var t=e.slice?[]:{};for(var s in e)e.hasOwnProperty(s)&&(t[s]=this.parse(e[s],this.scope));return t}return"string"==typeof e&&-1!=e.indexOf("{{")?dmx.parse(e,this.scope):e}}),dmx.Flow.run=function(e,t){return new dmx.Flow(t).run(e)},dmx.Component("app",{initialData:{query:{}},events:{ready:Event,load:Event},init(){this.dispatchLoad=this.dispatchEvent.bind(this,"load"),this._parseQuery=this._parseQuery.bind(this),window.addEventListener("load",this.dispatchLoad,{once:!0}),window.addEventListener("load",this._parseQuery),window.addEventListener("popstate",this._parseQuery),window.addEventListener("pushstate",this._parseQuery),window.addEventListener("replacestate",this._parseQuery),this._parseQuery(),queueMicrotask((()=>this.dispatchEvent("ready")))},destroy(){window.removeEventListener("load",this.dispatchLoad),window.removeEventListener("load",this._parseQuery),window.removeEventListener("popstate",this._parseQuery),window.removeEventListener("pushstate",this._parseQuery),window.removeEventListener("replacestate",this._parseQuery)},_parseQuery(){let e="";window.location.search?e=window.location.search.slice(1):window.location.hash.indexOf("?")&&(e=window.location.hash.slice(window.location.hash.indexOf("?")+1),e.indexOf("#")>0&&(e=e.slice(0,e.indexOf("#"))));let t=e.split("&").reduce((function(e,t){var s=t.replace(/\+/g," ").split("=");return s[0]&&(e[decodeURIComponent(s[0])]=decodeURIComponent(s[1]||"")),e}),{}),s=document.querySelector('meta[name="ac:base"]'),n=document.querySelector('meta[name="ac:route"]');if(n&&n.content){let e=[],r=n.content;s&&s.content&&(r=s.content.replace(/\/$/,"")+r);let i=dmx.pathToRegexp(r,e,{end:!1}).exec(decodeURI(window.location.pathname));i&&e.forEach((function(e,s){t[e.name]=i[s+1]}))}this.set("query",t)}}),dmx.Component("form",{attributes:{novalidate:{type:Boolean,default:!1}},methods:{submit(e){this._submit(e)},reset(){this._reset()},validate(){this._validate()}},events:{invalid:Event,submit:Event},init(e){this._submitHandler=this._submitHandler.bind(this),this._resetHandler=this._resetHandler.bind(this),e.noValidate=!0,e.addEventListener("submit",this._submitHandler),e.addEventListener("reset",this._resetHandler)},destroy(){this.$node.removeEventListener("submit",this._submitHandler),this.$node.removeEventListener("reset",this._resetHandler)},_submitHandler(e){e.preventDefault(),this._submit()},_resetHandler(e){dmx.validateReset&&dmx.validateReset(this.$node),window.grecaptcha&&this.$node.querySelector(".g-recaptcha")&&grecaptcha.reset()},_submit(e){if(e)return this._formSubmit();this.props.novalidate||this._validate()?this.dispatchEvent("submit",{cancelable:!0})&&this._formSubmit():(this.dispatchEvent("invalid"),this._focusFirstInvalid())},_reset(){this._formReset()},_validate(){return dmx.validate?dmx.validate(this.$node):(Array.from(this.$node.elements).forEach((e=>e.dirty=!0)),this.$node.checkValidity())},_formSubmit(){HTMLFormElement.prototype.submit.call(this.$node)},_formReset(){HTMLFormElement.prototype.reset.call(this.$node)},_focusFirstInvalid(){const e=Array.from(this.$node.elements).find((e=>!e.validity.valid));e&&e.focus()},_parseJsonForm(){const e={};for(const n of this.$node.elements)if(n.name&&!n.disabled){const r=t(n.name.replace(/\[\]$/,""));let i=e;for(const e of r){const t=n.type;"number"==t?n.value&&(i=s(i,e,i[e.key],+n.value)):"radio"==t||"checkbox"==t?n.getAttribute("value")?n.checked&&(i=s(i,e,i[e.key],n.value)):i=s(i,e,i[e.key],n.checked):i=s(i,e,i[e.key],"select-multiple"==t?Array.from(n.selectedOptions).map((e=>e.value)):n.value)}}return e;function t(e){const t=[],s=e,n=/^\[([^\]]*)\]/,r=/^\d+$/;if(!(e=e.replace(/^([^\[]+)/,((e,s)=>(t.push({type:"object",key:s}),"")))))return t[0].last=!0,t;for(;e;){if(!n.test(e))return{type:"object",key:s,last:!0};e=e.replace(n,((e,s)=>(s?r.test(s)?t.push({type:"array",key:+s}):t.push({type:"object",key:s}):t[t.length-1].append=!0,"")))}for(let e=0,s=t.length;ethis.dispatchEvent("updated")))},_focus(){this.$node.focus()},_disable(e){this.$node.disabled=e,this.set("disabled",this.$node.disabled)},_validate(){dmx.validate(this.$node),this.$node.dirty&&this.set({invalid:!this.$node.validity.valid,validationMessage:this.$node.validationMessage})},_inputHandler(e){this.$node.dirty&&this._validate(),dmx.nextTick((()=>{this.$node&&this.data.value!==this.$node.value&&(this.set("value",this.$node.value),e&&this.dispatchEvent("changed"),dmx.nextTick((()=>this.dispatchEvent("updated"))))}))},_changeHandler(e){this.$node.dirty&&this._validate(),dmx.nextTick((()=>{this.$node&&this.data.value!==this.$node.value&&(this.set("value",this.$node.value),e&&this.dispatchEvent("changed"),dmx.nextTick((()=>this.dispatchEvent("updated"))))}))},_invalidHandler(e){this.set({invalid:!this.$node.validity.valid,validationMessage:this.$node.validationMessage})},_resetHandler(e){this.$node&&(this.$node.dirty=!1,this.set({invalid:!1,validationMessage:""}),this._changeHandler(e))},_focusHandler(e){this.set("focused",!0)},_blurHandler(e){this.set("focused",!1)}}),dmx.Component("textarea",{extends:"form-element",init(e){if(!this.props.value){const e=this.$node.value;this.props.value=e.includes("{{")?this.parse(e):e}dmx.Component("form-element").prototype.init.call(this,e)}}),dmx.Component("input",{extends:"form-element"}),dmx.Component("input-file",{extends:"form-element",attributes:{imageMaxWidth:{type:Number,default:null},imageMaxHeight:{type:Number,default:null},imageType:{type:String,default:null,enum:["png","jpeg","webp"]},imageQuality:{type:Number,default:null}},initialData:{file:null},_imageTypes:{png:"image/png",jpeg:"image/jpeg",webp:"image/webp","image/png":"image/png","image/jpeg":"image/jpeg","image/webp":"image/webp"},_imageExtensions:{"image/png":"png","image/jpeg":"jpg","image/webp":"webp"},_setValue(e){console.warn("Can not set value of a file input!")},_changeHandler(e){dmx.Component("form-element").prototype._changeHandler.call(this,e),this._updateData(),this.$node.files.length&&(this.props.imageMaxWidth||this.props.imageMaxHeight||this.props.imageType)&&this._resizeImage()},_resizeImage(){const e=this.$node.files[0];if(e&&e.type.startsWith("image/")){const t=URL.createObjectURL(e),s=new Image;s.src=t,s.onerror=()=>URL.revokeObjectURL(t),s.onload=()=>{URL.revokeObjectURL(t);const{imageMaxWidth:n,imageMaxHeight:r,imageType:i,imageQuality:a}=this.props;let o=s.width,d=s.height,h=o/d,l=!1;n&&o>n&&(o=n,d=~~(o/h),l=!0),r&&d>r&&(d=r,o=~~(d*h),l=!0);const c=i?this._imageTypes[i]:e.type;if(c!==e.type||l){const t=document.createElement("canvas"),n=t.getContext("2d");t.width=o,t.height=d,n.drawImage(s,0,0,o,d),t.toBlob((t=>{if(null==t)return console.error("Could not resize image!");const s=new DataTransfer,n=e.name.replace(/\.\w+$/,"."+this._imageExtensions[t.type]),r=new File([t],n,{type:t.type});s.items.add(r),this.$node.files=s.files,this._updateData()}),c,a?a/100:void 0)}}}},_updateData(){let e=null;if(this.$node.files.length){const t=this,s=this.$node.files[0];e={date:(s.lastModified?new Date(s.lastModified):s.lastModifiedDate).toISOString(),name:s.name,size:s.size,type:s.type,get dataUrl(){return s._dataUrl||dmx.fileUtils.blobToDataURL(s).then((n=>{s._dataUrl=n,t.set("file",Object.assign({},e,{dataUrl:n}))})).catch((e=>{console.error(e)})),null}}}this.set("file",e)}}),dmx.Component("input-file-multiple",{extends:"form-element",attributes:{imageMaxWidth:{type:Number,default:null},imageMaxHeight:{type:Number,default:null},imageType:{type:String,default:null,enum:["png","jpeg","webp"]},imageQuality:{type:Number,default:null}},initialData:{files:[]},_imageTypes:{png:"image/png",jpeg:"image/jpeg",webp:"image/webp","image/png":"image/png","image/jpeg":"image/jpeg","image/webp":"image/webp"},_imageExtensions:{"image/png":"png","image/jpeg":"jpg","image/webp":"webp"},_setValue(e){console.warn("Can not set value of a file input!")},_changeHandler(e){dmx.Component("form-element").prototype._changeHandler.call(this,e),this._updateData(),this.$node.files.length&&(this.props.imageMaxWidth||this.props.imageMaxHeight||this.props.imageType)&&this._resizeImages()},_resizeImages(){const e=Array.from(this.$node.files);Promise.all(e.map((e=>new Promise((t=>{if(!e.type.startsWith("image/"))return void t(e);const s=URL.createObjectURL(e),n=new Image;n.src=s,n.onerror=()=>URL.revokeObjectURL(s),n.onload=()=>{URL.revokeObjectURL(s);const{imageMaxWidth:r,imageMaxHeight:i,imageType:a,imageQuality:o}=this.props;let d=n.width,h=n.height,l=d/h,c=!1;r&&d>r&&(d=r,h=~~(d/l),c=!0),i&&h>i&&(h=i,d=~~(h*l),c=!0);const u=a?this._imageTypes[a]:e.type;if(u!==e.type||c){const s=document.createElement("canvas"),r=s.getContext("2d");s.width=d,s.height=h,r.drawImage(n,0,0,d,h),s.toBlob((s=>{if(null==s)return console.error("Could not resize image!");const n=e.name.replace(/\.\w+$/,"."+this._imageExtensions[s.type]),r=new File([s],n,{type:s.type});t(r)}),u,o?o/100:void 0)}else t(e)}}))))).then((e=>{const t=new DataTransfer;for(let s of e)t.items.add(s);this.$node.files=t.files,this._updateData()}))},_updateData(){let e=[];if(this.$node.files.length){const t=this;e=Array.from(this.$node.files).map(((s,n)=>({date:(s.lastModified?new Date(s.lastModified):s.lastModifiedDate).toISOString(),name:s.name,size:s.size,type:s.type,get dataUrl(){return s._dataUrl||(loading=!0,dmx.fileUtils.blobToDataURL(s).then((r=>{s._dataUrl=r,e=dmx.clone(e),e[n].dataUrl=r,t.set("files",e)})).catch((e=>{console.error(e)}))),null}})))}this.set("files",e)}}),dmx.Component("button",{extends:"form-element",attributes:{type:{type:String,default:"button",enum:["button","reset","submit"]}},init(e){dmx.Component("form-element").prototype.init.call(this,e),e.type=this.props.type}}),dmx.Component("radio",{extends:"form-element",initialData:{checked:!1},attributes:{checked:{type:Boolean,default:!1,alwaysUpdate:!0}},methods:{select(e,t){this._select(e),t&&dmx.nextTick((()=>{this.dispatchEvent("changed"),this.dispatchEvent(this.$node.checked?"checked":"unchecked")}))}},events:{checked:Event,unchecked:Event},init(e){dmx.Component("form-element").prototype.init.call(this,e),e.type="radio",e.checked=this.props.checked,e.defaultChecked=this.props.checked,this.props.checked&&this.set("checked",!0)},performUpdate(e){dmx.Component("form-element").prototype.performUpdate.call(this,e),e.has("checked")&&this.$node.checked!=this.props.checked&&(this.$node.defaultChecked=this.props.checked,this.$node.checked=this.props.checked,this.set("checked",this.props.checked),this.$node.dispatchEvent(new Event("radio",{bubbles:!0})),dmx.nextTick((()=>this.dispatchEvent("updated"))))},_select(e){this.$node.checked=!1!==e,this.set("checked",this.$node.checked),dmx.nextTick((()=>this.dispatchEvent("updated")))},_changeHandler(e){this.$node.dirty&&this._validate(),dmx.nextTick((()=>{if(this.$node&&(this.set("checked",this.$node.checked),this.dispatchEvent("changed"),this.dispatchEvent(this.$node.checked?"checked":"unchecked"),dmx.nextTick((()=>this.dispatchEvent("updated"))),this.$node.checked&&this.$node.form))for(const e of this.$node.form.elements)e!=this.$node&&"radio"==e.type&&e.name==this.$node.name&&e.dispatchEvent(new Event("change",{bubbles:!0}))}))}}),dmx.Component("radio-group",{initialData:{value:null},attributes:{value:{type:String,default:null,alwaysUpdate:!0}},methods:{setValue(e){this._setValue(e)}},events:{updated:Event},init(e){this._changeHandler=this._changeHandler.bind(this),e.addEventListener("change",this._changeHandler),e.addEventListener("radio",this._changeHandler)},render(e){this.$parse(),this._setValue(this.props.value,!0),this._mutationObserver=new MutationObserver((e=>{let t=this.props.value;null==t&&(t=""),t=t.toString();for(let s of e)for(let e of s.addedNodes)e.nodeType===Node.ELEMENT_NODE&&requestAnimationFrame((()=>{"INPUT"===e.tagName&&"radio"===e.type?(e.checked=e.value==t,e.defaultChecked=e.checked):e.querySelectorAll("input[type=radio]").forEach((s=>{s.checked=e.value==t,s.defaultChecked=s.checked})),requestAnimationFrame((()=>{this._updateValue()}))}))})),this._mutationObserver.observe(e,{subtree:!0,childList:!0})},destroy(){this._mutationObserver.disconnect(),this.$node.removeEventListener("change",this._changeHandler),this.$node.removeEventListener("radio",this._changeHandler)},performUpdate(e){e.has("value")&&this._setValue(this.props.value,!0)},_setValue(e,t){null==e&&(e=""),e=e.toString(),this._radios().forEach((s=>{s.checked=s.value==e,t&&(s.defaultChecked=s.checked)})),this._updateValue()},_updateValue(){const e=this._radios().filter((e=>!e.disabled&&e.checked)).map((e=>e.value));dmx.equal(this.data.value,e[0])||(this.set("value",e[0]||null),dmx.nextTick((()=>this.dispatchEvent("updated"))))},_radios(){return Array.from(this.$node.querySelectorAll("input[type=radio]"))},_changeHandler(e){this._updateValue()}}),dmx.Component("checkbox",{extends:"form-element",initialData:{checked:!1},attributes:{checked:{type:Boolean,default:!1,alwaysUpdate:!0}},methods:{select(e,t){this._select(e),t&&dmx.nextTick((()=>{this.dispatchEvent("changed"),this.dispatchEvent(this.$node.checked?"checked":"unchecked")}))}},events:{checked:Event,unchecked:Event},init(e){dmx.Component("form-element").prototype.init.call(this,e),e.type="checkbox",e.checked=this.props.checked,e.defaultChecked=this.props.checked,this.props.checked&&this.set("checked",!0)},performUpdate(e){dmx.Component("form-element").prototype.performUpdate.call(this,e),e.has("checked")&&this.$node.checked!=this.props.checked&&(this.$node.defaultChecked=this.props.checked,this.$node.checked=this.props.checked,this.set("checked",this.props.checked),this.$node.dispatchEvent(new Event("checkbox",{bubbles:!0})),dmx.nextTick((()=>this.dispatchEvent("updated"))))},_select(e){this.$node.checked=!1!==e,this.set("checked",this.$node.checked),dmx.nextTick((()=>this.dispatchEvent("updated")))},_changeHandler(e){this.$node.dirty&&this._validate(),dmx.nextTick((()=>{this.$node&&(this.set("checked",this.$node.checked),this.dispatchEvent("changed"),"reset"!=e.type&&this.dispatchEvent(this.$node.checked?"checked":"unchecked"),dmx.nextTick((()=>this.dispatchEvent("updated"))))}))}}),dmx.Component("checkbox-group",{initialData:{value:[]},attributes:{value:{type:Array,default:[],alwaysUpdate:!0}},methods:{setValue(e){this._setValue(e)}},events:{updated:Event},init(e){this._changeHandler=this._changeHandler.bind(this),e.addEventListener("change",this._changeHandler),e.addEventListener("checkbox",this._changeHandler)},render(e){this.$parse(),this._setValue(this.props.value,!0),this._mutationObserver=new MutationObserver((e=>{let t=this.props.value;null==t&&(t=[]),Array.isArray(t)||(t=[t]),t=t.map((e=>e.toString()));for(let s of e)for(let e of s.addedNodes)e.nodeType===Node.ELEMENT_NODE&&requestAnimationFrame((()=>{"INPUT"===e.tagName&&"checkbox"===e.type?(e.checked=t.includes(e.value),e.defaultChecked=e.checked):e.querySelectorAll("input[type=checkbox]").forEach((e=>{e.checked=t.includes(e.value),e.defaultChecked=e.checked})),requestAnimationFrame((()=>{this._updateValue()}))}))})),this._mutationObserver.observe(e,{subtree:!0,childList:!0})},destroy(){this._mutationObserver.disconnect(),this.$node.removeEventListener("change",this._changeHandler),this.$node.removeEventListener("checkbox",this._changeHandler)},performUpdate(e){e.has("value")&&this._setValue(this.props.value,!0)},_setValue(e,t){null==e&&(e=[]),Array.isArray(e)||(e=[e]),e=e.map((e=>e.toString())),this._checkboxes().forEach((s=>{s.checked=e.includes(s.value),t&&(s.defaultChecked=s.checked)})),this._updateValue()},_updateValue(){const e=this._checkboxes().filter((e=>!e.disabled&&e.checked)).map((e=>e.value));dmx.equal(this.data.value,e)||(this.set("value",e),dmx.nextTick((()=>this.dispatchEvent("updated"))))},_checkboxes(){return Array.from(this.$node.querySelectorAll("input[type=checkbox]"))},_changeHandler(e){this._updateValue()}}),dmx.Component("select",{extends:"form-element",initialData:{selectedIndex:-1,selectedValue:"",selectedText:""},attributes:{options:{type:[Array,Object,Number],default:null},optiontext:{type:String,default:"$value"},optionvalue:{type:String,default:"$value"}},methods:{setSelectedIndex(e){this.$node.selectedIndex=e,this._updateValue()}},init(e){this._options=[],this.props.value||(this.props.value=this.$node.value,this._updateValue()),this._mutationObserver=new MutationObserver((e=>{this._updatingOptions||this._updateValue()})),this._mutationObserver.observe(this.$node,{subtree:!0,childList:!0}),dmx.Component("form-element").prototype.init.call(this,e)},render(e){this.$parse(),this._renderOptions();let t=this.props.value;null==t&&(t=""),Array.from(this.$node.options).forEach((e=>{e.toggleAttribute("selected",e.value==t),e.selected=e.value==t,e.defaultSelected=e.selected})),this._updateValue()},destroy(){this._mutationObserver.disconnect(),dmx.Component("form-element").prototype.destroy.call(this)},performUpdate(e){dmx.Component("form-element").prototype.performUpdate.call(this,e),(e.has("options")||e.has("optiontext")||e.has("optionvalue"))&&this._renderOptions()},_setValue(e,t){if(null==e&&(e=""),e=e.toString(),t)Array.from(this.$node.options).forEach((t=>{t.toggleAttribute("selected",t.value==e)}));else{const t=Array.from(this.$node.options).findIndex((t=>t.value==e));this.$node.selectedIndex=t}this._updateValue(),dmx.nextTick((()=>this.dispatchEvent("updated")))},_updateValue(){const e=this.$node.selectedIndex,t=this.$node.options[e]||{value:"",text:""};this.set({selectedIndex:e,selectedValue:t.value,selectedText:t.text,value:t.value})},_renderOptions(){this._options.forEach((e=>e.remove())),this._options=[],this.props.options&&(this._updatingOptions=!0,dmx.repeatItems(this.props.options).forEach((e=>{const t=document.createElement("option");t.value=dmx.parse(this.props.optionvalue,dmx.DataScope(e,this)),t.textContent=dmx.parse(this.props.optiontext,dmx.DataScope(e,this)),t.value==this.props.value&&(t.selected=!0),this.$node.append(t),this._options.push(t)})),this._updatingOptions=!1),this._updateValue()},_inputHandler(e){},_changeHandler(e){this.$node.dirty&&this._validate(),dmx.nextTick((()=>{this.data.selectedIndex!==this.$node.selectedIndex&&(this._updateValue(),this.dispatchEvent("changed"),dmx.nextTick((()=>this.dispatchEvent("updated"))))}))}}),dmx.Component("select-multiple",{extends:"select",initialData:{value:[]},attributes:{value:{type:Array,default:null,alwaysUpdate:!0}},performUpdate(e){dmx.Component("select").prototype.performUpdate.call(this,e),e.has("value")&&this._setValue(this.props.value,!0)},_setValue(e,t){null==e&&(e=""),Array.isArray(e)||(e=[e]),e=e.map((e=>e.toString())),Array.from(this.$node.options).forEach((s=>{const n=e.includes(s.value);t?s.toggleAttribute("selected",n):s.selected=n})),this._updateValue(),dmx.nextTick((()=>this.dispatchEvent("updated")))},_getValue(){return Array.from(this.$node.selectedOptions).map((e=>e.value))},_updateValue(){const e=this._getValue();dmx.batch((()=>{dmx.Component("select").prototype._updateValue.call(this),this.set("value",e)}))},_changeHandler(e){this.$node.dirty&&this._validate(),dmx.nextTick((()=>{this.data.selectedIndex===this.$node.selectedIndex&&dmx.equal(this.data.value,this._getValue())||(this._updateValue(),this.dispatchEvent("changed"),dmx.nextTick((()=>this.dispatchEvent("updated"))))}))}}),dmx.Component("value",{initialData:{value:null},attributes:{value:{default:null}},methods:{setValue(e){this.data.value!==e&&(this.set("value",e),dmx.nextTick((()=>this.dispatchEvent("updated"))))}},events:{updated:Event},render:!1,init(e){this.set("value",this.props.value)},performUpdate(e){e.has("value")&&(this.set("value",this.props.value),dmx.nextTick((()=>this.dispatchEvent("updated"))))}}),dmx.Component("repeat",{initialData:{items:[]},attributes:{repeat:{type:[Array,Object,Number],default:null},key:{type:String,default:""},rerender:{type:Boolean,default:!1}},events:{update:Event,updated:Event},render:!1,init(e){for(this.prevItems=[],this.childKeys=new Map,this.$template=document.createDocumentFragment();this.$node.hasChildNodes();)this.$template.appendChild(this.$node.firstChild);this.props.repeat&&this.performUpdate(new Map([["repeat",void 0]]))},performUpdate(e){if(e.has("key")&&(this._rerender=!0),e.has("repeat")){this.dispatchEvent("update"),(this.props.rerender||this._rerender)&&(this._rerender=!1,this._clear());var t=dmx.Component("repeat-item"),s=dmx.clone(this.props.repeat),n=dmx.repeatItems(s);if(n.length){if(!this.props.rerender&&this.props.key&&n[0].hasOwnProperty(this.props.key)&&this.prevItems.length){var r,i,a=this.props.key,o=this.prevItems,d=this._clone(n),h=0,l=0,c=o.length-1,u=d.length-1;e:for(;;){for(;o[h][a]===d[l][a];)if(this.childKeys.get(d[l][a]).set(d[l]),l++,++h>c||l>u)break e;for(;o[c][a]===d[u][a];)if(this.childKeys.get(d[u][a]).set(d[u]),u--,h>--c||l>u)break e;if(o[c][a]!==d[l][a]){if(o[h][a]!==d[u][a])break;if(i=u+1,this.childKeys.get(d[u][a]).set(d[u]),this._moveChild(d[u][a],d[i]&&d[i][a]),u--,++h>c||l>u)break}else if(this.childKeys.get(d[l][a]).set(d[l]),this._moveChild(d[l][a],o[h][a]),l++,h>--c||l>u)break}if(h>c)for(i=u+1;l<=u;)this._insertChild(d[l++],d[i]&&d[i][a]);else if(l>u)for(;h<=c;)this._removeChild(o[h++][a]);else{var p=c-h+1,m=u-l+1,f=o,g=new Array(m).fill(-1),y=!1,_=0,v=0;if(m<=4||p*m<=16){for(w=h;w<=c;w++)if(vr?y=!0:_=r,this.childKeys.get(d[r][a]).set(d[r]),v++,f[w]=null;break}}else{var x={};for(w=l;w<=u;w++)x[d[w][a]]=w;for(w=h;w<=c;w++)vr?y=!0:_=r,this.childKeys.get(d[r][a]).set(d[r]),v++,f[w]=null)}if(p===o.length&&0===v)for(this._clear();l0;)null!==f[h]&&(this._removeChild(o[h][a]),w--),h++;if(y){var b=this._lis(g);for(r=b.length-1,w=m-1;w>=0;w--)-1===g[w]?(i=(_=w+l)+1,this._insertChild(d[_],d[i]&&d[i][a])):r<0||w!==b[r]?(i=(_=w+l)+1,this._moveChild(d[_][a],d[i]&&d[i][a])):r--}else if(v!==m)for(w=m-1;w>=0;w--)-1===g[w]&&(i=(_=w+l)+1,this._insertChild(d[_],d[i]&&d[i][a]))}}}else if(this.children.length>n.length&&this.children.splice(n.length).forEach((e=>{e.$destroy()})),this.children.length&&this.children.forEach(((e,t)=>{e.set(n[t])})),n.length>this.children.length){const e=document.createDocumentFragment(),s=new Set;for(var w=this.children.length;w{e.appendChild(t),s.add(E)})),this.children.push(E)}this.$node.appendChild(e);for(const e of s)e.$nodes.forEach((t=>{e.$parse(t)}))}}else this._clear();if(this.props.key){this.prevItems=this._clone(n);for(let e of this.children)this.childKeys.set(e.data[this.props.key],e)}this.set("items",this.children.map((e=>e.data))),dmx.nextTick((()=>this.dispatchEvent("updated")))}},_lis(e){var t,s,n=e.slice(0),r=[];r.push(0);for(var i=0,a=e.length;i0&&(n[i]=r[t-1]),r[t]=i)}}for(s=r[(t=r.length)-1];t-- >0;)r[t]=s,s=n[s];return r},_clear(){this.prevItems=[],this.childKeys.clear(),this.$node.innerHTML="",this.children.splice(0).forEach((e=>{e.$destroy()}))},_insertChild(e,t){var s=new(dmx.Component("repeat-item"))(this.$template.cloneNode(!0),this,e);s.$nodes.forEach((e=>{t?this.childKeys.has(t)?this.$node.insertBefore(e,this.childKeys.get(t).$nodes[0]):console.warn("(insert) can not insert node before key "+t+"!"):this.$node.appendChild(e),s.$parse(e)})),this.childKeys.set(e[this.props.key],s),this.children.push(s)},_moveChild(e,t){var s=this.childKeys.get(e);s?this.childKeys.has(t)?s.$nodes.forEach((e=>{this.$node.insertBefore(e,this.childKeys.get(t).$nodes[0])})):s.$nodes.forEach((e=>{this.$node.appendChild(e)})):console.warn("(move) child with key "+e+" not found!")},_removeChild(e){var t=this.childKeys.get(e);t?(t.$destroy(),this.children.splice(this.children.indexOf(t),1),this.childKeys.delete(e)):console.warn("(remove) child with key "+e+" not found!")},_clone:e=>dmx.clone(e)}),dmx.Component("repeat-item",{constructor:function(e,t,s,n){this.parent=t,this.children=[],this.listeners={},this.props={},this.__disposables=[],this.__childDisposables=[],this.updatedProps=new Map,this.updateRequested=!1,this.isInitialized=!0,this.isDestroyed=!1,this.data=dmx.signalProxy(s),this.seed=t.seed,this.name=n||"repeatItem",this.$nodes=[];for(var r=0;r{t&&s&&["param","header"].includes(e)&&this.$watch(s,(s=>{this.props[e+"s"]=Object.assign({},this.props[e+"s"],{[t]:s})})),t&&s&&"data"==e&&this.$watch(s,(e=>{this.props.data=Object.assign({},this.props.data,{[t]:e})}))}))},_abort(){this._xhr&&this._xhr.abort()},_resetData(e){const t={status:0,headers:{},state:{executing:!1,uploading:!1,processing:!1,downloading:!1},uploadProgress:{position:0,total:0,percent:0},downloadProgress:{position:0,total:0,percent:0},lastError:{status:0,message:"",response:null}};e&&(t.data=null),this.set(t)},_fetch(e){this._abort(),e=dmx.extend(!0,this.props,e||{});let t=Object.keys(e.params).filter((t=>null!=e.params[t])).map((t=>{let s=e.params[t];return"string"==typeof s&&s.startsWith("{{")&&(s=this.parse(s)),encodeURIComponent(t)+"="+encodeURIComponent(s)})).join("&");if(this._resetData(),this.dispatchEvent("start"),this._url=e.url,t&&(this._url+=(this._url.includes("?")?"&":"?")+t),window.WebviewProxy&&(this._url=window.WebviewProxy.convertProxyUrl(this._url)),this.props.cache){const t=this.parse(`${this.props.cache}.data["${this._url}"]`);if(t){if(!(Date.now()-t.created>=1e3*e.ttl))return this.set({headers:t.headers||{},paging:t.paging||{},links:t.links||{},data:t.data}),this.dispatchEvent("success"),void this.dispatchEvent("done");this.parse(`${this.props.cache}.remove("${this._url}")`)}}this.set("state",{executing:!0,uploading:!1,processing:!1,downloading:!1});let s=null,n=this.props.method.toUpperCase();"GET"!==n&&("text"===this.props.dataType?s=this.props.data.toString():"json"===this.props.dataType?s=JSON.stringify(this.props.data):"POST"===n?(s=new FormData,Object.keys(this.props.data).forEach((e=>{let t=this.props.data[e];Array.isArray(t)?(/\[\]$/.text(t)||(e+="[]"),t.forEach((t=>s.append(e,t)))):s.set(e,t)}))):s=this.props.data.toString()),this._xhr.open(n,this._url),this._xhr.timeout=1e3*e.timeout,"json"!==this.props.dataType&&"text"!==this.props.dataType||this._xhr.setRequestHeader("Content-Type","application/"+this.props.dataType);for(const e in this.props.headers)this._xhr.setRequestHeader(e,this.props.headers[e]);if(this._xhr.setRequestHeader("accept","application/json"),this.props.credentials&&(this._xhr.withCredentials=!0),this.serverconnect&&"GET"!==n){const e=document.querySelector('meta[name="csrf-token"]');e&&this._xhr.setRequestHeader("X-CSRF-Token",e.content)}try{this._xhr.send(s)}catch(e){this._done(e)}},_done(e){if(this._resetData(),e)return this.set("lastError",{status:0,message:e.message,response:null}),this.dispatchEvent("error"),void this.dispatchEvent("done");let t=this._xhr.responseText;try{t=JSON.parse(t)}catch(e){if(this._xhr.status<400)return this.set("lastError",{status:0,message:"Response was not valid JSON",response:t}),this.dispatchEvent("error"),void this.dispatchEvent("done")}if(this._parseHeaders(),this._xhr.status<400)return this.set({status:this._xhr.status,data:t}),this.dispatchEvent("success"),this.dispatchEvent("done"),void(this.props.cache&&this.parse(`${this.props.cache}.set("${this._url}", { headers: headers, paging: paging, links: links, data: data, created: ${Date.now()} })`));this.set({status:this._xhr.status,lastError:{status:this._xhr.status,message:this._xhr.statusText,response:t}}),this.dispatchEvent(this._statusEvents[this._xhr.status]||"error"),this.dispatchEvent("done")},_parseHeaders(){try{const e=this._xhr.getAllResponseHeaders().trim().split(/[\r\n]+/);this.set("headers",e.reduce(((e,t)=>{const s=t.split(": "),n=s.shift(),r=s.join(": ");return e[n]=r,e}),{}))}catch(e){return void console.warn("Error parsing response headers",e)}this._parseLinkHeaders()},_parseLinkHeaders(){try{const e=Object.keys(this.data.headers).find((e=>"link"===e.toLowerCase()));e&&this.set("links",this.data.headers[e].split(/,\s*{try{const t=e.match(/]*)>(.*)/),s=new URL(t[1]),n=t[2].split(";"),r=s.search.slice(1).split("&").reduce(((e,t)=>{const s=t.split("=");return s[0]&&(e[decodeURIComponent[s[0]]]=decodeURIComponent(s[1]||"")),e}),{});let i=n.slice(1).reduce(((e,t)=>{const s=t.match(/\s*(.+)\s*=\s*"?([^"]+)"?/);return s&&(e[s[1]]=s[2]),e}),{});return i=Object.assign({},r,i),i.url=s.toString(),i}catch(e){return console.warn("Error parsing link header part",e),null}}))).filter((e=>e&&e.rel)).reduce(((e,t)=>(t.rel.split(/\s+/).forEach((s=>e[s]=Object.assign(t,{rel:s}))),e)),{})}catch(e){return void console.warn("Error parsing link header",e)}this._parsePaging()},_parsePaging(){try{const e={page:1,pages:1,items:0,has:{first:!1,prev:!1,next:!1,last:!1}},{first:t,prev:s,next:n,last:r}=this.data.links;if(s||n){r&&r.page?e.pages=+r.page:s&&s.page&&(e.pages=+s.page+1);const i=Object.keys(this.data.headers).find((e=>"x-total"===(e=e.toLowerCase())||"x-count"===e||"x-total-count"===e));i&&(e.items=+this.data.headers[i]),s&&s.page?e.page=+s.page+1:n&&n.page&&(e.page=+n.page-1),e.has={first:!!t,prev:!!s,next:!!n,last:!!r}}this.set("paging",e)}catch(e){console.warn("Error parsing paging",e)}},_loadHandler(e){this._done()},_abortHandler(e){this._resetData(),this.dispatchEvent("abort"),this.dispatchEvent("done")},_errorHandler(e){this._done(Error("Failed to execute"))},_timeoutHandler(e){this._done(Error("Execution timeout"))},_progressHandler(e,t){t.loaded=t.loaded||t.position;const s=t.lengthComputable?Math.ceil(100*t.loaded/t.total):0;this.set({state:{executing:!0,uploading:"upload"===e&&s<100,processing:"upload"===e&&100===s,downloading:"download"===e},[e+"Progress"]:{position:t.loaded,total:t.total,percent:s}}),this.dispatchEvent(e,{lengthComputable:t.lengthComputable,loaded:t.loaded,total:t.total})}}),dmx.Component("serverconnect",{extends:"fetch",attributes:{sockets:{type:Boolean,default:!1}},init(e){this.serverconnect=!0,this.props.sockets&&dmx.Socket&&(this._refresh=this._refresh.bind(this),this._event=this.props.url.replace(/^(.*?)api\//,""),this._socket=dmx.Socket("/api"),this._socket.on(this._event,this._refresh)),dmx.Component("fetch").prototype.init.call(this,e)},destroy(){this._socket&&this._socket.off(this._event,this._refresh),dmx.Component("fetch").prototype.destroy.call(this)},_fetch(e){this._socket&&this._socket.connected?this._refresh(e&&e.params):dmx.Component("fetch").prototype._fetch.call(this,e)},_refresh(e){e=dmx.extend(!0,{},this.props.params,e||{}),this.dispatchEvent("start"),this.set("state",{executing:!0,uploading:!1,processing:!0,downloading:!1}),this._socket.emit(this._event,e,(e=>{this.set({status:e.status,data:e.data,state:{executing:!1,uploading:!1,processing:!1,downloading:!1}}),this.dispatchEvent(this._statusEvents[e.status]||"error"),this.dispatchEvent("done")}))}}),dmx.Component("serverconnect-form",{extends:"form",initialData:{status:0,data:null,headers:{},state:{executing:!1,uploading:!1,processing:!1,downloading:!1},uploadProgress:{position:0,total:0,percent:0},downloadProgress:{position:0,total:0,percent:0},lastError:{status:0,message:"",response:null}},attributes:{timeout:{type:Number,default:0},autosubmit:{type:Boolean,default:!1},params:{type:Object,default:{}},headers:{type:Object,default:{}},postData:{type:String,default:"form"},credentials:{type:Boolean,default:!1}},methods:{abort(){this._abort()},reset(e){this._reset(),e&&(this._abort(),this._resetData(!0))}},events:{start:Event,done:Event,error:Event,unauthorized:Event,forbidden:Event,abort:Event,success:Event,upload:ProgressEvent,download:ProgressEvent},init(e){dmx.Component("form").prototype.init.call(this,e),this._loadHandler=this._loadHandler.bind(this),this._abortHandler=this._abortHandler.bind(this),this._errorHandler=this._errorHandler.bind(this),this._timeoutHandler=this._timeoutHandler.bind(this),this._downloadProgressHandler=this._progressHandler.bind(this,"download"),this._uploadProgressHandler=this._progressHandler.bind(this,"upload"),this._xhr=new XMLHttpRequest,this._xhr.addEventListener("load",this._loadHandler),this._xhr.addEventListener("abort",this._abortHandler),this._xhr.addEventListener("error",this._errorHandler),this._xhr.addEventListener("timeout",this._timeoutHandler),this._xhr.addEventListener("progress",this._downloadProgressHandler),this._xhr.upload.addEventListener("progress",this._uploadProgressHandler),this._extendNode(e),this.props.autosubmit&&dmx.nextTick((()=>this._submit()))},destroy(){dmx.Component("form").prototype.destroy.call(this),this._xhr.removeEventListener("load",this._loadHandler),this._xhr.removeEventListener("abort",this._abortHandler),this._xhr.removeEventListener("error",this._errorHandler),this._xhr.removeEventListener("timeout",this._timeoutHandler),this._xhr.removeEventListener("progress",this._downloadProgressHandler),this._xhr.upload.removeEventListener("progress",this._uploadProgressHandler),this._xhr=null},$parseAttributes(e){dmx.BaseComponent.prototype.$parseAttributes.call(this,e),dmx.dom.getAttributes(e).forEach((({name:e,argument:t,value:s})=>{t&&s&&["param","header"].includes(e)&&this.$watch(s,(s=>{this.props[e+"s"]=Object.assign({},this.props[e+"s"],{[t]:s})}))}))},_extendNode(e){e.dmxExtraData={},e.dmxExtraElements=[]},_abort(){this._xhr.abort()},_resetData(e){const t={status:0,headers:{},state:{executing:!1,uploading:!1,processing:!1,downloading:!1},uploadProgress:{position:0,total:0,percent:0},downloadProgress:{position:0,total:0,percent:0},lastError:{status:0,message:"",response:null}};e&&(t.data=null),this.set(t)},_formSubmit(){this._send()},_send(){this._abort();const e=this.$node.method.toUpperCase(),t=this.$node.action;let s=null,n=Object.keys(this.props.params).filter((e=>null!=this.props.params[e])).map((e=>{let t=this.props.params[e];return"string"==typeof t&&t.startsWith("{{")&&(t=this.parse(t)),encodeURIComponent(e)+"="+encodeURIComponent(t)})).join("&");if("GET"===e)n.length&&(n+="&"),n+=Array.from(this.$node.elements).filter((e=>!e.disabled&&("radio"!==e.type&&"checkbox"!==e.type||e.checked))).map((e=>encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))).join("&");else if("json"===this.props.postData)s=this._parseJsonForm(),this.$node.dmxExtraData&&Object.assign(s,this.$node.dmxExtraData),s=JSON.stringify(s);else if(s=new FormData(this.$node),this.$node.dmxExtraData)for(let e in this.$node.dmxExtraData){let t=this.$node.dmxExtraData[e];Array.isArray(t)?(/\[\]$/.test(e)||(e+="[]"),t.forEach((t=>s.append(e,t)))):s.set(e,t)}this._resetData(),this.dispatchEvent("start"),this.set("state",{executing:!0,uploading:!1,processing:!1,downloading:!1});let r=t;n&&(r+=(r.includes("?")?"&":"?")+n),window.WebviewProxy&&(r=window.WebviewProxy.convertProxyUrl(r)),this._xhr.open(e,r),this._xhr.timeout=1e3*this.props.timeout,"json"===this.props.postData&&this._xhr.setRequestHeader("Content-Type","application/json");for(const e in this.props.headers)this._xhr.setRequestHeader(e,this.props.headers[e]);this._xhr.setRequestHeader("accept","application/json"),this.props.credentials&&(this._xhr.withCredentials=!0);const i=document.querySelector('meta[name="csrf-token"]');i&&this._xhr.setRequestHeader("X-CSRF-Token",i.content);try{this._xhr.send(s)}catch(e){this._done(e)}},_done(e){if(this._resetData(),e)return this.set("lastError",{status:0,message:e.message,response:null}),this.dispatchEvent("error"),void this.dispatchEvent("done");let t=this._xhr.responseText;try{t=JSON.parse(t)}catch(e){if(this._xhr.status<400)return this.set("lastError",{status:0,message:"Response was not valid JSON",response:t}),this.dispatchEvent("error"),void this.dispatchEvent("done")}try{const e=this._xhr.getAllResponseHeaders().trim().split(/[\r\n]+/);this.set("headers",e.reduce(((e,t)=>{const s=t.split(": "),n=s.shift(),r=s.join(": ");return e[n]=r,e}),{}))}catch(e){console.warn("Error parsing response headers",e)}if(dmx.validateReset&&dmx.validateReset(this.$node),window.grecaptcha&&this.$node.querySelector(".g-recaptcha")&&grecaptcha.reset(),this._xhr.status<400)return this.set({status:this._xhr.status,data:t}),this.dispatchEvent("success"),void this.dispatchEvent("done");if(this.set({status:this._xhr.status,lastError:{status:this._xhr.status,message:this._xhr.statusText,response:t}}),400===this._xhr.status)if(this.dispatchEvent("invalid"),t.form&&dmx.validate.setMessage)for(const e in t.form){const s=this.$node.querySelector(`[name="${e}"]`);if(s){const n=t.form[e];dmx.validate.setMessage(s,n)}}else dmx.debug&&console.warn("400 error, no form errors in response.",t);else 401===this._xhr.status?this.dispatchEvent("unauthorized"):403===this._xhr.status?this.dispatchEvent("forbidden"):this.dispatchEvent("error");this.dispatchEvent("done")},_loadHandler(e){this._done()},_abortHandler(e){this._resetData(),this.dispatchEvent("abort"),this.dispatchEvent("done")},_errorHandler(e){this._done(Error("Failed to execute"))},_timeoutHandler(e){this._done(Error("Execution timeout"))},_progressHandler(e,t){t.loaded=t.loaded||t.position;const s=t.lengthComputable?Math.ceil(100*t.loaded/t.total):0;this.set({state:{executing:!0,uploading:"upload"===e&&s<100,processing:"upload"===e&&100===s,downloading:"download"===e},[e+"Progress"]:{position:t.loaded,total:t.total,percent:s}}),this.dispatchEvent(e,{lengthComputable:t.lengthComputable,loaded:t.loaded,total:t.total})}}),dmx.Component("if",{attributes:{condition:{type:Boolean,default:!1}},events:{show:Event,hide:Event},init(e){for(this._shown=!1,this._template=document.createDocumentFragment();e.firstChild;)this._template.appendChild(e.firstChild)},render(e){this.props.condition&&this._show()},performUpdate(e){this.props.condition?this._show():this._hide()},destroy(){this._template=null},_show(){if(this._shown)return;const e=this._template.cloneNode(!0);this.$node.appendChild(e),this.$parse(),this.dispatchEvent("show"),this._shown=!0},_hide(){this._shown&&(this.effects&&(this.effects.forEach((e=>e())),this.effects=null),Array.from(this.$node.childNodes).forEach((e=>{const t=new Event("remove",{cancelable:!0});e.dispatchEvent(t)&&e.remove()})),this.$destroyChildren(),this.dispatchEvent("hide"),this._shown=!1)}}),dmx.Component("datetime",{initialData:{datetime:null},attributes:{interval:{type:String,default:"seconds",enum:["seconds","minutes","hours","days"]},utc:{type:Boolean,default:!1}},init(){this._tick=this._tick.bind(this),this._tick()},destroy(){this._timeout&&clearTimeout(this._timeout),this._animationFrame&&cancelAnimationFrame(this._animationFrame)},_tick(){switch(this.set("datetime",this._datetime()),this.props.interval){case"seconds":return this._timeout=setTimeout(this._tick,1e3);case"minutes":return this._timeout=setTimeout(this._tick,6e4);case"hours":return this._timeout=setTimeout(this._tick,36e5);case"days":return this._timeout=setTimeout(this._tick,864e5);default:return this._animationFrame=requestAnimationFrame(this._tick)}},_datetime(){const e=new Date,t=(e,t)=>("0000"+e).slice(-t),s=this.props.utc,n=s?e.getUTCFullYear():e.getFullYear(),r=(s?e.getUTCMonth():e.getMonth())+1,i=s?e.getUTCDate():e.getDate(),a=s?e.getUTCHours():e.getHours(),o=s?e.getUTCMinutes():e.getMinutes(),d=s?e.getUTCSeconds():e.getSeconds(),h=t(n,4)+"-"+t(r,2)+"-"+t(i,2),l=s?"Z":"";switch(this.props.interval){case"days":return h+"T00:00:00"+l;case"hours":return h+"T"+t(a,2)+":00:00"+l;case"minutes":return h+"T"+t(a,2)+":"+t(o,2)+":00"+l}return h+"T"+t(a,2)+":"+t(o,2)+":"+t(d,2)+l}}),dmx.Component("api-action",{extends:"fetch"}),dmx.Component("api-form",{extends:"serverconnect-form"}),dmx.Component("array",{initialData:{items:[],count:0},attributes:{items:{type:Array,default:[]}},events:{updated:Event},methods:{add(e){this._splice(this._count(),0,e)},addUniq(e){-1==this._indexOf(e)&&this._splice(this._count(),0,e)},insert(e,t){this._splice(e,0,t)},insertBefore(e,t){const s=this._indexOf(e);-1!=s&&this._splice(s,0,t)},insertAfter(e,t){const s=this._indexOf(e);-1!=s&&this._splice(s+1,0,t)},replace(e,t){const s=this._indexOf(e);-1!=s&&this._splice(s,1,t)},replaceAt(e,t){this._splice(e,1,t)},remove(e){const t=this._indexOf(e);-1!=t&&this._splice(t,1)},removeAt(e){this._splice(e,1)},reverse(){this._reverse()},sort(){this._sort()},empty(){this._updateData([])}},render:!1,init(){const e=dmx.array(this.props.items);this.set({items:e,count:e.length})},performUpdate(e){e.has("items")&&this._updateData(dmx.array(this.props.items))},_count(){return this.data.items.length},_indexOf(e){return this.data.items.indexOf(e)},_splice(e,t,s){const n=dmx.clone(this.data.items);void 0!==s?n.splice(e,t,s):n.splice(e,t),this._updateData(n)},_reverse(){const e=dmx.clone(this.data.items);e.reverse(),this._updateData(e)},_sort(){const e=dmx.clone(this.data.items);e.sort(),this._updateData(e)},_updateData(e){dmx.equal(this.data.items,e)||(this.set({items:e,count:e.length}),dmx.nextTick((()=>this.dispatchEvent("updated"))))}}),dmx.Component("group",{}),dmx.Component("flow",{initialData:{data:null,running:!1,lastError:null},attributes:{src:{type:String,default:null},preload:{type:Boolean,default:!1},autorun:{type:Boolean,default:!1},params:{type:Object,default:{}}},methods:{run(e,t){return this._run(e,t)},runSub(e){return this._runSub(e)}},events:{start:Event,done:Event,error:Event},render:!1,init(e){if(this.props.src)(this.props.preload||this.props.autorun)&&this._load(this.props.src,this.props.autorun).catch(console.error);else try{this._flow=this._parse(e.textContent),this.props.autorun&&this._run()}catch(e){console.error(e)}},destroy(){this._destroyed=!0},$parseAttributes(e){dmx.BaseComponent.prototype.$parseAttributes.call(this,e),dmx.dom.getAttributes(e).forEach((({name:e,argument:t,value:s})=>{t&&s&&"param"==e&&this.$watch(s,(e=>{this.props.params=Object.assign({},this.props.params,{[t]:e})}))}))},_load(e,t){return fetch(e).then((e=>{if(!e.ok||e.status>=400)throw Error(`Could not load flow ${this.name}, status ${e.status} ${e.statusText}`);return e.text()})).then((e=>{this._flow=this._parse(e),t&&this._run()}))},_parse:e=>(window.Hjson?Hjson:JSON).parse(e),_runSub(e){if(!this._flow){if(this.props.src)return this._load(this.props.src).then((()=>{this._runFlow(e)}));throw Error("No flow")}return this._runFlow(e)},_run(e,t){return this._flow?this.data.running?void console.info(`Can't run flow ${this.name} when a previous run didn't finish.`):(this.set({running:!0,lastError:null}),this.dispatchEvent("start"),dmx.debug&&(console.debug(`Running flow ${this.name} with params`,e),console.time(`Flow ${this.name}`)),this._runFlow(e).then((e=>(dmx.debug&&(console.debug(`Flow ${this.name} finished`,e),console.timeEnd(`Flow ${this.name}`)),this.set({running:!1,data:e}),this.dispatchEvent("done"),e))).catch((e=>{if(this.set({running:!1,lastError:e&&e.message}),this.dispatchEvent("error"),t)throw e}))):this.props.src?this._load(this.props.src).then((()=>{this._run(e,t)})).catch(console.error):void console.warn(`Flow ${this.name} is missing.`)},_runFlow(e){return dmx.Flow.run(this._flow,dmx.DataScope({$param:Object.assign({},this.props.params,e)},this))}}),dmx.Component("toggle",{initialData:{checked:!1},attributes:{checked:{type:Boolean,default:!1}},methods:{check(){this.props.checked=!0},uncheck(){this.props.checked=!1},toggle(){this.props.checked=!this.data.checked}},events:{updated:Event},render:!1,init(e){this.set("checked",this.props.checked)},performUpdate(e){e.has("checked")&&(this.set("checked",this.props.checked),dmx.nextTick((()=>this.dispatchEvent("updated"))))}}),dmx.Component("form-data",{attributes:{name:{type:String,default:"data"},data:{type:[Array,Object],default:null}},init(e){this._formdataHandler=this._formdataHandler.bind(this),this._form=e.closest("form"),this._form&&this._form.addEventListener("formdata",this._formdataHandler)},destroy(){this._form&&this._form.removeEventListener("formdata",this._formdataHandler)},_formdataHandler(e){const t=e.formData,s=this.props.data;this._appendData(t,s,this.props.name)},_appendData(e,t,s=""){if(Array.isArray(t))t.forEach(((t,n)=>{this._appendData(e,t,`${s}[${n}]`)}));else if("object"==typeof t)for(const n in t)this._appendData(e,t[n],`${s}[${n}]`);else e.append(s,t)}}),dmx.Attribute("bind","mounted",(function(e,t){const s=t.argument,n=dmx.reToggleAttribute.test(s);this.$watch(t.value,(t=>{if(n)e.toggleAttribute(s,!!t);else{if("style"===s&&"object"==typeof t)return Object.assign(e.style,t);if(null==t)return e.removeAttribute(s);e.setAttribute(s,t),"src"===s&&("VIDEO"===e.nodeName||"AUDIO"===e.nodeName?e.load():"SOURCE"===e.nodeName&&e.parentNode&&e.parentNode.load())}}))})),dmx.Attribute("on","mounted",(function(e,t){return e.dmxOn||(e.dmxOn={component:this}),e.dmxOn[t.argument]=!0,dmx.eventListener(e,t.argument,(function(s){s.originalEvent&&(s=s.originalEvent);return dmx.parse(t.value,dmx.DataScope({$event:s.$data,$originalEvent:s},e.dmxOn.component))}),t.modifiers)})),dmx.Attribute("repeat","before",(function(e,t){const s=document.createComment("Repeat Attribute"),n=document.createDocumentFragment(),r=dmx.Component("repeat-item");e.parentNode.replaceChild(s,e),e.removeAttribute(t.fullName),n.append(e);let i=[];this.$watch(t.value,(e=>{const a=dmx.repeatItems(e);if(a.length>1e4&&(console.warn("More than 10000 repeat items, we limit the result!"),a.length=1e4),t.modifiers.fast){if(i.length>a.length&&i.splice(a.length).forEach((e=>e.$destroy())),i.length&&i.forEach(((e,t)=>e.set(a[t]))),a.length>i.length){const e=document.createDocumentFragment(),o=new Set;a.slice(i.length).forEach((t=>{const s=new r(n.cloneNode(!0),this,t);e.appendChild(s.$nodes[0]),o.add(s),i.push(s),this.$addChild(s)})),s.parentNode.insertBefore(e,s);for(const e of o)e.$parse(e.$nodes[0]);t.argument&&this.set(t.argument,a)}}else{const e=document.createDocumentFragment(),o=new Set;i.splice(0).forEach((e=>e.$destroy()));for(const t of a){const s=new r(n.cloneNode(!0),this,t);e.append(s.$nodes[0]),o.add(s),i.push(s),this.$addChild(s)}s.parentNode.insertBefore(e,s);for(const e of o)e.$parse(e.$nodes[0]);t.argument&&this.set(t.argument,a)}}))})),dmx.Attribute("class","mounted",(function(e,t){e.dmxClass||(e.dmxClass={component:this}),this.$watch(t.value,(s=>{e.dmxClass[t.argument]=s,e.classList[s?"add":"remove"](t.argument)}))})),dmx.Attribute("hide","mounted",(function(e,t){e.dmxHide||(e.dmxHide={component:this,initial:{display:e.style.getPropertyValue("display"),priority:e.style.getPropertyPriority("display")},hide:null},this.$watch(t.value,(t=>{e.dmxHide.hide=t;const{initial:s}=e.dmxHide,n=t?"none":s.display,r=t?"important":s.priority;e.style.setProperty("display",n,r)})))})),dmx.Attribute("show","mounted",(function(e,t){e.dmxShow||(e.dmxShow={component:this,initial:{display:e.style.getPropertyValue("display"),priority:e.style.getPropertyPriority("display")},show:null},this.$watch(t.value,(t=>{e.dmxShow.show=t;const{initial:s}=e.dmxShow,n=t?s.display:"none",r=t?s.priority:"important";e.style.setProperty("display",n,r)})))})),dmx.Attribute("html","mounted",(function(e,t){e.dmxHtml||(e.dmxHtml={component:this},this.$watch(t.value,(t=>{e.innerHTML=null!=t?String(t):""})))})),dmx.Attribute("text","mounted",(function(e,t){e.dmxText||(e.dmxText={component:this},this.$watch(t.value,(t=>{e.innerText=null!=t?String(t):""})))})),dmx.Attribute("style","mounted",(function(e,t){e.dmxStyle||(e.dmxStyle={component:this});const s=t.modifiers.important?"important":"";this.$watch(t.value,(n=>{e.dmxStyle[t.argument]=n,null!=n&&e.style.setProperty(t.argument,n,s)}))})),dmx.Formatters("global",{json:function(e){return JSON.stringify(e)},log:function(e){return console.log(e),e},run:function(e,t){var s=dmx.DataScope({$param:t},this);dmx.Flow.run(e,s)}}),dmx.Actions({subflow(e){const t=this.parse(e.flow),s=this.parse(e.param);return this.parse(t+".runSub("+JSON.stringify(s)+")")},comment(e){dmx.debug&&console.debug(e.message)},wait(e){const t=this.parse(e.delay);if("number"!=typeof t)throw new Error("wait: Invalid delay");return new Promise((e=>{setTimeout(e,t)}))},now:e=>(new Date).toISOString(),random(e){let t=this.parse(e.lower),s=this.parse(e.upper),n=!!this.parse(e.floating);"number"==typeof t&&isFinite(t)||(t=0),"number"==typeof s&&isFinite(s)||(s=1);let r=t+Math.random()*(s-t);return n||Math.floor(t)!=t||Math.floor(s)!=s||(r=Math.round(r)),r},confirm(e){const t=this.parse(e.message);if("string"!=typeof t)throw new Error("confirm: Invalid message");const s=confirm(t);if(s){if(e.then)return this._exec(e.then).then((()=>s))}else if(e.else)return this._exec(e.else).then((()=>s));return s},prompt(e){const t=this.parse(e.message);if("string"!=typeof t)throw new Error("prompt: Invalid message");return prompt(t)},alert(e){const t=this.parse(e.message);if("string"!=typeof t)throw new Error("alert: Invalid message");return alert(t)},repeat(e){let t=dmx.clone(this.parse(e.repeat));if(!t)return;if("boolean"==typeof t)t=t?[0]:[];else if("string"==typeof t)t=t.split(/\s*,\s*/);else if("number"==typeof t){for(var s=[],n=0;n(this.scope=new dmx.DataScope(Object.assign({$value:t,$index:s,$name:s,$key:s,$number:s+1,$oddeven:s%2},t),r),this.output={},Array.isArray(e.outputFields)&&t instanceof Object&&e.outputFields.forEach((e=>{this.output[e]=t[e]})),this._exec(e.exec).then((()=>{var e=this.output;return this.scope=r,this.output=i,e})))))},condition(e){const t=!!this.parse(e.if);if(t){if(e.then)return this._exec(e.then).then((()=>t))}else if(e.else)return this._exec(e.else).then((()=>t));return t},conditions(e){if(Array.isArray(e.conditions))for(let t=0;t{var e=this.output;return self.output=t,e}))}return this._exec(e.exec)},while(e){const t=()=>new Promise((s=>{if(!this.parse(e.condition))return s();this._exec(e.exec).then(t).then(s)}));return t()},switch(e){const t=this.parse(e.expression);for(let s=0;sthis._exec(e.catch)))},run(e){if(!e.action)throw new Error("run: missing action");return this.parse(e.action)},runJS(e){if(!e.function)throw new Error("runJS: missing function");const t=this.parse(e.function),s=this.parse(e.args);return window[t].apply(null,s)},assign(e){return this.parse(e.value)},setGlobal(e){const t=this.parse(e.key),s=this.parse(e.value);if("string"!=typeof t)throw new Error("setGlobal: key must be a string");return dmx.global.set(t,s),s},setSession(e){const t=this.parse(e.key),s=this.parse(e.value);if("string"!=typeof t)throw new Error("setSession: key must be a string");return sessionStorage.setItem(t,JSON.stringify(s)),s},getSession(e){const t=this.parse(e.key);if("string"!=typeof t)throw new Error("getSession: key must be a string");return JSON.parse(sessionStorage.getItem(t))},removeSession(e){const t=this.parse(e.key);if("string"!=typeof t)throw new Error("removeSession: key must be a string");return sessionStorage.removeItem(t),!0},setStorage(e){const t=this.parse(e.key),s=this.parse(e.value);if("string"!=typeof t)throw new Error("setStorage: key must be a string");return localStorage.setItem(t,JSON.stringify(s)),s},getStorage(e){const t=this.parse(e.key);if("string"!=typeof t)throw new Error("getStorage: key must be a string");const s=localStorage.getItem(t);return null==s?null:JSON.parse(s)},removeStorage(e){const t=this.parse(e.key);if("string"!=typeof t)throw new Error("removeStorage: key must be a string");return localStorage.removeItem(t),!0},fetch(e){let t=this.parse(e.url),s=this.parse(e.method),n=this.parse(e.timeout),r=this.parse(e.dataType),i=this.parse(e.data),a=this.parse(e.params),o=this.parse(e.headers),d=this.parse(e.credentials),h=null;if("string"!=typeof t)throw new Error("fetch: invalid url "+t);if(["GET","POST","PUT","DELETE"].includes(s)||(s="GET"),["auto","json","text"].includes(r)||(r="auto"),"number"!=typeof n&&(n=0),o||(o={}),"object"==typeof a)for(var l in a)a.hasOwnProperty(l)&&null!=a[l]&&(t+=(-1!=t.indexOf("?")?"&":"?")+decodeURIComponent(l)+"="+decodeURIComponent(a[l]));if("GET"!=s)if("text"==r)o["Content-Type"]||(o["Content-Type"]="application/text"),h=i.toString();else if("json"==r)o["Content-Type"]||(o["Content-Type"]="application/json"),h=JSON.stringify(i);else if("POST"==s){if(h=new FormData,"object"==typeof i&&!Array.isArray(i))for(var c in i)if(i.hasOwnProperty(c)){var u=i[c];if(Array.isArray(u))for(var p in/\[\]$/.test(c)||(c+="[]"),u)u.hasOwnProperty(p)&&h.append(c,u[p]);else h.set(c,u)}}else i&&(o["Content-Type"]||(o["Content-Type"]="application/text"),h=i.toString());return new Promise(((e,r)=>{var i=new XMLHttpRequest;for(var a in i.onerror=r,i.onabort=r,i.ontimeout=r,i.onload=function(){var t=i.responseText,s=i.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((function(e,t){var s=t.split(": "),n=s.shift(),r=s.join(": ");return e[n.toLowerCase()]=r,e}),{});/^application\/json/.test(s["content-type"])&&(t=JSON.parse(t)),e({status:i.status,headers:s,data:t})},i.open(s,t),i.timeout=1e3*n,o)o.hasOwnProperty(a)&&i.setRequestHeader(a,o[a]);d&&(i.withCredentials=!0),i.send(h)}))}}),dmx.__actions.setValue=dmx.__actions.assign,dmx.__actions.api=dmx.__actions.fetch,dmx.__actions["api.send"]=dmx.__actions.fetch,dmx.__actions.serverConnect=dmx.__actions.fetch,dmx.Actions({"collections.addColumns":function(e){var t=this.parse(e.collection),s=e.add,n=!!this.parse(e.overwrite);if(!t.length)return[];for(var r=[],i=0,a=t.length;i {},\r\n isset: (v) => v !== undefined,\r\n array: (a) => a != null ? Array.from(a) : [],\r\n\r\n // Global Regexp\r\n reIgnoreElement: /^(script|style)$/i,\r\n rePrefixed: /^dmx-/i,\r\n reExpression: /\\{\\{(.+?)\\}\\}/,\r\n reExpressionReplace: /\\{\\{(.+?)\\}\\}/g,\r\n reToggleAttribute: /^(checked|selected|disabled|required|hidden|async|autofocus|autoplay|default|defer|multiple|muted|novalidate|open|readonly|reversed|scoped)$/i,\r\n reDashAlpha: /-([a-z])/g,\r\n reUppercase: /[A-Z]/g,\r\n \r\n // Internal collections for registering components etc.\r\n __components: Object.create(null),\r\n __attributes: {\r\n before: Object.create(null),\r\n mounted: Object.create(null),\r\n },\r\n __formatters: {\r\n boolean: Object.create(null),\r\n global: Object.create(null),\r\n string: Object.create(null),\r\n number: Object.create(null),\r\n object: Object.create(null),\r\n array: Object.create(null),\r\n any: Object.create(null),\r\n },\r\n __adapters: Object.create(null),\r\n __actions: Object.create(null),\r\n __startup: new Set(),\r\n};\r\n","// Polyfills for older browsers\r\n// IE is not supported\r\n\r\n// https://caniuse.com/element-closest\r\nif (window.Element && !('closest' in Element.prototype)) {\r\n Element.prototype.closest = function (s) {\r\n let matches = (this.document || this.ownerDocument).querySelectorAll(s),\r\n i,\r\n el = this;\r\n do {\r\n i = matches.length;\r\n while (--i >= 0 && matches.item(i) !== el) {};\r\n } while ((i < 0) && (el = el.parentElement));\r\n return el;\r\n };\r\n}\r\n\r\n// https://caniuse.com/mdn-api_nodelist_foreach\r\nif (window.NodeList && !('forEach' in NodeList.prototype)) {\r\n NodeList.prototype.forEach = Array.prototype.forEach;\r\n}\r\n\r\n// https://caniuse.com/mdn-api_queuemicrotask\r\nif (typeof window.queueMicrotask !== 'function') {\r\n window.queueMicrotask = function (callback) {\r\n Promise.resolve().then(callback).catch(e => setTimeout(() => { throw e }));\r\n }\r\n}\r\n\r\n// https://caniuse.com/mdn-api_node_isconnected\r\nif (window.Node && !('isConnected' in Node.prototype)) {\r\n Object.defineProperty(Node.prototype, 'isConnected', {\r\n get: function () { return document.contains(this) }\r\n });\r\n}\r\n\r\n// https://caniuse.com/mdn-api_element_toggleattribute\r\nif (window.Element && !('toggleAttribute' in Element.prototype)) {\r\n Element.prototype.toggleAttribute = function (name, force) {\r\n if (this.hasAttribute(name)) {\r\n if (force !== true) this.removeAttribute(name);\r\n } else {\r\n if (force !== false) this.setAttribute(name, '');\r\n }\r\n }\r\n}","(function() {\r\n\r\n // unsupported types: ImageBitmap\r\n\r\n var LARGE_ARRAY_SIZE = 200;\r\n\r\n var toString = Object.prototype.toString;\r\n var hasOwnProperty = Object.prototype.hasOwnProperty;\r\n\r\n var reFlags = /\\w*$/;\r\n var reTypedType = /^(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array$/;\r\n\r\n var eq = function(value, other) {\r\n return value === other || (value !== value && other !== other);\r\n }\r\n\r\n var assocIndexOf = function(array, key) {\r\n var length = array.length;\r\n while (length--) {\r\n if (eq(array[length][0], key)) {\r\n return length;\r\n }\r\n }\r\n return -1;\r\n };\r\n\r\n var isKeyable = function(value) {\r\n var type = typeof value;\r\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\r\n ? (value !== '__proto__')\r\n : (value === null);\r\n };\r\n\r\n var getMapData = function(data, key) {\r\n return isKeyable(key)\r\n ? data[typeof key == 'string' ? 'string' : 'hash']\r\n : data.map;\r\n };\r\n\r\n var Hash = function(entries) {\r\n var index = -1,\r\n length = entries == null ? 0 : entries.length;\r\n\r\n this.clear();\r\n while (++index < length) {\r\n var entry = entries[index];\r\n this.set(entry[0], entry[1]);\r\n }\r\n }\r\n\r\n Hash.prototype = {\r\n clear: function() {\r\n this.__data__ = Object.create(null);\r\n this.size = 0;\r\n },\r\n\r\n delete: function(key) {\r\n var result = this.has(key) && delete this.__data__[key];\r\n this.size -= result ? 1 : 0;\r\n return result;\r\n },\r\n\r\n get: function(key) {\r\n var result = this.__data__[key];\r\n return result === HASH_UNDEFINED ? undefined : result;\r\n },\r\n\r\n has: function(key) {\r\n return this.__data__[key] !== undefined;\r\n },\r\n\r\n set: function(key, value) {\r\n this.size += this.has(key) ? 0 : 1;\r\n this.__data__[key] = value === undefined ? HASH_UNDEFINED : value;\r\n return this;\r\n }\r\n }\r\n\r\n var ListCache = function(entries) {\r\n var index = -1;\r\n var length = entries == null ? 0 : entries.length;\r\n this.clear();\r\n while (++index < length) {\r\n var entry = entries[index];\r\n this.set(entry[0], entry[1]);\r\n }\r\n };\r\n\r\n ListCache.prototype = {\r\n clear: function() {\r\n this.__data__ = [];\r\n this.size = 0;\r\n },\r\n\r\n delete: function(key) {\r\n var data = this.__data__;\r\n var index = assocIndexOf(data, key);\r\n if (index < 0) {\r\n return false;\r\n }\r\n var lastIndex = data.length - 1;\r\n if (index == lastIndex) {\r\n data.pop();\r\n } else {\r\n data.splice(index, 1);\r\n }\r\n --this.size;\r\n return true;\r\n },\r\n\r\n get: function(key) {\r\n var data = this.__data__;\r\n var index = assocIndexOf(data, key);\r\n return index < 0 ? undefined : data[index][1];\r\n },\r\n\r\n has: function(key) {\r\n return assocIndexOf(this.__data__, key) > -1;\r\n },\r\n\r\n set: function(key, value) {\r\n var data = this.__data__;\r\n var index = assocIndexOf(data, key);\r\n if (index < 0) {\r\n ++this.size;\r\n data.push([key, value]);\r\n } else {\r\n data[index][1] = value;\r\n }\r\n return this;\r\n }\r\n };\r\n\r\n var MapCache = function(entries) {\r\n var index = -1;\r\n var length = entries == null ? 0 : entries.length;\r\n this.clear();\r\n while (++index < length) {\r\n var entry = entries[index];\r\n this.set(entry[0], entry[1]);\r\n }\r\n };\r\n\r\n MapCache.prototype = {\r\n clear: function() {\r\n this.size = 0;\r\n this.__data__ = {\r\n 'hash': new Hash(),\r\n 'map': new Map(),\r\n 'string': new Hash()\r\n };\r\n },\r\n\r\n delete: function(key) {\r\n var result = getMapData(this.__data__, key)['delete'](key);\r\n this.size -= result ? 1 : 0;\r\n return result;\r\n },\r\n\r\n get: function(key) {\r\n return getMapData(this.__data__, key).get(key);\r\n },\r\n\r\n has: function(key) {\r\n return getMapData(this.__data__, key).has(key);\r\n },\r\n\r\n set: function(key, value) {\r\n var data = getMapData(this.__data__, key);\r\n var size = data.size;\r\n data.set(key, value);\r\n this.size += data.size == size ? 0 : 1;\r\n return this;\r\n }\r\n };\r\n\r\n var Stack = function(entries) {\r\n var data = this.__data__ = new ListCache(entries);\r\n this.size = data.size;\r\n };\r\n\r\n Stack.prototype = {\r\n clear: function() {\r\n this.__data__ = new ListCache();\r\n this.size = 0;\r\n },\r\n\r\n delete: function(key) {\r\n var data = this.__data__;\r\n var result = data['delete'](key);\r\n this.size = data.size;\r\n return result;\r\n },\r\n\r\n get: function(key) {\r\n return this.__data__.get(key);\r\n },\r\n\r\n has: function(key) {\r\n return this.__data__.has(key);\r\n },\r\n\r\n set: function(key, value) {\r\n var data = this.__data__;\r\n if (data instanceof ListCache) {\r\n var pairs = data.__data__;\r\n if (pairs.length < LARGE_ARRAY_SIZE - 1) {\r\n pairs.push([key, value]);\r\n this.size = ++data.size;\r\n return this;\r\n }\r\n data = this.__data__ = new MapCache(pairs);\r\n }\r\n data.set(key, value);\r\n this.size = data.size;\r\n return this;\r\n }\r\n };\r\n\r\n var arrayEach = function(array, iteratee) {\r\n var index = -1;\r\n var length = array.length;\r\n while (++index < length) {\r\n if (iteratee(array[index], index, array) === false) {\r\n break;\r\n }\r\n }\r\n return array;\r\n };\r\n\r\n var getType = function(value) {\r\n return toString.call(value).slice(8, -1);\r\n };\r\n\r\n var isArray = function(value) {\r\n return Array.isArray(value);\r\n };\r\n\r\n var isObject = function(value) {\r\n return value != null && typeof value == 'object'\r\n };\r\n\r\n var isTypedArray = function(value) {\r\n return isObject(value) && reTypedType.test(getType(value));\r\n };\r\n\r\n var assignValue = function(object, key, value) {\r\n // Prototype polution protection\r\n if (key == '__proto__') return;\r\n\r\n var objValue = object[key];\r\n\r\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value))) {\r\n //if (value !== 0 || (1 / value) === (1 / objValue)) {\r\n object[key] = value;\r\n //}\r\n } else if (value === undefined && !(key in object)) {\r\n object[key] = value;\r\n }\r\n };\r\n\r\n var cloneArrayBuffer = function(arrayBuffer) {\r\n var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\r\n new Uint8Array(result).set(new Uint8Array(arrayBuffer));\r\n return result;\r\n };\r\n\r\n var cloneDataView = function(dataView) {\r\n var buffer = cloneArrayBuffer(dataView.buffer);\r\n return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\r\n };\r\n\r\n var cloneTypedArray = function(typedArray) {\r\n var buffer = cloneArrayBuffer(typedArray.buffer);\r\n return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\r\n };\r\n\r\n var cloneRegExp = function(regexp) {\r\n var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\r\n result.lastIndex = regexp.lastIndex;\r\n return result;\r\n };\r\n\r\n var cloneImageData = function(imageData) {\r\n var data = cloneTypedArray(imageData.data);\r\n return new imageData.constructor(data, imageData.width, imageData.height);\r\n }\r\n\r\n var initArray = function(array) {\r\n return new array.constructor(array.length);\r\n };\r\n\r\n var initType = function(object, type) {\r\n var Ctor = object.constructor;\r\n\r\n switch (type) {\r\n case 'ArrayBuffer':\r\n return cloneArrayBuffer();\r\n \r\n case 'Boolean':\r\n case 'Date':\r\n return new Ctor(+object);\r\n\r\n case 'DataView':\r\n return cloneDataView(object);\r\n\r\n case 'Float32Array':\r\n case 'Float64Array':\r\n case 'Int8Array':\r\n case 'Int16Array':\r\n case 'Int32Array':\r\n case 'Uint8Array':\r\n case 'Uint8ClampedArray':\r\n case 'Uint16Array':\r\n case 'Uint32Array':\r\n return cloneTypedArray(object);\r\n\r\n case 'Map':\r\n case 'Set':\r\n return new Ctor();\r\n\r\n case 'Number':\r\n case 'String':\r\n return new Ctor(object);\r\n\r\n case 'RegExp':\r\n return cloneRegExp(object);\r\n\r\n case 'ImageData':\r\n return cloneImageData(object);\r\n }\r\n };\r\n\r\n var clone = function(value, key, object, stack) {\r\n var result;\r\n\r\n if (!isObject(value)) {\r\n return value;\r\n }\r\n \r\n var type = getType(value);\r\n\r\n if (isArray(value)) {\r\n result = initArray(value);\r\n } else {\r\n if (type == 'Object') {\r\n result = {};\r\n } else {\r\n result = initType(value, type);\r\n }\r\n } \r\n \r\n stack = stack || new Stack();\r\n\r\n var stacked = stack.get(value);\r\n if (stacked) {\r\n return stacked;\r\n }\r\n stack.set(value, result);\r\n\r\n if (type == 'Map') {\r\n value.forEach(function(subValue, key) {\r\n result.set(key, clone(subValue, key, value, stack));\r\n });\r\n\r\n return result;\r\n }\r\n\r\n if (type == 'Set') {\r\n value.forEach(function(subValue) {\r\n result.add(clone(subValue, subValue, value, stack));\r\n });\r\n\r\n return result;\r\n }\r\n\r\n if (isTypedArray(value)) {\r\n return result;\r\n }\r\n\r\n var props = isArray(value) ? undefined : Object.keys(Object(value));\r\n arrayEach(props || value, function(subValue, key) {\r\n if (props) {\r\n key = subValue;\r\n subValue = value[key];\r\n }\r\n assignValue(result, key, clone(subValue, key, value, stack));\r\n });\r\n\r\n return result;\r\n };\r\n\r\n dmx.clone = clone;\r\n\r\n})();","(function() {\r\n\r\n // TODO: ImageData compare\r\n\r\n var hasOwnProperty = Object.prototype.hasOwnProperty;\r\n\r\n var reTypedType = /^(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array$/;\r\n\r\n var eq = function(value, other) {\r\n return value === other || (value !== value && other !== other);\r\n }\r\n\r\n var getType = function(value) {\r\n return toString.call(value).slice(8, -1);\r\n };\r\n\r\n var isObject = function(value) {\r\n return typeof value === 'object' && value !== null;\r\n };\r\n\r\n var isTypedArray = function(value) {\r\n return isObject(value) && reTypedType.test(getType(value));\r\n };\r\n\r\n var mapToArray = function(map) {\r\n var index = -1;\r\n var result = new Array(map.size);\r\n map.forEach(function(value, key) {\r\n result[++index] = [key, value];\r\n });\r\n return result;\r\n };\r\n\r\n var setToArray = function(set) {\r\n var index = -1;\r\n var result = new Array(set.size);\r\n set.forEach(function(value) {\r\n result[++index] = value;\r\n });\r\n return result;\r\n }\r\n\r\n var equalByType = function(object, other, type) {\r\n switch (type) {\r\n case 'DataView':\r\n if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) {\r\n return false;\r\n }\r\n object = object.buffer;\r\n other = other.buffer;\r\n\r\n case 'ArrayBuffer':\r\n if (object.byteLength != other.byteLength || !equal(new Uint8Array(object), new Uint8Array(other))) {\r\n return false;\r\n }\r\n return true;\r\n\r\n case 'Boolean':\r\n case 'Date':\r\n case 'Number':\r\n return eq(+object, +other);\r\n\r\n case 'RegExp':\r\n case 'String':\r\n return object == String(other);\r\n\r\n case 'Map':\r\n case 'Set':\r\n if (object.size != other.size) return false;\r\n return equalArrays(Array.from(object), Array.from(other));\r\n }\r\n };\r\n\r\n var equalArrays = function(array, other) {\r\n var arrLength = array.length;\r\n var othLength = other.length;\r\n\r\n if (arrLength != othLength) {\r\n return false;\r\n }\r\n\r\n var index = -1;\r\n\r\n while (++index < arrLength) {\r\n var arrValue = array[index];\r\n var othValue = other[index];\r\n\r\n if (!(arrValue === othValue || equal(arrValue, othValue))) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n };\r\n\r\n var equalObjects = function(object, other) {\r\n var objProps = Object.keys(object);\r\n var objLength = objProps.length;\r\n var othProps = Object.keys(other);\r\n var othLength = othProps.length;\r\n\r\n if (objLength != othLength) {\r\n return false;\r\n }\r\n\r\n var key, index = objLength;\r\n \r\n while (index--) {\r\n key = objProps[index];\r\n if (!hasOwnProperty.call(other, key)) {\r\n return false;\r\n }\r\n }\r\n\r\n var result = true;\r\n\r\n while (++index < objLength) {\r\n key = objProps[index];\r\n var objValue = object[key];\r\n var othValue = other[key];\r\n\r\n if (!(objValue === othValue || equal(objValue, othValue))) {\r\n result = false;\r\n break;\r\n }\r\n }\r\n\r\n if (result) {\r\n var objCtor = object.constructor;\r\n var othCtor = other.constructor;\r\n\r\n if (objCtor != othCtor &&\r\n ('constructor' in object && 'constructor' in other) &&\r\n !(typeof objCtor === 'function' && objCtor instanceof objCtor &&\r\n typeof othCtor === 'function' && othCtor instanceof othCtor)) {\r\n result = false;\r\n }\r\n }\r\n\r\n return result;\r\n };\r\n\r\n var equalDeep = function(object, other) {\r\n var objIsArr = Array.isArray(object);\r\n var othIsArr = Array.isArray(other);\r\n var objType = objIsArr ? 'Array' : getType(object);\r\n var othType = othIsArr ? 'Array' : getType(other);\r\n var objIsObj = objType == 'Object';\r\n var othIsObj = othType == 'Object';\r\n var isSameType = objType == othType;\r\n\r\n if (isSameType && !objIsObj) {\r\n return (objIsArr || isTypedArray(object))\r\n ? equalArrays(object, other)\r\n : equalByType(object, other, objType);\r\n }\r\n\r\n if (!isSameType) {\r\n return false;\r\n }\r\n\r\n return equalObjects(object, other);\r\n };\r\n\r\n var equal = function(value, other) {\r\n if (value === other) {\r\n return true;\r\n }\r\n if (value == null || other == null || (!isObject(value) && !isObject(other))) {\r\n return value !== value && other !== other;\r\n }\r\n return equalDeep(value, other);\r\n };\r\n\r\n dmx.equal = equal;\r\n\r\n})();","// Create Class\r\ndmx.createClass = (proto, parent) => {\r\n const Cls = function () {\r\n if (proto.constructor) {\r\n proto.constructor.apply(this, arguments);\r\n }\r\n };\r\n\r\n if (parent && parent.prototype) {\r\n Cls.prototype = Object.create(parent.prototype);\r\n }\r\n\r\n Object.assign(Cls.prototype, proto);\r\n\r\n Cls.prototype.constructor = Cls;\r\n\r\n return Cls;\r\n};\r\n\r\ndmx.ready = (fn) => {\r\n if (document.readyState === 'loading') {\r\n document.addEventListener('DOMContentLoaded', () => { fn() }, { once: true });\r\n } else {\r\n fn();\r\n }\r\n};\r\n\r\n// Config\r\ndmx.Config = (config) => {\r\n Object.assign(dmx.config, config)\r\n};\r\n\r\n// Create/get component\r\ndmx.Component = (name, proto) => {\r\n if (proto) {\r\n const parentComponent = proto.extends ? dmx.Component(proto.extends) : dmx.BaseComponent;\r\n \r\n if (typeof proto.initialData != 'function') {\r\n proto.initialData = Object.assign({}, parentComponent.prototype.initialData, proto.initialData);\r\n }\r\n proto.attributes = Object.assign({}, parentComponent.prototype.attributes, proto.attributes);\r\n proto.methods = Object.assign({}, parentComponent.prototype.methods, proto.methods);\r\n proto.events = Object.assign({}, parentComponent.prototype.events, proto.events);\r\n\r\n if (!proto.hasOwnProperty('constructor')) {\r\n proto.constructor = function(node, parent) {\r\n parentComponent.call(this, node, parent);\r\n }\r\n }\r\n\r\n proto.type = name;\r\n\r\n const Component = dmx.createClass(proto, parentComponent);\r\n Component.extends = proto.extends;\r\n\r\n dmx.__components[name] = Component;\r\n }\r\n\r\n return dmx.__components[name];\r\n};\r\n\r\n// Create attribute\r\ndmx.Attribute = (name, hook, fn) => {\r\n dmx.__attributes[hook][name] = fn;\r\n};\r\n\r\n// Create/get formatter(s)\r\ndmx.Formatter = (type, name, fn) => {\r\n dmx.__formatters[type][name] = fn;\r\n}\r\ndmx.Formatters = (type, formatters) => {\r\n for (const name in formatters) {\r\n dmx.Formatter(type, name, formatters[name]);\r\n }\r\n};\r\n\r\n// Create/get adapter\r\ndmx.Adapter = (type, name, fn) => {\r\n if (fn) dmx.__adapters[type][name] = fn;\r\n return dmx.__adapters[type][name];\r\n};\r\n\r\n// Create action(s)\r\ndmx.Action = (name, action) => {\r\n dmx.__actions[name] = action;\r\n};\r\ndmx.Actions = (actions) => {\r\n for (const name in actions) {\r\n dmx.Action(name, actions[name]);\r\n }\r\n};\r\n\r\n// Startup\r\ndmx.Startup = (promise) => {\r\n dmx.__startup.add(promise)\r\n};","dmx.debounce = (fn, delay) => {\r\n let handle;\r\n\r\n return function () {\r\n const cb = () => {\r\n fn.apply(this, arguments);\r\n };\r\n\r\n if (delay) {\r\n clearTimeout(handle);\r\n handle = setTimeout(cb, delay);\r\n } else {\r\n cancelAnimationFrame(handle);\r\n handle = requestAnimationFrame(cb);\r\n }\r\n };\r\n};\r\n\r\ndmx.throttle = (fn, delay) => {\r\n let throttle = false, args;\r\n\r\n return function () {\r\n args = Array.from(arguments);\r\n\r\n if (!throttle) {\r\n const cb = () => {\r\n throttle = false;\r\n if (args) fn.apply(this, args);\r\n };\r\n\r\n fn.apply(this, args);\r\n\r\n args = undefined;\r\n throttle = true;\r\n\r\n if (delay) {\r\n setTimeout(db, delay);\r\n } else {\r\n requestAnimationFrame(cb);\r\n }\r\n }\r\n };\r\n};\r\n\r\ndmx.keyCodes = {\r\n 'bs': 8,\r\n 'tab': 9,\r\n 'enter': 13,\r\n 'esc': 27,\r\n 'space': 32,\r\n 'left': 37,\r\n 'up': 38,\r\n 'right': 39,\r\n 'down': 40,\r\n 'delete': 46,\r\n\r\n 'backspace': 8,\r\n 'pause': 19,\r\n 'capslock': 20,\r\n 'escape': 27,\r\n 'pageup': 33,\r\n 'pagedown': 34,\r\n 'end': 35,\r\n 'home': 36,\r\n 'arrowleft': 37,\r\n 'arrowup': 38,\r\n 'arrowright': 39,\r\n 'arrowdown': 40,\r\n 'insert': 45,\r\n 'numlock': 144,\r\n 'scrolllock': 145,\r\n 'semicolon': 186,\r\n 'equal': 187,\r\n 'comma': 188,\r\n 'minus': 189,\r\n 'period': 190,\r\n 'slash': 191,\r\n 'backquote': 192,\r\n 'bracketleft': 219,\r\n 'backslash': 220,\r\n 'bracketright': 221,\r\n 'quote': 222,\r\n\r\n 'numpad0': 96,\r\n 'numpad1': 97,\r\n 'numpad2': 98,\r\n 'numpad3': 99,\r\n 'numpad4': 100,\r\n 'numpad5': 101,\r\n 'numpad6': 102,\r\n 'numpad7': 103,\r\n 'numpad8': 104,\r\n 'numpad9': 105,\r\n 'numpadmultiply': 106,\r\n 'numpadadd': 107,\r\n 'numpadsubstract': 109,\r\n 'numpaddivide': 111,\r\n\r\n 'f1': 112,\r\n 'f2': 113,\r\n 'f3': 114,\r\n 'f4': 115,\r\n 'f5': 116,\r\n 'f6': 117,\r\n 'f7': 118,\r\n 'f8': 119,\r\n 'f9': 120,\r\n 'f10': 121,\r\n 'f11': 122,\r\n 'f12': 123,\r\n\r\n 'digit0': 48,\r\n 'digit1': 49,\r\n 'digit2': 50,\r\n 'digit3': 51,\r\n 'digit4': 52,\r\n 'digit5': 53,\r\n 'digit6': 54,\r\n 'digit7': 55,\r\n 'digit8': 56,\r\n 'digit9': 57,\r\n\r\n 'keya': [65, 97],\r\n 'keyb': [66, 98],\r\n 'keyc': [67, 99],\r\n 'keyd': [68, 100],\r\n 'keye': [69, 101],\r\n 'keyf': [70, 102],\r\n 'keyg': [71, 103],\r\n 'keyh': [72, 104],\r\n 'keyi': [73, 105],\r\n 'keyj': [74, 106],\r\n 'keyk': [75, 107],\r\n 'keyl': [76, 108],\r\n 'keym': [77, 109],\r\n 'keyn': [78, 110],\r\n 'keyo': [79, 111],\r\n 'keyp': [80, 112],\r\n 'keyq': [81, 113],\r\n 'keyr': [82, 114],\r\n 'keys': [83, 115],\r\n 'keyt': [84, 116],\r\n 'keyu': [85, 117],\r\n 'keyv': [86, 118],\r\n 'keyw': [87, 119],\r\n 'keyx': [88, 120],\r\n 'keyy': [89, 121],\r\n 'keyz': [90, 122]\r\n};\r\n\r\ndmx.eventListener = function(target, eventType, handler, modifiers) {\r\n let timeout, throttle;\r\n \r\n const listener = function(event) {\r\n if (modifiers.self && event.target !== event.currentTarget) return;\r\n if (modifiers.ctrl && !event.ctrlKey) return;\r\n if (modifiers.alt && !event.altKey) return;\r\n if (modifiers.shift && !event.shiftKey) return;\r\n if (modifiers.meta && !event.metaKey) return;\r\n\r\n if ((event.originalEvent || event).nsp && !Object.keys(modifiers).includes((event.originalEvent || event).nsp)) {\r\n return;\r\n }\r\n\r\n if ((event.originalEvent || event) instanceof MouseEvent) {\r\n if (modifiers.button != null && event.button != (parseInt(modifiers.button, 10) || 0)) return;\r\n if (modifiers.button0 && event.button != 0) return;\r\n if (modifiers.button1 && event.button != 1) return;\r\n if (modifiers.button2 && event.button != 2) return;\r\n if (modifiers.button3 && event.button != 3) return;\r\n if (modifiers.button4 && event.button != 4) return;\r\n }\r\n\r\n if ((event.originalEvent || event) instanceof KeyboardEvent) {\r\n var keys = [];\r\n\r\n Object.keys(modifiers).forEach(function(key) {\r\n var keyVal = parseInt(key, 10);\r\n\r\n if (keyVal) {\r\n keys.push(keyVal);\r\n } else if (dmx.keyCodes[key]) {\r\n keys.push(dmx.keyCodes[key]);\r\n }\r\n });\r\n\r\n for (var i = 0; i < keys.length; i++) {\r\n if (Array.isArray(keys[i])) {\r\n if (!keys[i].includes(event.which)) return;\r\n } else if (event.which !== keys[i]) return;\r\n }\r\n }\r\n\r\n if (modifiers.stop) event.stopPropagation();\r\n if (modifiers.prevent) event.preventDefault();\r\n \r\n if (event.originalEvent) event = event.originalEvent;\r\n\r\n if (!event.$data) event.$data = {};\r\n\r\n if (event instanceof MouseEvent) {\r\n event.$data.altKey = event.altKey;\r\n event.$data.ctrlKey = event.ctrlKey;\r\n event.$data.metaKey = event.metaKey;\r\n event.$data.shiftKey = event.shiftKey;\r\n event.$data.pageX = event.pageX;\r\n event.$data.pageY = event.pageY;\r\n event.$data.x = event.x || event.clientX;\r\n event.$data.y = event.y || event.clientY;\r\n event.$data.button = event.button;\r\n }\r\n\r\n if (event instanceof WheelEvent) {\r\n event.$data.deltaX = event.deltaX;\r\n event.$data.deltaY = event.deltaY;\r\n event.$data.deltaZ = event.deltaZ;\r\n event.$data.deltaMode = event.deltaMode;\r\n }\r\n\r\n if (window.PointerEvent && event instanceof PointerEvent) {\r\n event.$data.pointerId = event.pointerId;\r\n event.$data.width = event.width;\r\n event.$data.height = event.height;\r\n event.$data.pressure = event.pressure;\r\n event.$data.tangentialPressure = event.tangentialPressure;\r\n event.$data.tiltX = event.tiltX;\r\n event.$data.tiltY = event.tiltY;\r\n event.$data.twist = event.twist;\r\n event.$data.pointerType = event.pointerType;\r\n event.$data.isPrimary = event.isPrimary;\r\n }\r\n\r\n if (window.TouchEvent && event instanceof TouchEvent) {\r\n const touchMap = touch => ({\r\n identifier: touch.identifier,\r\n screenX: touch.screenX,\r\n screenY: touch.screenY,\r\n clientX: touch.clientX,\r\n clientY: touch.clientY,\r\n pageX: touch.pageX,\r\n pageY: touch.pageY\r\n });\r\n\r\n event.$data.altKey = event.altKey;\r\n event.$data.ctrlKey = event.ctrlKey;\r\n event.$data.metaKey = event.metaKey;\r\n event.$data.shiftKey = event.shiftKey;\r\n event.$data.touches = Array.from(event.touches).map(touchMap);\r\n event.$data.changedTouches = Array.from(event.changedTouches).map(touchMap);\r\n event.$data.targetTouches = Array.from(event.targetTouches).map(touchMap);\r\n event.$data.rotation = event.rotation;\r\n event.$data.scale = event.scale;\r\n }\r\n\r\n if (event instanceof KeyboardEvent) {\r\n event.$data.altKey = event.altKey;\r\n event.$data.ctrlKey = event.ctrlKey;\r\n event.$data.metaKey = event.metaKey;\r\n event.$data.shiftKey = event.shiftKey;\r\n event.$data.location = event.location;\r\n event.$data.repeat = event.repeat;\r\n event.$data.code = event.code;\r\n event.$data.key = event.key;\r\n }\r\n\r\n if (modifiers.debounce) {\r\n clearTimeout(timeout);\r\n timeout = setTimeout(() => {\r\n handler.apply(this, arguments);\r\n }, parseInt(modifiers.debounce, 10) || 0);\r\n } else if (modifiers.throttle) {\r\n if (!throttle) {\r\n throttle = true;\r\n handler.apply(this, arguments);\r\n setTimeout(() => {\r\n throttle = false\r\n }, parseInt(modifiers.throttle, 10) || 0);\r\n }\r\n } else {\r\n return handler.apply(this, arguments);\r\n }\r\n };\r\n\r\n modifiers = modifiers || {};\r\n\r\n if (window.Dom7 && target.nodeType === 1) {\r\n Dom7(target)[modifiers.once ? 'once' : 'on'](eventType.replace(/-/g, '.'), listener, !!modifiers.capture);\r\n return () => Dom7(target).off(eventType.replace(/-/g, '.'), listener, !!modifiers.capture);\r\n } else if (window.jQuery && !modifiers.capture) {\r\n jQuery(target)[modifiers.once ? 'one' : 'on'](eventType.replace(/-/g, '.'), listener);\r\n return () => jQuery(target).off(eventType.replace(/-/g, '.'), listener);\r\n } else {\r\n target.addEventListener(eventType.replace(/-/g, '.'), listener, {\r\n capture: !!modifiers.capture,\r\n once: !!modifiers.once,\r\n passive: !!modifiers.passive\r\n });\r\n return () => target.removeEventListener(eventType.replace(/-/g, '.'), listener, !!modifiers.capture);\r\n }\r\n};\r\n","dmx.fileUtils = {\r\n\r\n fileReader (file, readAs) {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = () => resolve(reader.result);\r\n reader.onerror = () => reject(reader.error);\r\n reader[readAs](file);\r\n });\r\n },\r\n\r\n blobToArrayBuffer: function(blob) {\r\n return dmx.fileUtils.fileReader(blob, 'readAsArrayBuffer');\r\n },\r\n \r\n blobToBinaryString: function(blob) {\r\n return dmx.fileUtils.fileReader(blob, 'readAsBinaryString');\r\n },\r\n\r\n blobToDataURL: function(blob) {\r\n return dmx.fileUtils.fileReader(blob, 'readAsDataURL');\r\n },\r\n \r\n blobToBase64String: function(blob) {\r\n return dmx.fileUtils.fileReader(blob, 'readAsDataURL').then(dataURL =>\r\n dataURL.substring(dataURL.indexOf(',') + 1)\r\n );\r\n },\r\n\r\n arrayBufferToBlob: function(arrayBuffer, type) {\r\n return Promise.resolve(new Blob([arrayBuffer], { type }));\r\n },\r\n\r\n binaryStringToBlob: function(binary, type) {\r\n const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));\r\n return Promise.resolve(new Blob([bytes], { type }));\r\n }, \r\n \r\n dataURLToBlob: function(dataURL) {\r\n const { data, type } = dmx.fileUtils.parseDataURL(dataURL);\r\n return dmx.fileUtils.base64StringToBlob(data, type);\r\n },\r\n\r\n base64StringToBlob: function(base64String, type) {\r\n const binary = window.atob(base64String);\r\n return dmx.fileUtils.binaryStringToBlob(binary, type);\r\n },\r\n\r\n parseDataURL: function(dataURL) {\r\n const match = dataURL.match(/^data:(.*?)(;base64)?,(.*)$/);\r\n return {\r\n mediaType: match[1],\r\n base64: !!match[2],\r\n data: match[3],\r\n type: match[1].split(';')[0],\r\n };\r\n },\r\n\r\n parseMediaType: function(mediaType) {\r\n const match = mediaType.match(/^([^/]+)\\/([^+;]+)(?:\\+([^;]+))?(?:;(.*))?$/);\r\n return {\r\n type: match[1],\r\n subtype: match[2],\r\n suffix: match[3],\r\n parameters: match[4] ? match[4].split(';').reduce((obj, param) => {\r\n const [key, value] = param.split('=');\r\n obj[key] = value;\r\n return obj;\r\n }, {}) : {},\r\n };\r\n },\r\n\r\n};\r\n\r\n\r\n","// Trigger event on pushState and replaceState\r\n// https://stackoverflow.com/questions/5129386/how-to-detect-when-history-pushstate-and-history-replacestate-are-used/25673911#25673911\r\n{\r\n const _wr = function(type) {\r\n const orig = history[type];\r\n\r\n return function() {\r\n const rv = orig.apply(this, arguments);\r\n const e = new Event(type.toLowerCase());\r\n e.arguments = arguments;\r\n window.dispatchEvent(e);\r\n return rv;\r\n };\r\n };\r\n\r\n history.pushState = _wr('pushState');\r\n history.replaceState = _wr('replaceState');\r\n}\r\n\r\nwindow.onpopstate = function(e) {\r\n if (e.state && e.state.title) {\r\n document.title = e.state.title;\r\n }\r\n};\r\n\r\ndocument.documentElement.style.visibility = 'hidden';\r\n\r\ndmx.ready(() => {\r\n // First execute all startup scripts that are registered\r\n const startup = Promise.all(dmx.__startup);\r\n\r\n // Now we can start App Connect\r\n startup.then(() => {\r\n if (dmx.app) {\r\n throw Error('App already running!');\r\n }\r\n\r\n history.replaceState({ title: document.title }, '');\r\n\r\n const root = document.querySelector(':root[dmx-app], [dmx-app], :root[is=\"dmx-app\"], [is=\"dmx-app\"]');\r\n\r\n if (!root) {\r\n throw Error('App root not found!');\r\n }\r\n\r\n const App = dmx.Component('app');\r\n\r\n dmx.app = new App(root, dmx.global);\r\n\r\n document.documentElement.style.visibility = '';\r\n }).catch((err) => {\r\n // Something went wrong, log error and show page\r\n console.error(err);\r\n document.documentElement.style.visibility = '';\r\n })\r\n});\r\n\r\ndmx.extend = function () {\r\n // Variables\r\n var extended = {};\r\n var deep = false;\r\n var i = 0;\r\n var length = arguments.length;\r\n\r\n // Check if a deep merge\r\n if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {\r\n deep = arguments[0];\r\n i++;\r\n }\r\n\r\n // Merge the object into the extended object\r\n var merge = function (obj) {\r\n for ( var prop in obj ) {\r\n // Prototype polution protection\r\n if (prop == '__proto__') continue;\r\n\r\n if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {\r\n // If deep merge and property is an object, merge properties\r\n if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {\r\n extended[prop] = dmx.extend( true, extended[prop], obj[prop] );\r\n } else {\r\n if (obj[prop] != null) {\r\n extended[prop] = obj[prop];\r\n }\r\n }\r\n }\r\n }\r\n };\r\n\r\n // Loop through each object and conduct a merge\r\n for ( ; i < length; i++ ) {\r\n var obj = arguments[i];\r\n merge(obj);\r\n }\r\n\r\n return extended;\r\n};\r\n\r\ndmx.parseDate = function(obj) {\r\n if (typeof obj == 'string') {\r\n var d, struct, offset = 0, n = [1,4,5,6,7,10,11];\r\n\r\n if (obj.toLowerCase() == 'now') {\r\n return new Date();\r\n }\r\n\r\n if ((struct = /^(\\d{4}|[+\\-]\\d{6})(?:-(\\d{2})(?:-(\\d{2}))?)?(?:[T ](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:(Z)|([+\\-])(\\d{2})(?::(\\d{2}))?)?)?$/.exec(obj))) {\r\n for (var i = 0, k; (k = n[i]); ++i) {\r\n struct[k] = +struct[k] || 0;\r\n }\r\n\r\n struct[2] = (+struct[2] || 1) - 1;\r\n struct[3] = +struct[3] || 1;\r\n\r\n if (struct[8] === undefined) {\r\n return new Date(struct[1], struct[2], struct[3], struct[4], struct[5], struct[6], struct[7]);\r\n } else {\r\n if (struct[8] !== 'Z' && struct[9] !== undefined) {\r\n offset = struct[10] * 60 + struct[11];\r\n if (struct[9] === '+') offset = 0 - offset;\r\n }\r\n\r\n return new Date(Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + offset, struct[6], struct[7]));\r\n }\r\n } else if ((struct = /^(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:(Z)|([+\\-])(\\d{2})(?::(\\d{2}))?)?$/.exec(obj))) {\r\n var d = new Date();\r\n if (struct[5] === 'Z') {\r\n d.setUTCHours(+struct[1] || 0);\r\n d.setUTCMinutes(+struct[2] || 0);\r\n d.setUTCSeconds(+struct[3] || 0);\r\n d.setUTCMilliseconds(+struct[4] || 0);\r\n } else {\r\n d.setHours(+struct[1] || 0);\r\n d.setMinutes(+struct[2] || 0);\r\n d.setSeconds(+struct[3] || 0);\r\n d.setMilliseconds(+struct[4] || 0);\r\n }\r\n return d;\r\n }\r\n\r\n return new Date(obj);\r\n } else if (typeof obj == 'number') {\r\n return new Date(obj * 1000);\r\n } else {\r\n return new Date('');\r\n }\r\n};\r\n\r\ndmx.hashCode = function(o) {\r\n if (o == null) return 0;\r\n var str = JSON.stringify(o);\r\n var i, hash = 0;\r\n for (i = 0; i < str.length; i++) {\r\n hash = ((hash << 5) - hash) + str.charCodeAt(i);\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash);\r\n};\r\n\r\ndmx.randomizer = function(seed) {\r\n seed = +seed || 0;\r\n return function() {\r\n seed = (seed * 9301 + 49297) % 233280;\r\n return seed / 233280;\r\n };\r\n};\r\n\r\ndmx.repeatItems = function(repeat) {\r\n const items = [];\r\n\r\n if (repeat) {\r\n if (typeof repeat == 'object') {\r\n if (Array.isArray(repeat)) {\r\n for (let i = 0, l = repeat.length; i < l; i++) {\r\n const item = dmx.clone(repeat[i]);\r\n items.push(Object.assign({}, item, {\r\n $key: i,\r\n $index: i,\r\n $value: item\r\n }));\r\n }\r\n } else {\r\n let i = 0;\r\n for (const key in repeat) {\r\n if (repeat.hasOwnProperty(key)) {\r\n const item = dmx.clone(repeat[key]);\r\n items.push(Object.assign({}, item, {\r\n $key: key,\r\n $index: i,\r\n $value: item\r\n }));\r\n i++;\r\n }\r\n }\r\n }\r\n } else if (typeof repeat == 'number') {\r\n for (let n = 0; n < repeat; n++) {\r\n items.push({\r\n $key: String(n),\r\n $index: n,\r\n $value: n + 1\r\n });\r\n }\r\n }\r\n }\r\n\r\n return items;\r\n};\r\n\r\ndmx.escapeRegExp = function(val) {\r\n // https://github.com/benjamingr/RegExp.escape\r\n return val.replace(/[\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\r\n};\r\n\r\ndmx.validate = function(node) {\r\n if (node.tagName == 'FORM') {\r\n Array.from(node.elements).forEach(node => node.dirty = true);\r\n }\r\n\r\n return node.checkValidity();\r\n};\r\n\r\ndmx.validateReset = function(node) {\r\n // reset validation?\r\n};\r\n\r\n(() => {\r\n const queue = [];\r\n\r\n window.addEventListener('message', event => {\r\n if (event.source === window && event.data === 'dmxNextTick' && queue.length) {\r\n event.stopPropagation();\r\n while (queue.length) {\r\n const task = queue.shift();\r\n task.fn.call(task.context);\r\n };\r\n }\r\n }, true);\r\n\r\n dmx.nextTick = (fn, context) => {\r\n queue.push({ fn, context });\r\n window.postMessage('dmxNextTick', '*');\r\n }\r\n})();\r\n\r\ndmx.requestUpdate = function() {\r\n console.warn('dmx.requestUpdate is deprecated.');\r\n};\r\n","// cordova init\r\n\r\nif (document.location.protocol == 'app:') { // cordova detection by checking protocol\r\n dmx.Startup(new Promise(resolve => document.addEventListener('deviceready', resolve)));\r\n}","(() => {\r\n /*! (c) Andrea Giammarchi */\r\n \r\n const {is} = Object;\r\n \r\n let batches;\r\n \r\n /**\r\n * Execute a callback that will not side-effect until its top-most batch is\r\n * completed.\r\n * @param {() => void} callback a function that batches changes to be notified\r\n * through signals.\r\n */\r\n const batch = callback => {\r\n const prev = batches;\r\n batches = prev || [];\r\n try {\r\n callback();\r\n if (!prev)\r\n for (const {value} of batches);\r\n }\r\n finally { batches = prev }\r\n };\r\n dmx.batch = batch;\r\n \r\n /**\r\n * A signal with a value property also exposed via toJSON, toString and valueOf.\r\n * When created via computed, the `value` property is **readonly**.\r\n * @template T\r\n */\r\n class Signal {\r\n constructor(value) {\r\n this._ = value;\r\n }\r\n \r\n /** @returns {T} */\r\n toJSON() { return this.value }\r\n \r\n /** @returns {string} */\r\n toString() { return String(this.value) }\r\n \r\n /** @returns {T} */\r\n valueOf() { return this.value }\r\n }\r\n dmx.Signal = Signal\r\n \r\n let computedSignal;\r\n /**\r\n * @template T\r\n * @extends {Signal}\r\n */\r\n class Computed extends Signal {\r\n /**\r\n * @private\r\n * @type{Reactive}\r\n */\r\n s\r\n /**\r\n * @param {(v: T) => T} _ \r\n * @param {T} v \r\n * @param {{ equals?: Equals }} o\r\n * @param {boolean} f \r\n */\r\n constructor(_, v, o, f) {\r\n super(_);\r\n this.f = f; // is effect?\r\n this.$ = true; // should update (\"value for money\")\r\n this.r = new Set; // related signals\r\n this.s = new Reactive(v, o); // signal\r\n }\r\n peek() { return this.s.peek() }\r\n get value() {\r\n if (this.$) {\r\n const prev = computedSignal;\r\n computedSignal = this;\r\n try { this.s.value = this._(this.s._) }\r\n finally {\r\n this.$ = false;\r\n computedSignal = prev;\r\n }\r\n }\r\n return this.s.value;\r\n }\r\n }\r\n \r\n const defaults = {async: false, equals: true};\r\n \r\n /**\r\n * Returns a read-only Signal that is invoked only when any of the internally\r\n * used signals, as in within the callback, is unknown or updated.\r\n * @type {(fn: (v: T) => R, value?: V, options?: { equals?: Equals }) => ComputedSignal}\r\n */\r\n const computed = (fn, value, options = defaults) =>\r\n new Computed(fn, value, options, false);\r\n dmx.computed = computed;\r\n \r\n let outerEffect;\r\n const empty = [];\r\n const noop = () => {};\r\n const dispose = ({s}) => {\r\n if (typeof s._ === 'function')\r\n s._ = s._();\r\n };\r\n \r\n class FX extends Computed {\r\n constructor(_, v, o) {\r\n super(_, v, o, true);\r\n this.e = empty;\r\n }\r\n run() {\r\n this.$ = true;\r\n this.value;\r\n return this;\r\n }\r\n stop() {\r\n this._ = noop;\r\n for (const s of this.r) {\r\n // remove computed from related signals\r\n s.c.delete(this);\r\n }\r\n this.r.clear();\r\n this.s.c.clear();\r\n }\r\n }\r\n dmx.FX = FX\r\n \r\n class Effect extends FX {\r\n constructor(_, v, o) {\r\n super(_, v, o);\r\n this.i = 0; // index\r\n this.a = !!o.async; // async\r\n this.m = true; // microtask\r\n this.e = []; // effects\r\n // \"I am effects\" ^_^;;\r\n }\r\n get value() {\r\n this.a ? this.async() : this.sync();\r\n }\r\n async() {\r\n if (this.m) {\r\n this.m = false;\r\n queueMicrotask(() => {\r\n this.m = true;\r\n this.sync();\r\n });\r\n }\r\n }\r\n sync() {\r\n const prev = outerEffect;\r\n (outerEffect = this).i = 0;\r\n dispose(this);\r\n super.value;\r\n outerEffect = prev;\r\n }\r\n stop() {\r\n super.stop();\r\n dispose(this);\r\n for (const effect of this.e.splice(0))\r\n effect.stop();\r\n }\r\n }\r\n dmx.Effect = Effect\r\n \r\n /**\r\n * Invokes a function when any of its internal signals or computed values change.\r\n * \r\n * Returns a dispose callback.\r\n * @template T\r\n * @type {(fn: (v: T) => T, value?: T, options?: { async?: boolean }) => () => void}\r\n */\r\n const effect = (callback, value, options = defaults) => {\r\n let unique;\r\n if (outerEffect) {\r\n const {i, e} = outerEffect;\r\n const isNew = i === e.length;\r\n // bottleneck:\r\n // there's literally no way to optimize this path *unless* the callback is\r\n // already a known one. however, latter case is not really common code so\r\n // the question is: should I optimize this more than this? 'cause I don't\r\n // think the amount of code needed to understand if a callback is *likely*\r\n // the same as before makes any sense + correctness would be trashed.\r\n if (isNew || e[i]._ !== callback) {\r\n if (!isNew) e[i].stop();\r\n e[i] = new Effect(callback, value, options).run();\r\n }\r\n unique = e[i];\r\n outerEffect.i++;\r\n }\r\n else\r\n unique = new Effect(callback, value, options).run();\r\n return () => { unique.stop() };\r\n };\r\n dmx.effect = effect;\r\n \r\n const skip = () => false;\r\n /**\r\n * @template T\r\n * @extends {Signal}\r\n */\r\n class Reactive extends Signal {\r\n constructor(_, {equals}) {\r\n super(_)\r\n this.c = new Set; // computeds\r\n this.s = equals === true ? is : (equals || skip); // (don't) skip updates\r\n }\r\n /**\r\n * Allows to get signal.value without subscribing to updates in an effect\r\n * @returns {T}\r\n */\r\n peek() { return this._ }\r\n /** @returns {T} */\r\n get value() {\r\n if (computedSignal) {\r\n this.c.add(computedSignal);\r\n computedSignal.r.add(this);\r\n }\r\n return this._;\r\n }\r\n set value(_) {\r\n const prev = this._;\r\n if (!this.s((this._ = _), prev)) {\r\n if (this.c.size) {\r\n const effects = [];\r\n const stack = [this];\r\n for (const signal of stack) {\r\n for (const computed of signal.c) {\r\n if (!computed.$ && computed.r.has(signal)) {\r\n computed.r.clear();\r\n computed.$ = true;\r\n if (computed.f) {\r\n effects.push(computed);\r\n const stack = [computed];\r\n for (const c of stack) {\r\n for (const effect of c.e) {\r\n effect.r.clear();\r\n effect.$ = true;\r\n stack.push(effect);\r\n }\r\n }\r\n }\r\n else\r\n stack.push(computed.s);\r\n }\r\n }\r\n }\r\n for (const effect of effects)\r\n batches ? batches.push(effect) : effect.value;\r\n }\r\n }\r\n }\r\n }\r\n \r\n /**\r\n * Returns a writable Signal that side-effects whenever its value gets updated.\r\n * @template T\r\n * @type {(initialValue: T, options?: { equals?: Equals }) => ReactiveSignal}\r\n */\r\n const signal = (value, options = defaults) => new Reactive(value, options);\r\n dmx.signal = signal;\r\n \r\n /**\r\n * @template [T=any]\r\n * @typedef {boolean | ((prev: T, next: T) => boolean)} Equals\r\n */\r\n \r\n /**\r\n * @public\r\n * @template T\r\n * @typedef {Omit, '_'|'s'|'c'>} ReactiveSignal\r\n */\r\n \r\n /**\r\n * @public\r\n * @template T\r\n * @typedef {Omit, '$'|'s'|'f'|'r'|'_'>} ComputedSignal\r\n */\r\n })();","dmx.signalProxy = function (o = {}) {\r\n const signals = new Map();\r\n const equals = (a, b) => {\r\n return dmx.equal(a, b);\r\n };\r\n\r\n return new Proxy(o, {\r\n has (target, prop) {\r\n // when checking for a prop, always return true\r\n return true;\r\n },\r\n\r\n get (target, prop, receiver) {\r\n const value = Reflect.get(target, prop, receiver);\r\n\r\n if (typeof value == 'function' || typeof prop != 'string' || prop.startsWith('_')) {\r\n // ignore private props\r\n return value;\r\n }\r\n\r\n if (!signals.has(prop)) {\r\n signals.set(prop, dmx.signal(value, { equals }));\r\n }\r\n\r\n return signals.get(prop).value;\r\n },\r\n\r\n set (target, prop, value, receiver) {\r\n const ok = Reflect.set(target, prop, value, receiver);\r\n\r\n if (ok) {\r\n if (signals.has(prop)) {\r\n signals.get(prop).value = value;\r\n }\r\n }\r\n\r\n return ok;\r\n },\r\n\r\n deleteProperty (target, prop) {\r\n const ok = Reflect.deleteProperty(target, prop);\r\n\r\n if (ok && signals.has(prop)) {\r\n signals.get(prop).value = undefined;\r\n }\r\n\r\n return ok;\r\n }\r\n });\r\n};","(() => {\r\n\r\n class Scope {\r\n\r\n constructor (initialData = {}, parent = null) {\r\n if (typeof initialData !== 'object') {\r\n initialData = { $value: initialData };\r\n }\r\n\r\n this.data = dmx.signalProxy();\r\n Object.assign(this.data, initialData);\r\n this.parent = parent;\r\n this.seed = Math.random();\r\n }\r\n\r\n get (name) {\r\n if (this.data[name] !== undefined) {\r\n return this.data[name];\r\n }\r\n\r\n if (this.parent) {\r\n if (name == 'parent') {\r\n return this.parent.data;\r\n }\r\n\r\n return this.parent.get(name);\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n set (name, value) {\r\n if (typeof name === 'object') {\r\n dmx.batch(() => {\r\n for (var prop in name) {\r\n if (name.hasOwnProperty(prop)) {\r\n this.set(prop, name[prop]);\r\n }\r\n }\r\n });\r\n } else {\r\n this.data[name] = value;\r\n }\r\n }\r\n\r\n del (name) {\r\n delete this.data[name];\r\n }\r\n\r\n }\r\n\r\n dmx.global = new Scope();\r\n dmx.DataScope = function (data, parent) {\r\n return new Scope(data, parent || dmx.global);\r\n };\r\n\r\n})();","(function() {\r\n\r\n var $ = function(selector) {\r\n if (!(this instanceof $)) {\r\n return new $(selector);\r\n }\r\n if (selector instanceof $) {\r\n return selector;\r\n }\r\n if (!selector) return this;\r\n var len = selector.length;\r\n if (selector.nodeType) {\r\n this[0] = selector;\r\n this.length = 1;\r\n } else if (typeof selector == 'string') {\r\n return $(document.querySelectorAll(selector));\r\n } else if (len) {\r\n for (var i = 0; i < len; i++) {\r\n if (selector[i] && selector[i].nodeType) {\r\n this[this.length] = selector[i];\r\n this.length++;\r\n }\r\n }\r\n }\r\n return this;\r\n };\r\n\r\n $.prototype = {\r\n constructor: $,\r\n length: 0,\r\n\r\n addClass: function(className) {\r\n for (var i = 0; i < this.length; i++) {\r\n this[i].classList.add(className);\r\n }\r\n return this;\r\n },\r\n\r\n removeClass: function(className) {\r\n for (var i = 0; i < this.length; i++) {\r\n this[i].classList.remove(className);\r\n }\r\n return this;\r\n },\r\n\r\n toggleClass: function(className) {\r\n for (var i = 0; i < this.length; i++) {\r\n this[i].classList.toggle(className);\r\n }\r\n return this;\r\n },\r\n\r\n hasClass: function(className) {\r\n if (!this[0]) return false;\r\n return this[0].classList.contains(className);\r\n },\r\n\r\n attr: function(attrs, value) {\r\n if (arguments.length === 1 && typeof attrs === 'string') {\r\n return this[0] && this[0].getAttribute(attrs);\r\n } else {\r\n for (var i = 0; i < this.length; i++) {\r\n if (arguments.length === 2) {\r\n this[i].setAttribute(attrs, value);\r\n } else {\r\n for (var attr in attrs) {\r\n if (attrs.hasOwnProperty(attr)) {\r\n this[i].setAttribute(attr, attrs[attr]);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n return this;\r\n },\r\n\r\n removeAttr: function(attr) {\r\n for (var i = 0; i < this.length; i++) {\r\n this[i].removeAttribute(attr);\r\n }\r\n return this;\r\n },\r\n\r\n prop: function(props, value) {\r\n if (arguments.length === 1 && typeof props === 'string') {\r\n return this[0] && this[0][props];\r\n } else {\r\n for (var i = 0; i < this.length; i++) {\r\n if (arguments.length === 2) {\r\n this[i][props] = value;\r\n } else {\r\n for (var prop in props) {\r\n if (props.hasOwnProperty(prop)) {\r\n this[i][prop] = props[prop];\r\n }\r\n }\r\n }\r\n }\r\n }\r\n return this;\r\n },\r\n\r\n css: function(props, value) {\r\n if (arguments.length === 1 && typeof props === 'string') {\r\n return this[0] && window.getComputedStyle(this[0], null).getPropertyValue(props);\r\n } else {\r\n for (var i = 0; i < this.length; i++) {\r\n if (arguments.length === 2) {\r\n this[i].style.setProperty(props, value);\r\n } else {\r\n for (var prop in props) {\r\n if (props.hasOwnProperty(prop)) {\r\n this[i].style.setProperty(prop, props[prop]);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n return this;\r\n },\r\n\r\n each: function(callback, context) {\r\n if (!callback) return this;\r\n for (var i = 0; i < this.length; i++) {\r\n if (callback.call(context || this[i], i, this[i]) === false) {\r\n return this;\r\n }\r\n }\r\n return this;\r\n },\r\n\r\n append: function() {\r\n for (var i = 0; i < arguments.length; i++) {\r\n var children = $(arguments[i]);\r\n\r\n for (var j = 0; j < children.length; j++) {\r\n this[0].appendChild(children[j]);\r\n }\r\n }\r\n return this;\r\n },\r\n\r\n appendTo: function(parent) {\r\n $(parent).append(this);\r\n return this;\r\n },\r\n\r\n detach: function() {\r\n for (var i = 0; i < this.length; i++) {\r\n if (this[i].parentNode) {\r\n this[i].parentNode.removeChild(this[i]);\r\n }\r\n }\r\n return this;\r\n },\r\n\r\n empty: function() {\r\n for (var i = 0; i < this.length; i++) {\r\n this[i].innerHTML = '';\r\n }\r\n return this;\r\n }\r\n };\r\n\r\n dmx.dom = {\r\n\r\n get: function(id) {\r\n return $(document.getElementById(id));\r\n },\r\n\r\n select: function(query) {\r\n return $(query);\r\n },\r\n\r\n create: function(tagName) {\r\n var elem = document.createElement(tagName);\r\n return $(elem);\r\n },\r\n\r\n contains: function(node) {\r\n return document.documentElement.contains(node);\r\n },\r\n\r\n walk: function(node, fn, context) {\r\n if (node) {\r\n if (fn.call(context, node) === false) {\r\n // stop going deeper when callback returns false\r\n return;\r\n } else if (node.hasChildNodes()) {\r\n for (const child of Array.from(node.childNodes)) {\r\n dmx.dom.walk(child, fn, context);\r\n }\r\n }\r\n }\r\n },\r\n\r\n getAttributes: function(node) {\r\n var attributes = [];\r\n\r\n if (node.nodeType == 1) {\r\n for (var i = 0; i < node.attributes.length; i++) {\r\n var attribute = node.attributes[i];\r\n\r\n if (attribute && attribute.specified && dmx.rePrefixed.test(attribute.name)) {\r\n var name = attribute.name.substr(4);\r\n var argument = null;\r\n var modifiers = {};\r\n\r\n name.split('.').forEach(function(part, i) {\r\n if (i === 0) {\r\n name = part;\r\n } else {\r\n var pos = part.indexOf(':');\r\n if (pos > 0) {\r\n modifiers[part.substr(0, pos)] = part.substr(pos + 1);\r\n } else {\r\n modifiers[part] = true;\r\n }\r\n }\r\n });\r\n\r\n var pos = name.indexOf(':');\r\n if (pos > 0) {\r\n argument = name.substr(pos + 1);\r\n name = name.substr(0, pos);\r\n }\r\n\r\n attributes.push({\r\n name: name,\r\n fullName: attribute.name,\r\n value: attribute.value,\r\n argument: argument,\r\n modifiers: modifiers\r\n });\r\n }\r\n }\r\n }\r\n\r\n return attributes;\r\n },\r\n\r\n remove: function(node) {\r\n if (Array.isArray(node)) {\r\n node.forEach(function(node) {\r\n dmx.dom.remove(node);\r\n });\r\n } else {\r\n node.remove()\r\n }\r\n },\r\n\r\n replace: function(oldNode, newNode) {\r\n if (oldNode.parentNode) {\r\n oldNode.parentNode.replaceChild(newNode, oldNode);\r\n }\r\n }\r\n\r\n };\r\n\r\n})();\r\n","dmx._CACHE = new Map();\r\n\r\ndmx._OPERATORS = new Map([\r\n ['{', 'L_CURLY'],\r\n ['}', 'R_CURLY'],\r\n ['[', 'L_BRACKET'],\r\n [']', 'R_BRACKET'],\r\n ['(', 'L_PAREN'],\r\n [')', 'R_PAREN'],\r\n ['.', 'PERIOD'],\r\n [',', 'COMMA'],\r\n [';', 'SEMI'], // not used\r\n [':', 'COLON'],\r\n ['?', 'QUESTION'],\r\n // Arithmetic operators\r\n ['-', 'ADDICTIVE'],\r\n ['+', 'ADDICTIVE'],\r\n ['*', 'MULTIPLICATIVE'],\r\n ['/', 'MULTIPLICATIVE'],\r\n ['%', 'MULTIPLICATIVE'],\r\n // Comparison operators\r\n ['===', 'EQUALITY'],\r\n ['!==', 'EQUALITY'],\r\n ['==', 'EQUALITY'],\r\n ['!=', 'EQUALITY'],\r\n ['<', 'RELATIONAL'],\r\n ['>', 'RELATIONAL'],\r\n ['<=', 'RELATIONAL'],\r\n ['>=', 'RELATIONAL'],\r\n ['in', 'RELATIONAL'],\r\n // Logical operators\r\n ['&&', 'LOGICAL_AND'],\r\n ['||', 'LOGICAL_OR'],\r\n ['!', 'LOGICAL_NOT'],\r\n // Bitwise operators\r\n ['&', 'BITWISE_AND'],\r\n ['|', 'BITWISE_OR'],\r\n ['^', 'BITWISE_XOR'],\r\n ['~', 'BITWISE_NOT'],\r\n ['<<', 'BITWISE_SHIFT'],\r\n ['>>', 'BITWISE_SHIFT'],\r\n ['>>>', 'BITWISE_SHIFT'],\r\n]);\r\n\r\ndmx._ESCAPE_CHARS = new Map([\r\n ['n', '\\n'],\r\n ['r', '\\r'],\r\n ['t', '\\t'],\r\n ['b', '\\b'],\r\n ['f', '\\f'],\r\n ['v', '\\v'],\r\n ['0', '\\0'],\r\n [\"'\", \"'\"],\r\n ['`', '`'],\r\n ['\"', '\"'],\r\n]);\r\n\r\ndmx._EXPRESSIONS = new Map([\r\n ['**', (a, b) => Math.pow(a(), b())],\r\n ['??', (a, b) => (a = a(), a == null ? b() : a)],\r\n ['in', (a, b) => a() in b()],\r\n ['?', (a, b, c) => (a() ? b() : c())],\r\n ['+', (a, b) => (a = a(), b = b(), a == null ? b : b == null ? a : a + b)],\r\n ['-', (a, b) => a() - b()],\r\n ['*', (a, b) => a() * b()],\t\r\n ['/', (a, b) => a() / b()],\r\n ['%', (a, b) => a() % b()],\r\n ['===', (a, b) => a() === b()],\r\n ['!==', (a, b) => a() !== b()],\r\n ['==', (a, b) => a() == b()],\r\n ['!=', (a, b) => a() != b()],\r\n ['<', (a, b) => a() < b()],\r\n ['>', (a, b) => a() > b()],\r\n ['<=', (a, b) => a() <= b()],\r\n ['>=', (a, b) => a() >= b()],\r\n ['&&', (a, b) => a() && b()],\r\n ['||', (a, b) => a() || b()],\r\n ['&', (a, b) => a() & b()],\r\n ['|', (a, b) => a() | b()],\r\n ['^', (a, b) => a() ^ b()],\r\n ['<<', (a, b) => a() << b()],\r\n ['>>', (a, b) => a() >> b()],\r\n ['>>>', (a, b) => a() >>> b()],\r\n ['~', (a) => ~a()],\r\n ['!', (a) => !a()],\r\n]);\r\n\r\ndmx._RESERVED = new Map([\r\n ['this', (scope) => () => scope.data],\r\n ['true', () => () => true],\r\n ['false', () => () => false],\r\n ['null', () => () => null],\r\n ['undefined', () => () => undefined],\r\n ['_', () => () => ({ __dmxScope__: true })],\r\n]);\r\n\r\ndmx._SUPPORTED_TYPES = new Map([\r\n ['Boolean', 'boolean'],\r\n ['Null', 'null'],\r\n ['Undefined', 'undefined'],\r\n ['Number', 'number'],\r\n ['BigInt', 'number'],\r\n ['Decimal', 'number'], // requires Decimal.js\r\n ['String', 'string'],\r\n ['Date', 'date'],\r\n ['RegExp', 'regexp'],\r\n ['Blob', 'blob'],\r\n ['File', 'file'],\r\n ['FileList', 'filelist'],\r\n ['ArrayBuffer', 'arraybuffer'],\r\n ['ImageBitmap', 'imagebitmap'],\r\n ['ImageData', 'imagedata'],\r\n ['Array', 'array'],\r\n ['Object', 'object'],\r\n ['Map', 'map'],\r\n ['Set', 'set'],\r\n ['DataView', 'array'],\r\n ['Int8Array', 'array'],\r\n ['Uint8Array', 'array'],\r\n ['Uint8ClampedArray', 'array'],\r\n ['Int16Array', 'array'],\r\n ['Uint16Array', 'array'],\r\n ['Int32Array', 'array'],\r\n ['Uint32Array', 'array'],\r\n ['Float32Array', 'array'],\r\n ['Float64Array', 'array'],\r\n ['BigInt64Array', 'array'],\r\n ['BigUint64Array', 'array'],\r\n]);\r\n\r\ndmx.getType = function (obj) {\r\n return dmx._SUPPORTED_TYPES.get(Object.prototype.toString.call(obj).slice(8, -1));\r\n};\r\n\r\ndmx.lexer = function (expression) {\r\n if (dmx._CACHE.has(expression)) {\r\n return dmx._CACHE.get(expression);\r\n }\r\n\r\n let tokens = [], token, name, start, index = 0, op = true, ch, ch2, ch3;\r\n\r\n while (index < expression.length) {\r\n start = index;\r\n\r\n ch = read();\r\n\r\n if (isQuote(ch)) {\r\n name = 'STRING';\r\n token = readString(ch);\r\n op = false;\r\n } else if ((isDigid(ch) || (is('.') && peek() && isDigid(peek()))) && op) {\r\n name = 'NUMBER';\r\n token = readNumber();\r\n op = false;\r\n } else if (isAlpha(ch) && op) {\r\n name = 'IDENT';\r\n token = readIdent();\r\n if (is('(')) {\r\n name = 'METHOD';\r\n }\r\n op = false;\r\n } else if (is('/') && op && (token == '(' || token == ',' || token == '?' || token == ':') && testRegexp()) {\r\n name = 'REGEXP';\r\n token = readRegexp();\r\n op = false;\r\n } else if (isWhitespace(ch)) {\r\n index++;\r\n continue;\r\n } else if ((ch3 = read(3)) && dmx._OPERATORS.has(ch3)) {\r\n name = dmx._OPERATORS.get(ch3);\r\n token = ch3;\r\n op = true;\r\n index += 3;\r\n } else if ((ch2 = read(2)) && dmx._OPERATORS.has(ch2)) {\r\n name = dmx._OPERATORS.get(ch2);\r\n token = ch2;\r\n op = true;\r\n index += 2;\r\n } else if (dmx._OPERATORS.has(ch)) {\r\n name = dmx._OPERATORS.get(ch);\r\n token = ch;\r\n op = true;\r\n index++;\r\n } else {\r\n throw new Error(`Unexpected token \"${ch}\" at index ${index} in expression: ${expression}`);\r\n }\r\n\r\n tokens.push({ name, index: start, value: token });\r\n }\r\n\r\n dmx._CACHE.set(expression, tokens);\r\n\r\n return tokens;\r\n\r\n function read (n) {\r\n return n > 1 ? expression.slice(index, index + n) : expression[index];\r\n }\r\n\r\n function peek (n = 1) {\r\n return index + n < expression.length ? expression[index + n] : false;\r\n }\r\n\r\n function is (chars) {\r\n return chars.includes(ch);\r\n }\r\n\r\n function isQuote (ch) {\r\n return ch == '\"' || ch == \"'\" || ch == '`';\r\n }\r\n\r\n function isDigid (ch) {\r\n return ch >= '0' && ch <= '9';\r\n }\r\n\r\n function isAlpha (ch) {\r\n return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_' || ch === '$';\r\n }\r\n\r\n function isAlphaNum (ch) {\r\n return isAlpha(ch) || isDigid(ch);\r\n }\r\n\r\n function isWhitespace (ch) {\r\n return ch == ' ' || ch == '\\r' || ch == '\\t' || ch == '\\n' || ch == '\\v' || ch == '\\u00A0';\r\n }\r\n\r\n function isExpOperator (ch) {\r\n return ch == '-' || ch == '+' || isDigid(ch);\r\n }\r\n\r\n function readString (quote) {\r\n let escape = false, result = '';\r\n\r\n index++;\r\n\r\n while (index < expression.length) {\r\n ch = read();\r\n\r\n if (escape) {\r\n if (ch == 'u') {\r\n index++;\r\n const hex = read(4);\r\n if (!hex.match(/[\\da-f]{4}/i)) {\r\n throw new Error(`Invalid unicode escape [\\\\u${hex}] at index ${index} in expression: ${expression}`);\r\n }\r\n result += String.fromCharCode(parseInt(hex, 16));\r\n index += 4;\r\n } else {\r\n result += dmx._ESCAPE_CHARS.has(ch) ? dmx._ESCAPE_CHARS.get(ch) : ch;\r\n }\r\n\r\n escape = false;\r\n } else if (ch == '\\\\') {\r\n escape = true;\r\n } else if (ch == quote) {\r\n index++;\r\n if (quote == '`') {\r\n result = '{{' + result + '}}';\r\n }\r\n return result;\r\n } else {\r\n result += ch;\r\n }\r\n\r\n index++;\r\n }\r\n\r\n throw new Error(`Unterminated string in expression: ${expression}`);\r\n }\r\n\r\n function readNumber () {\r\n let result = '', exponent = false;\r\n\r\n while (index < expression.length) {\r\n ch = read();\r\n\r\n if (is('_') && peek() && isDigid(peek())) {\r\n index++;\r\n continue;\r\n }\r\n\r\n if ((is('.') && peek() && isDigid(peek())) || isDigid(ch)) {\r\n result += ch;\r\n } else {\r\n const next = peek();\r\n\r\n if (is('eE') && isExpOperator(next)) {\r\n result += 'e';\r\n exponent = true;\r\n } else if (isExpOperator(ch) && next && isDigid(next) && exponent) {\r\n result += ch;\r\n exponent = false;\r\n } else if (isExpOperator(ch) && (!next || !isDigid(next)) && exponent) {\r\n throw new Error(`Invalid exponent in expression: ${expression}`);\r\n } else {\r\n break;\r\n }\r\n }\r\n\r\n index++;\r\n }\r\n\r\n if (read() == 'n') {\r\n index++;\r\n return BigInt(result);\r\n }\r\n\r\n if (read() == 'm') {\r\n index++;\r\n if (window.Decimal) {\r\n return new Decimal(result);\r\n } else {\r\n console.warn('Decimal number in expression but library not found');\r\n }\r\n }\r\n\r\n return +result;\r\n }\r\n\r\n function readIdent () {\r\n let result = '';\r\n\r\n while (index < expression.length) {\r\n ch = read();\r\n\r\n if (isAlphaNum(ch)) {\r\n result += ch;\r\n } else {\r\n break;\r\n }\r\n\r\n index++;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n function readRegexp () {\r\n let result = '', modifiers = '', escape = false;\r\n\r\n index++;\r\n\r\n while (index < expression.length) {\r\n ch = read();\r\n\r\n if (escape) {\r\n escape = false;\r\n } else if (ch == '\\\\') {\r\n escape = true;\r\n } else if (ch == '/') {\r\n index++;\r\n\r\n while ('ign'.includes(ch = read())) {\r\n modifiers += ch;\r\n index++;\r\n }\r\n\r\n return new RegExp(result, modifiers);\r\n }\r\n\r\n result += ch;\r\n index++;\r\n }\r\n\r\n throw new Error(`Unterminated regexp in expression: ${expression}`);\r\n }\r\n\r\n function testRegexp () {\r\n let i = index, ok = true;\r\n\r\n try {\r\n readRegexp();\r\n } catch (e) {\r\n ok = false;\r\n }\r\n\r\n index = i;\r\n ch = '/';\r\n\r\n return ok;\r\n }\r\n};\r\n\r\ndmx.parse = function (expression, scope = dmx.app) {\r\n expression = expression.trim();\r\n\r\n if (expression.includes('{{')) {\r\n if (expression.startsWith('{{') && expression.endsWith('}}') && !expression.slice(2).includes('{{')) {\r\n expression = expression.slice(2, -2);\r\n } else {\r\n return expression.replace(/{{(.+?)}}/g, (_, expression) => {\r\n const result = dmx.parse(expression, scope);\r\n return result == null ? '' : result;\r\n });\r\n }\r\n }\r\n\r\n if (!expression) return undefined;\r\n\r\n let tokens, context, result;\r\n\r\n try {\r\n tokens = Array.from(dmx.lexer(expression));\r\n result = doParse();\r\n } catch (e) {\r\n console.error('Error parsing expression:', expression, e);\r\n }\r\n\r\n return result;\r\n\r\n function read () {\r\n if (tokens.length === 0) {\r\n throw new Error(`Unexpected end of expression: ${expression}`);\r\n }\r\n\r\n return tokens[0];\r\n }\r\n\r\n function peek (e) {\r\n if (tokens.length > 0) {\r\n const token = tokens[0];\r\n\r\n if (!e || token.name == e) {\r\n return token;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n function expect (e) {\r\n const token = peek(e);\r\n\r\n if (token) {\r\n tokens.shift();\r\n return token;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n function consume (e) {\r\n if (!expect(e)) {\r\n throw new Error(`Expected ${e} at index ${tokens[0].index} in expression: ${expression}`);\r\n }\r\n }\r\n\r\n function fn (exp) {\r\n const args = Array.prototype.slice.call(arguments, 1);\r\n\r\n return () => {\r\n if (dmx._EXPRESSIONS.has(exp)) {\r\n return dmx._EXPRESSIONS.get(exp).apply(scope, args);\r\n }\r\n\r\n return exp;\r\n };\r\n }\r\n\r\n function doParse () {\r\n const a = [];\r\n\r\n while (true) {\r\n if (tokens.length > 0 && !(peek('R_PAREN') || peek('R_BRACKET') || peek('R_CURLY') || peek('COMMA') || peek('SEMI'))) {\r\n a.push(parseExpression());\r\n }\r\n\r\n if (!(expect('COMMA') || expect('SEMI'))) {\r\n return (a.length == 1 ? a[0] : b)();\r\n }\r\n }\r\n\r\n function b () {\r\n let result;\r\n\r\n for (let i = 0; i < a.length; i++) {\r\n const e = a[i];\r\n if (e) result = e();\r\n }\r\n\r\n return result;\r\n }\r\n }\r\n\r\n function parseExpression () {\r\n return parseConditional();\r\n }\r\n\r\n function parseConditional () {\r\n const a = parseLogicalOr();\r\n\r\n if (expect('QUESTION')) {\r\n const b = parseExpression();\r\n consume('COLON');\r\n const c = parseExpression();\r\n\r\n return fn('?', a, b, c);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseLogicalOr () {\r\n let a = parseLogicalAnd();\r\n\r\n while (expect('LOGICAL_OR')) {\r\n const b = parseLogicalAnd();\r\n a = fn('||', a, b);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseLogicalAnd () {\r\n let a = parseBitwiseOr();\r\n\r\n while (expect('LOGICAL_AND')) {\r\n const b = parseBitwiseOr();\r\n a = fn('&&', a, b);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseBitwiseOr () {\r\n let a = parseBitwiseXor();\r\n\r\n while (expect('BITWISE_OR')) {\r\n const b = parseBitwiseXor();\r\n a = fn('|', a, b);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseBitwiseXor () {\r\n let a = parseBitwiseAnd();\r\n\r\n while (expect('BITWISE_XOR')) {\r\n const b = parseBitwiseAnd();\r\n a = fn('^', a, b);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseBitwiseAnd () {\r\n let a = parseEquality();\r\n\r\n while (expect('BITWISE_AND')) {\r\n const b = parseEquality();\r\n a = fn('&', a, b);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseEquality () {\r\n let a = parseRelational(), b;\r\n\r\n if ((b = expect('EQUALITY'))) {\r\n const c = parseEquality();\r\n a = fn(b.value, a, c);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseRelational () {\r\n let a = parseBitwiseShift(), b;\r\n\r\n if ((b = expect('RELATIONAL'))) {\r\n const c = parseRelational();\r\n a = fn(b.value, a, c);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseBitwiseShift () {\r\n let a = parseAddictive(), b;\r\n\r\n if ((b = expect('BITWISE_SHIFT'))) {\r\n const c = parseBitwiseShift();\r\n a = fn(b.value, a, c);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseAddictive () {\r\n let a = parseMultiplicative(), b;\r\n\r\n while ((b = expect('ADDICTIVE'))) {\r\n const c = parseMultiplicative();\r\n a = fn(b.value, a, c);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseMultiplicative () {\r\n let a = parseUnary(), b;\r\n\r\n while ((b = expect('MULTIPLICATIVE'))) {\r\n const c = parseUnary();\r\n a = fn(b.value, a, c);\r\n }\r\n\r\n return a;\r\n }\r\n\r\n function parseUnary () {\r\n let a;\r\n\r\n if ((a = expect('ADDICTIVE'))) {\r\n if (a.value == '+') {\r\n return parsePrimary();\r\n } else {\r\n return fn(a.value, () => 0, parsePrimary());\r\n }\r\n } else if ((a = expect('LOGICAL_NOT'))) {\r\n return fn(a.value, parseUnary());\r\n } else if ((a = expect('BITWISE_NOT'))) {\r\n return fn(a.value, parseUnary());\r\n } else {\r\n return parsePrimary();\r\n }\r\n }\r\n\r\n function parsePrimary () {\r\n let result, next;\r\n\r\n if (expect('L_PAREN')) {\r\n result = parseExpression();\r\n consume('R_PAREN');\r\n } else if (expect('L_CURLY')) {\r\n const obj = {};\r\n\r\n if (read().name != 'R_CURLY') {\r\n do {\r\n const key = expect().value;\r\n consume('COLON');\r\n obj[key] = parseExpression()();\r\n } while (expect('COMMA'));\r\n }\r\n\r\n result = fn(obj);\r\n\r\n consume('R_CURLY');\r\n } else if (expect('L_BRACKET')) {\r\n const arr = [];\r\n\r\n if (read().name != 'R_BRACKET') {\r\n do {\r\n arr.push(parseExpression()());\r\n } while (expect('COMMA'));\r\n }\r\n\r\n result = fn(arr);\r\n\r\n consume('R_BRACKET');\r\n } else if (expect('PERIOD')) {\r\n result = peek() ? parseMember(fn(scope.data)) : fn(scope.data);\r\n } else {\r\n const token = expect();\r\n\r\n if (token === false) {\r\n throw new Error(`Unexpected end of expression: ${expression}`);\r\n }\r\n\r\n if (token.name == 'IDENT') {\r\n result = dmx._RESERVED.has(token.value) ? dmx._RESERVED.get(token.value)(scope) : () => scope.get(token.value);\r\n } else if (token.name == 'METHOD') {\r\n result = fn(dmx.__formatters.global[token.value] || (() => {\r\n console.warn(`Method \"${token.value}\" not found in expression: ${expression}`);\r\n return undefined;\r\n })\r\n );\r\n } else {\r\n result = () => token.value;\r\n }\r\n }\r\n\r\n while ((next = expect('L_PAREN') || expect('L_BRACKET') || expect('PERIOD'))) {\r\n if (next.value == '(') {\r\n result = parseCall(result, context);\r\n } else if (next.value == '[') {\r\n context = result;\r\n result = parseIndex(result, context);\r\n } else if (next.value == '.') {\r\n context = result;\r\n result = parseMember(result, context);\r\n } else {\r\n throw new Error(`Unexpected token \"${next.value}\" at index ${next.index} in expression: ${expression}`);\r\n }\r\n }\r\n\r\n context = null;\r\n\r\n return result;\r\n }\r\n\r\n function parseCall (func, context) {\r\n const argsFn = [];\r\n\r\n if (read().name != 'R_PAREN') {\r\n do {\r\n argsFn.push(parseExpression());\r\n } while (expect('COMMA'));\r\n }\r\n\r\n consume('R_PAREN');\r\n\r\n return () => {\r\n let args = [];\r\n\r\n if (context) {\r\n args.push(context());\r\n }\r\n\r\n for (let f of argsFn) {\r\n args.push(f());\r\n }\r\n\r\n try {\r\n return (func() || dmx.noop).apply(scope, args);\r\n } catch (e) {\r\n console.warn(`Error calling method ${func().name} in expression: ${expression}`, e);\r\n return undefined;\r\n }\r\n };\r\n }\r\n\r\n function parseIndex (object) {\r\n const indexFn = parseExpression();\r\n\r\n consume('R_BRACKET');\r\n\r\n return () => {\r\n const obj = object();\r\n const index = indexFn();\r\n\r\n if (typeof obj != 'object' || obj == null) {\r\n return undefined;\r\n }\r\n\r\n if (obj.__dmxScope__) {\r\n return scope.get(index);\r\n }\r\n\r\n if (dmx.getType(obj) == 'map') {\r\n return obj.get(index);\r\n }\r\n\r\n return obj[index];\r\n };\r\n }\r\n\r\n function parseMember (object) {\r\n const token = expect();\r\n\r\n return () => {\r\n const obj = object();\r\n const type = dmx.getType(obj);\r\n\r\n if (token.name == 'METHOD') {\r\n const method = '__' + token.value;\r\n\r\n if (type == 'map' && typeof obj.get(method) == 'function') {\r\n return obj.get(method).bind(obj);\r\n }\r\n\r\n if (type == 'object' && typeof obj[method] == 'function') {\r\n return obj[method];\r\n }\r\n\r\n if (dmx.__formatters[type] && dmx.__formatters[type][token.value]) {\r\n return dmx.__formatters[type][token.value];\r\n }\r\n\r\n if (dmx.__formatters['any'] && dmx.__formatters['any'][token.value]) {\r\n return dmx.__formatters['any'][token.value];\r\n }\r\n\r\n return () => {\r\n if (obj != null) {\r\n console.warn(`Method \"${token.value}\" not found in expression: ${expression}`);\r\n }\r\n return undefined;\r\n };\r\n }\r\n\r\n if (obj && obj.__dmxScope__) {\r\n return scope.get(token.value);\r\n }\r\n\r\n if (type == 'map') {\r\n return obj.get(token.value);\r\n }\r\n\r\n return obj && typeof obj == 'object' && token.value in obj ? obj[token.value] : undefined;\r\n };\r\n }\r\n};","dmx.BaseComponent = dmx.createClass({\r\n constructor: function (node, parent) {\r\n this.$node = node;\r\n this.parent = parent;\r\n this.children = [];\r\n this.listeners = {};\r\n\r\n this.__disposables = [];\r\n this.__childDisposables = [];\r\n\r\n this.updatedProps = new Map();\r\n this.updateRequested = false;\r\n\r\n this.isInitialized = false;\r\n this.isDestroyed = false;\r\n\r\n this.props = new Proxy(\r\n {},\r\n {\r\n set: (target, prop, value, receiver) => {\r\n const oldValue = Reflect.get(target, prop, receiver);\r\n const ok = Reflect.set(target, prop, value, receiver);\r\n\r\n if (ok && this.isInitialized) {\r\n if ((this.attributes[prop] && this.attributes[prop].alwaysUpdate) || !dmx.equal(oldValue, value)) {\r\n this.requestUpdate(prop, oldValue);\r\n }\r\n }\r\n\r\n return ok;\r\n },\r\n }\r\n );\r\n\r\n this.data = dmx.signalProxy();\r\n this.seed = Math.random();\r\n\r\n this.name =\r\n node.getAttribute('id') ||\r\n node.getAttribute('name') ||\r\n (this.type && this.type.toLowerCase().replace(/^dmx-/, '')) ||\r\n '';\r\n this.name = this.name.replace(/[^\\w]/g, '');\r\n\r\n try {\r\n this.$initialData();\r\n this.$parseAttributes(node);\r\n this.init(node);\r\n if (this.render !== false) {\r\n this.render(node);\r\n }\r\n if (this.$node) {\r\n this.$customAttributes('mounted', this.$node);\r\n this.$node.dmxComponent = this;\r\n this.$node.dmxRendered = true;\r\n }\r\n this.isInitialized = true;\r\n } catch (e) {\r\n console.error(e);\r\n }\r\n },\r\n\r\n tag: null,\r\n initialData: {},\r\n attributes: {},\r\n methods: {},\r\n events: {\r\n destroy: Event,\r\n },\r\n\r\n render: function (node) {\r\n if (this.$node) {\r\n this.$parse();\r\n }\r\n },\r\n\r\n parse: function (expression) {\r\n return dmx.parse(expression, this);\r\n },\r\n\r\n // find component based on name inside children\r\n find: function (name) {\r\n if (this.name == name) return this;\r\n\r\n for (var i = 0; i < this.children.length; i++) {\r\n var found = this.children[i].find(name);\r\n if (found) return found;\r\n }\r\n\r\n return null;\r\n },\r\n\r\n init: dmx.noop,\r\n\r\n beforeUpdate: dmx.noop,\r\n update: dmx.noop,\r\n updated: dmx.noop,\r\n\r\n beforeDestroy: dmx.noop,\r\n destroy: dmx.noop,\r\n destroyed: dmx.noop,\r\n\r\n addEventListener: function (type, callback) {\r\n if (!(type in this.listeners)) {\r\n this.listeners[type] = new Set();\r\n }\r\n this.listeners[type].add(callback);\r\n },\r\n\r\n removeEventListener: function (type, callback) {\r\n if (!(type in this.listeners)) return;\r\n this.listeners[type].delete(callback);\r\n },\r\n\r\n dispatchEvent: function (event, props, data, nsp) {\r\n if (this.isDestroyed) return;\r\n\r\n if (typeof event == 'string') {\r\n var ComponentEvent = this.events[event] || CustomEvent;\r\n event = new ComponentEvent(event, props);\r\n }\r\n\r\n if (!(event.type in this.listeners)) return true;\r\n\r\n event.nsp = nsp;\r\n event.target = this;\r\n event.$data = data || {};\r\n for (let listener of this.listeners[event.type]) {\r\n if (listener.call(this, event) === false) {\r\n event.preventDefault();\r\n }\r\n }\r\n\r\n return !event.defaultPrevented;\r\n },\r\n\r\n $createChild: function (name, node) {\r\n var Component = dmx.__components[name];\r\n var component = new Component(node, this);\r\n this.$addChild(component, component.name);\r\n },\r\n\r\n $addChild: function (child, name) {\r\n this.children.push(child);\r\n if (name) {\r\n if (this.data[name] && dmx.debug) {\r\n console.warn('Duplicate name \"' + name + '\" found, component not added to scope.');\r\n //return;\r\n }\r\n this.set(name, child.data);\r\n }\r\n },\r\n\r\n $removeChild: function (child) {\r\n // remove from children collection\r\n if (this.children.includes(child)) {\r\n this.children.splice(this.children.indexOf(child), 1);\r\n }\r\n // remove from data\r\n if (child.name && this.data[child.name]) {\r\n this.del(child.name);\r\n }\r\n },\r\n\r\n $customAttributes: function (hook, node, attributes) {\r\n const toCamelCase = (s) => s.replace(/-./g, (x) => x[1].toUpperCase());\r\n if (!attributes) attributes = dmx.dom.getAttributes(node);\r\n\r\n attributes.forEach((attr) => {\r\n if (node == this.$node) {\r\n if (attr.name == 'bind' && this.attributes[toCamelCase(attr.argument)]) {\r\n return;\r\n }\r\n\r\n if (attr.name == 'on' && this.events[attr.argument]) {\r\n return;\r\n }\r\n }\r\n\r\n if (dmx.__attributes[hook][attr.name]) {\r\n this.__inChild = node != this.$node;\r\n const dispose = dmx.__attributes[hook][attr.name].call(this, node, attr);\r\n if (dispose) {\r\n this[this.__inChild ? '__childDisposables' : '__disposables'].push(dispose);\r\n }\r\n }\r\n });\r\n\r\n this.__inChild = null;\r\n },\r\n\r\n $parseTextNode(node) {\r\n if (node.nodeType !== 3) return;\r\n\r\n if (dmx.reExpression.test(node.nodeValue)) {\r\n const parts = node.nodeValue\r\n .replace(dmx.reExpressionReplace, (_, expression) => {\r\n return `##split##${expression}##split##`;\r\n })\r\n .split('##split##');\r\n\r\n const fragment = document.createDocumentFragment();\r\n parts.forEach((part, i) => {\r\n const textNode = document.createTextNode(part);\r\n fragment.appendChild(textNode);\r\n\r\n if (i % 2) {\r\n this.$watch(part, (value) => {\r\n textNode.nodeValue = value;\r\n });\r\n }\r\n });\r\n\r\n node.parentNode.replaceChild(fragment, node);\r\n }\r\n },\r\n\r\n $parse: function (node) {\r\n node = node || this.$node;\r\n\r\n if (!node) return;\r\n\r\n if (node.nodeType === 3) {\r\n return this.$parseTextNode(node);\r\n }\r\n\r\n if (node.nodeType !== 1) return;\r\n\r\n if (dmx.config.mapping) {\r\n Object.keys(dmx.config.mapping).forEach((map) => {\r\n dmx.array(node.querySelectorAll(map)).forEach((node) => {\r\n if (!node.hasAttribute('is')) {\r\n node.setAttribute('is', 'dmx-' + dmx.config.mapping[map]);\r\n }\r\n });\r\n });\r\n }\r\n\r\n dmx.dom.walk(\r\n node,\r\n function (node) {\r\n if (node == this.$node) {\r\n // skip current node\r\n return;\r\n }\r\n\r\n // Element Node\r\n if (node.nodeType === 1) {\r\n var tagName = node.tagName.toLowerCase();\r\n var attributes = dmx.dom.getAttributes(node);\r\n\r\n if (node.hasAttribute('is')) {\r\n tagName = node.getAttribute('is');\r\n }\r\n\r\n if (dmx.reIgnoreElement.test(tagName)) {\r\n // ignore element\r\n return false;\r\n }\r\n\r\n this.$customAttributes('before', node, attributes);\r\n var idx = attributes.findIndex((attr) => attr.name === 'repeat');\r\n if (idx !== -1) return false;\r\n\r\n if (dmx.rePrefixed.test(tagName)) {\r\n tagName = tagName.replace(/^dmx-/i, '');\r\n\r\n if (tagName in dmx.__components) {\r\n node.isComponent = true;\r\n if (!node.dmxRendered) {\r\n this.$createChild(tagName, node);\r\n } else if (window.__WAPPLER__) {\r\n // This breaks some components in design view\r\n // causes flows to trigger constantly\r\n // components ofter have there own parsing and this breaks it\r\n if (node.dmxComponent && node.dmxComponent.$parse) {\r\n // for now ignode specific for flows with script tag\r\n if (!dmx.reIgnoreElement.test(node.tagName)) {\r\n node.dmxComponent.$parse();\r\n }\r\n }\r\n }\r\n return false;\r\n } else {\r\n console.warn('Unknown component found! ' + tagName);\r\n return;\r\n }\r\n }\r\n\r\n this.$customAttributes('mounted', node, attributes);\r\n }\r\n\r\n // Text Node\r\n if (node.nodeType === 3) {\r\n this.$parseTextNode(node);\r\n }\r\n },\r\n this\r\n );\r\n },\r\n\r\n $update: function (idents) {\r\n console.warn('Component.$update is deprecated.');\r\n },\r\n\r\n $parseAttributes: function (node) {\r\n const toKebabCase = (s) => s.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());\r\n\r\n for (const name in this.attributes) {\r\n const opts = this.attributes[name];\r\n const attrName = toKebabCase(name);\r\n\r\n let value = dmx.clone(opts.default);\r\n\r\n // static\r\n if (node.hasAttribute(attrName)) {\r\n if (opts.type === Boolean) {\r\n value = node.getAttribute(attrName) !== 'false';\r\n } else {\r\n value = node.getAttribute(attrName);\r\n\r\n if (opts.type === Number) {\r\n // Only set number is a valid number is given\r\n if (value && isFinite(Number(value))) {\r\n value = Number(value);\r\n }\r\n }\r\n\r\n if (opts.type === Object || opts.type === Array) {\r\n try {\r\n value = JSON.parse(value);\r\n } catch (err) {\r\n console.warn('Invalid attribute value, expected a JSON string got ' + value);\r\n }\r\n }\r\n\r\n if (opts.enum && !opts.enum.includes(value)) {\r\n value = dmx.clone(opts.default);\r\n }\r\n\r\n if (opts.validate && !opts.validate(value)) {\r\n value = dmx.clone(opts.default);\r\n }\r\n }\r\n\r\n this.props[name] = value;\r\n //opts.default = value;\r\n }\r\n\r\n // dynamic\r\n if (node.hasAttribute('dmx-bind:' + attrName)) {\r\n const expression = node.getAttribute('dmx-bind:' + attrName);\r\n\r\n this.$watch(expression, (value) => {\r\n if (value === undefined) {\r\n value = dmx.clone(opts.default);\r\n } else if (opts.type === Boolean) {\r\n value = !!value;\r\n } else {\r\n if (value != null) {\r\n if (opts.type === Number) {\r\n if (typeof value === 'string') {\r\n if (value && isFinite(Number(value))) {\r\n value = Number(value);\r\n } else {\r\n value = dmx.clone(opts.default);\r\n }\r\n } else if (typeof value !== 'number' || !isFinite(Number(value))) {\r\n value = dmx.clone(opts.default);\r\n }\r\n }\r\n\r\n if (opts.type === String) {\r\n value = String(value);\r\n }\r\n\r\n if (opts.type === Object && typeof value !== 'object') {\r\n value = dmx.clone(opts.default);\r\n }\r\n\r\n if (opts.type === Array) {\r\n value = Array.from(value);\r\n }\r\n }\r\n\r\n if (opts.enum && !opts.enum.includes(value)) {\r\n value = dmx.clone(opts.default);\r\n }\r\n\r\n if (opts.validate && !opts.validate(value)) {\r\n value = dmx.clone(opts.default);\r\n }\r\n }\r\n\r\n this.props[name] = value;\r\n });\r\n } else {\r\n this.props[name] = value;\r\n }\r\n }\r\n\r\n for (const event in this.events) {\r\n if (node.hasAttribute('on' + event)) {\r\n this.__disposables.push(dmx.eventListener(this, event, Function('event', node.getAttribute('on' + event)), {}));\r\n }\r\n }\r\n\r\n dmx.dom.getAttributes(node).forEach((attr) => {\r\n if (attr.name == 'on' && this.events[attr.argument]) {\r\n this.__disposables.push(dmx.eventListener(\r\n this,\r\n attr.argument,\r\n (event) => {\r\n if (event.originalEvent) {\r\n event = event.originalEvent;\r\n }\r\n\r\n var returnValue = dmx.parse(\r\n attr.value,\r\n dmx.DataScope(\r\n {\r\n $event: event.$data,\r\n $originalEvent: event,\r\n },\r\n this\r\n )\r\n );\r\n\r\n return returnValue;\r\n },\r\n attr.modifiers\r\n ));\r\n }\r\n });\r\n },\r\n\r\n requestUpdate: function (prop, oldValue) {\r\n //console.log(`request Update ${this.name} (${prop}: ${oldValue} => ${this.prop})`);\r\n if (!this.performUpdate) return;\r\n\r\n if (!this.updatedProps.has(prop)) {\r\n this.updatedProps.set(prop, oldValue);\r\n }\r\n\r\n if (!this.updateRequested) {\r\n //console.log('queue Microtask', this.name, this.updateRequested);\r\n //queueMicrotask(() => {\r\n dmx.nextTick(() => {\r\n //console.log('exec Microtask', this.name, this.updateRequested);\r\n if (this.isDestroyed) return;\r\n this.updateRequested = false;\r\n this.performUpdate(this.updatedProps);\r\n this.updatedProps.clear();\r\n });\r\n }\r\n\r\n this.updateRequested = true;\r\n },\r\n\r\n $initialData: function () {\r\n Object.assign(\r\n this.data,\r\n { $type: this.type },\r\n typeof this.initialData == 'function' ? this.initialData() : this.initialData\r\n );\r\n\r\n Object.keys(this.methods).forEach(function (method) {\r\n var self = this;\r\n this.data['__' + method] = function () {\r\n return self.methods[method].apply(self, Array.prototype.slice.call(arguments, 1));\r\n };\r\n }, this);\r\n },\r\n\r\n // alias for $watch\r\n $addBinding: function (expression, cb) {\r\n this.$watch(expression, cb);\r\n },\r\n\r\n $watch: function (expression, cb) {\r\n const prop = this.__inChild ? '__childDisposables' : '__disposables';\r\n if (!this[prop]) this[prop] = [];\r\n let init = true;\r\n\r\n this[prop].push(\r\n dmx.effect(() => {\r\n if (init) {\r\n cb.call(this, this.parse(expression));\r\n init = false;\r\n } else {\r\n const value = this.parse(expression);\r\n queueMicrotask(() => cb.call(this, value));\r\n }\r\n })\r\n );\r\n },\r\n\r\n $destroy: function () {\r\n this.dispatchEvent('destroy');\r\n this.beforeDestroy();\r\n this.destroy();\r\n this.isDestroyed = true;\r\n if (this.parent && this.parent.$removeChild) {\r\n this.parent.$removeChild(this);\r\n }\r\n this.$destroyChildren();\r\n this.__disposables.forEach((dispose) => dispose());\r\n this.__disposables = [];\r\n if (this.$node) {\r\n this.$node.dmxComponent = null;\r\n this.$node = null;\r\n }\r\n this.parent = null;\r\n this.data = {};\r\n this.destroyed();\r\n },\r\n\r\n $destroyChildren: function () {\r\n Array.from(this.children).forEach((child) => {\r\n child.$destroy();\r\n });\r\n this.children = [];\r\n\r\n this.__childDisposables.forEach((dispose) => dispose());\r\n this.__childDisposables = [];\r\n },\r\n\r\n get: function (name, ignoreParents) {\r\n if (this.data[name] !== undefined) {\r\n return this.data[name];\r\n }\r\n\r\n if (this.parent && ignoreParents !== true) {\r\n if (name == 'parent') {\r\n return this.parent.data;\r\n }\r\n\r\n return this.parent.get(name);\r\n }\r\n\r\n return undefined;\r\n },\r\n\r\n add: function (name, value) {\r\n if (this.data[name]) {\r\n if (Array.isArray(this.data[name])) {\r\n this.data[name].push(value);\r\n } else {\r\n this.data[name] = [this.data[name], value];\r\n }\r\n } else {\r\n this.set(name, value);\r\n }\r\n },\r\n\r\n set: function (name, value) {\r\n if (typeof name == 'object') {\r\n dmx.batch(() => {\r\n for (var prop in name) {\r\n if (name.hasOwnProperty(prop)) {\r\n this.set(prop, name[prop]);\r\n }\r\n }\r\n });\r\n } else {\r\n this.data[name] = value;\r\n }\r\n },\r\n\r\n del: function (name) {\r\n delete this.data[name];\r\n },\r\n});\r\n","(function() {\r\n\r\n/**\r\n * Expose `pathToRegexp`.\r\n */\r\ndmx.pathToRegexp = pathToRegexp\r\ndmx.pathToRegexp.parse = parse\r\ndmx.pathToRegexp.compile = compile\r\ndmx.pathToRegexp.tokensToFunction = tokensToFunction\r\ndmx.pathToRegexp.tokensToRegExp = tokensToRegExp\r\n\r\n/**\r\n * Default configs.\r\n */\r\nvar DEFAULT_DELIMITER = '/'\r\n\r\n/**\r\n * The main path matching regexp utility.\r\n *\r\n * @type {RegExp}\r\n */\r\nvar PATH_REGEXP = new RegExp([\r\n // Match escaped characters that would otherwise appear in future matches.\r\n // This allows the user to escape special characters that won't transform.\r\n '(\\\\\\\\.)',\r\n // Match Express-style parameters and un-named parameters with a prefix\r\n // and optional suffixes. Matches appear as:\r\n //\r\n // \":test(\\\\d+)?\" => [\"test\", \"\\d+\", undefined, \"?\"]\r\n // \"(\\\\d+)\" => [undefined, undefined, \"\\d+\", undefined]\r\n '(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))([+*?])?'\r\n].join('|'), 'g')\r\n\r\n/**\r\n * Parse a string for the raw tokens.\r\n *\r\n * @param {string} str\r\n * @param {Object=} options\r\n * @return {!Array}\r\n */\r\nfunction parse (str, options) {\r\n var tokens = []\r\n var key = 0\r\n var index = 0\r\n var path = ''\r\n var defaultDelimiter = (options && options.delimiter) || DEFAULT_DELIMITER\r\n var whitelist = (options && options.whitelist) || undefined\r\n var pathEscaped = false\r\n var res\r\n\r\n while ((res = PATH_REGEXP.exec(str)) !== null) {\r\n var m = res[0]\r\n var escaped = res[1]\r\n var offset = res.index\r\n path += str.slice(index, offset)\r\n index = offset + m.length\r\n\r\n // Ignore already escaped sequences.\r\n if (escaped) {\r\n path += escaped[1]\r\n pathEscaped = true\r\n continue\r\n }\r\n\r\n var prev = ''\r\n var name = res[2]\r\n var capture = res[3]\r\n var group = res[4]\r\n var modifier = res[5]\r\n\r\n if (!pathEscaped && path.length) {\r\n var k = path.length - 1\r\n var c = path[k]\r\n var matches = whitelist ? whitelist.indexOf(c) > -1 : true\r\n\r\n if (matches) {\r\n prev = c\r\n path = path.slice(0, k)\r\n }\r\n }\r\n\r\n // Push the current path onto the tokens.\r\n if (path) {\r\n tokens.push(path)\r\n path = ''\r\n pathEscaped = false\r\n }\r\n\r\n var repeat = modifier === '+' || modifier === '*'\r\n var optional = modifier === '?' || modifier === '*'\r\n var pattern = capture || group\r\n var delimiter = prev || defaultDelimiter\r\n\r\n tokens.push({\r\n name: name || key++,\r\n prefix: prev,\r\n delimiter: delimiter,\r\n optional: optional,\r\n repeat: repeat,\r\n pattern: pattern\r\n ? escapeGroup(pattern)\r\n : '[^' + escapeString(delimiter === defaultDelimiter ? delimiter : (delimiter + defaultDelimiter)) + ']+?'\r\n })\r\n }\r\n\r\n // Push any remaining characters.\r\n if (path || index < str.length) {\r\n tokens.push(path + str.substr(index))\r\n }\r\n\r\n return tokens\r\n}\r\n\r\n/**\r\n * Compile a string to a template function for the path.\r\n *\r\n * @param {string} str\r\n * @param {Object=} options\r\n * @return {!function(Object=, Object=)}\r\n */\r\nfunction compile (str, options) {\r\n return tokensToFunction(parse(str, options))\r\n}\r\n\r\n/**\r\n * Expose a method for transforming tokens into the path function.\r\n */\r\nfunction tokensToFunction (tokens) {\r\n // Compile all the tokens into regexps.\r\n var matches = new Array(tokens.length)\r\n\r\n // Compile all the patterns before compilation.\r\n for (var i = 0; i < tokens.length; i++) {\r\n if (typeof tokens[i] === 'object') {\r\n matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$')\r\n }\r\n }\r\n\r\n return function (data, options) {\r\n var path = ''\r\n var encode = (options && options.encode) || encodeURIComponent\r\n\r\n for (var i = 0; i < tokens.length; i++) {\r\n var token = tokens[i]\r\n\r\n if (typeof token === 'string') {\r\n path += token\r\n continue\r\n }\r\n\r\n var value = data ? data[token.name] : undefined\r\n var segment\r\n\r\n if (Array.isArray(value)) {\r\n if (!token.repeat) {\r\n throw new TypeError('Expected \"' + token.name + '\" to not repeat, but got array')\r\n }\r\n\r\n if (value.length === 0) {\r\n if (token.optional) continue\r\n\r\n throw new TypeError('Expected \"' + token.name + '\" to not be empty')\r\n }\r\n\r\n for (var j = 0; j < value.length; j++) {\r\n segment = encode(value[j], token)\r\n\r\n if (!matches[i].test(segment)) {\r\n throw new TypeError('Expected all \"' + token.name + '\" to match \"' + token.pattern + '\"')\r\n }\r\n\r\n path += (j === 0 ? token.prefix : token.delimiter) + segment\r\n }\r\n\r\n continue\r\n }\r\n\r\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\r\n segment = encode(String(value), token)\r\n\r\n if (!matches[i].test(segment)) {\r\n throw new TypeError('Expected \"' + token.name + '\" to match \"' + token.pattern + '\", but got \"' + segment + '\"')\r\n }\r\n\r\n path += token.prefix + segment\r\n continue\r\n }\r\n\r\n if (token.optional) continue\r\n\r\n throw new TypeError('Expected \"' + token.name + '\" to be ' + (token.repeat ? 'an array' : 'a string'))\r\n }\r\n\r\n return path\r\n }\r\n}\r\n\r\n/**\r\n * Escape a regular expression string.\r\n *\r\n * @param {string} str\r\n * @return {string}\r\n */\r\nfunction escapeString (str) {\r\n return str.replace(/([.+*?=^!:${}()[\\]|/\\\\])/g, '\\\\$1')\r\n}\r\n\r\n/**\r\n * Escape the capturing group by escaping special characters and meaning.\r\n *\r\n * @param {string} group\r\n * @return {string}\r\n */\r\nfunction escapeGroup (group) {\r\n return group.replace(/([=!:$/()])/g, '\\\\$1')\r\n}\r\n\r\n/**\r\n * Get the flags for a regexp from the options.\r\n *\r\n * @param {Object} options\r\n * @return {string}\r\n */\r\nfunction flags (options) {\r\n return options && options.sensitive ? '' : 'i'\r\n}\r\n\r\n/**\r\n * Pull out keys from a regexp.\r\n *\r\n * @param {!RegExp} path\r\n * @param {Array=} keys\r\n * @return {!RegExp}\r\n */\r\nfunction regexpToRegexp (path, keys) {\r\n if (!keys) return path\r\n\r\n // Use a negative lookahead to match only capturing groups.\r\n var groups = path.source.match(/\\((?!\\?)/g)\r\n\r\n if (groups) {\r\n for (var i = 0; i < groups.length; i++) {\r\n keys.push({\r\n name: i,\r\n prefix: null,\r\n delimiter: null,\r\n optional: false,\r\n repeat: false,\r\n pattern: null\r\n })\r\n }\r\n }\r\n\r\n return path\r\n}\r\n\r\n/**\r\n * Transform an array into a regexp.\r\n *\r\n * @param {!Array} path\r\n * @param {Array=} keys\r\n * @param {Object=} options\r\n * @return {!RegExp}\r\n */\r\nfunction arrayToRegexp (path, keys, options) {\r\n var parts = []\r\n\r\n for (var i = 0; i < path.length; i++) {\r\n parts.push(pathToRegexp(path[i], keys, options).source)\r\n }\r\n\r\n return new RegExp('(?:' + parts.join('|') + ')', flags(options))\r\n}\r\n\r\n/**\r\n * Create a path regexp from string input.\r\n *\r\n * @param {string} path\r\n * @param {Array=} keys\r\n * @param {Object=} options\r\n * @return {!RegExp}\r\n */\r\nfunction stringToRegexp (path, keys, options) {\r\n return tokensToRegExp(parse(path, options), keys, options)\r\n}\r\n\r\n/**\r\n * Expose a function for taking tokens and returning a RegExp.\r\n *\r\n * @param {!Array} tokens\r\n * @param {Array=} keys\r\n * @param {Object=} options\r\n * @return {!RegExp}\r\n */\r\nfunction tokensToRegExp (tokens, keys, options) {\r\n options = options || {}\r\n\r\n var strict = options.strict\r\n var start = options.start !== false\r\n var end = options.end !== false\r\n var delimiter = options.delimiter || DEFAULT_DELIMITER\r\n var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|')\r\n var route = start ? '^' : ''\r\n\r\n // Iterate over the tokens and create our regexp string.\r\n for (var i = 0; i < tokens.length; i++) {\r\n var token = tokens[i]\r\n\r\n if (typeof token === 'string') {\r\n route += escapeString(token)\r\n } else {\r\n var capture = token.repeat\r\n ? '(?:' + token.pattern + ')(?:' + escapeString(token.delimiter) + '(?:' + token.pattern + '))*'\r\n : token.pattern\r\n\r\n if (keys) keys.push(token)\r\n\r\n if (token.optional) {\r\n if (!token.prefix) {\r\n route += '(' + capture + ')?'\r\n } else {\r\n route += '(?:' + escapeString(token.prefix) + '(' + capture + '))?'\r\n }\r\n } else {\r\n route += escapeString(token.prefix) + '(' + capture + ')'\r\n }\r\n }\r\n }\r\n\r\n if (end) {\r\n if (!strict) route += '(?:' + escapeString(delimiter) + ')?'\r\n\r\n route += endsWith === '$' ? '$' : '(?=' + endsWith + ')'\r\n } else {\r\n var endToken = tokens[tokens.length - 1]\r\n var isEndDelimited = typeof endToken === 'string'\r\n ? endToken[endToken.length - 1] === delimiter\r\n : endToken === undefined\r\n\r\n if (!strict) route += '(?:' + escapeString(delimiter) + '(?=' + endsWith + '))?'\r\n if (!isEndDelimited) route += '(?=' + escapeString(delimiter) + '|' + endsWith + ')'\r\n }\r\n\r\n return new RegExp(route, flags(options))\r\n}\r\n\r\n/**\r\n * Normalize the given path string, returning a regular expression.\r\n *\r\n * An empty array can be passed in for the keys, which will hold the\r\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\r\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\r\n *\r\n * @param {(string|RegExp|Array)} path\r\n * @param {Array=} keys\r\n * @param {Object=} options\r\n * @return {!RegExp}\r\n */\r\nfunction pathToRegexp (path, keys, options) {\r\n if (path instanceof RegExp) {\r\n return regexpToRegexp(path, keys)\r\n }\r\n\r\n if (Array.isArray(path)) {\r\n return arrayToRegexp(/** @type {!Array} */ (path), keys, options)\r\n }\r\n\r\n return stringToRegexp(/** @type {string} */ (path), keys, options)\r\n}\r\n\r\n})();\r\n","if (!window.Hjson) {\r\n window.Hjson = {};\r\n\r\n Hjson.parse = function(source) {\r\n // only parse, stripped dsf and comment support\r\n\r\n var text;\r\n var at; // The index of the current character\r\n var ch; // The current character\r\n var escapee = {\r\n '\"': '\"',\r\n \"'\": \"'\",\r\n '\\\\': '\\\\',\r\n '/': '/',\r\n b: '\\b',\r\n f: '\\f',\r\n n: '\\n',\r\n r: '\\r',\r\n t: '\\t'\r\n };\r\n\r\n function resetAt() {\r\n at = 0;\r\n ch = ' ';\r\n }\r\n\r\n function isPunctuatorChar(c) {\r\n return c === '{' || c === '}' || c === '[' || c === ']' || c === ',' || c === ':';\r\n }\r\n\r\n // Call error when something is wrong.\r\n function error(m) {\r\n var i, col=0, line=1;\r\n for (i = at-1; i > 0 && text[i] !== '\\n'; i--, col++) {}\r\n for (; i > 0; i--) if (text[i] === '\\n') line++;\r\n throw new Error(m + \" at line \" + line + \",\" + col + \" >>>\" + text.substr(at-col, 20) + \" ...\");\r\n }\r\n\r\n function next() {\r\n // get the next character.\r\n ch = text.charAt(at);\r\n at++;\r\n return ch;\r\n }\r\n\r\n function peek(offs) {\r\n // range check is not required\r\n return text.charAt(at + offs);\r\n }\r\n\r\n function string(allowML) {\r\n // Parse a string value.\r\n // callers make sure that (ch === '\"' || ch === \"'\")\r\n var string = '';\r\n\r\n // When parsing for string values, we must look for \"/' and \\ characters.\r\n var exitCh = ch;\r\n while (next()) {\r\n if (ch === exitCh) {\r\n next();\r\n if (allowML && exitCh === \"'\" && ch === \"'\" && string.length === 0) {\r\n // ''' indicates a multiline string\r\n next();\r\n return mlString();\r\n } else return string;\r\n }\r\n if (ch === '\\\\') {\r\n next();\r\n if (ch === 'u') {\r\n var uffff = 0;\r\n for (var i = 0; i < 4; i++) {\r\n next();\r\n var c = ch.charCodeAt(0), hex;\r\n if (ch >= '0' && ch <= '9') hex = c - 48;\r\n else if (ch >= 'a' && ch <= 'f') hex = c - 97 + 0xa;\r\n else if (ch >= 'A' && ch <= 'F') hex = c - 65 + 0xa;\r\n else error(\"Bad \\\\u char \" + ch);\r\n uffff = uffff * 16 + hex;\r\n }\r\n string += String.fromCharCode(uffff);\r\n } else if (typeof escapee[ch] === 'string') {\r\n string += escapee[ch];\r\n } else break;\r\n } else if (ch === '\\n' || ch === '\\r') {\r\n error(\"Bad string containing newline\");\r\n } else {\r\n string += ch;\r\n }\r\n }\r\n error(\"Bad string\");\r\n }\r\n\r\n function mlString() {\r\n // Parse a multiline string value.\r\n var string = '', triple = 0;\r\n\r\n // we are at ''' +1 - get indent\r\n var indent = 0;\r\n for (;;) {\r\n var c=peek(-indent-5);\r\n if (!c || c === '\\n') break;\r\n indent++;\r\n }\r\n\r\n function skipIndent() {\r\n var skip = indent;\r\n while (ch && ch <= ' ' && ch !== '\\n' && skip-- > 0) next();\r\n }\r\n\r\n // skip white/to (newline)\r\n while (ch && ch <= ' ' && ch !== '\\n') next();\r\n if (ch === '\\n') { next(); skipIndent(); }\r\n\r\n // When parsing multiline string values, we must look for ' characters.\r\n for (;;) {\r\n if (!ch) {\r\n error(\"Bad multiline string\");\r\n } else if (ch === '\\'') {\r\n triple++;\r\n next();\r\n if (triple === 3) {\r\n if (string.slice(-1) === '\\n') string=string.slice(0, -1); // remove last EOL\r\n return string;\r\n } else continue;\r\n } else {\r\n while (triple > 0) {\r\n string += '\\'';\r\n triple--;\r\n }\r\n }\r\n if (ch === '\\n') {\r\n string += '\\n';\r\n next();\r\n skipIndent();\r\n } else {\r\n if (ch !== '\\r') string += ch;\r\n next();\r\n }\r\n }\r\n }\r\n\r\n function keyname() {\r\n // quotes for keys are optional in Hjson\r\n // unless they include {}[],: or whitespace.\r\n\r\n if (ch === '\"' || ch === \"'\") return string(false);\r\n\r\n var name = \"\", start = at, space = -1;\r\n for (;;) {\r\n if (ch === ':') {\r\n if (!name) error(\"Found ':' but no key name (for an empty key name use quotes)\");\r\n else if (space >=0 && space !== name.length) { at = start + space; error(\"Found whitespace in your key name (use quotes to include)\"); }\r\n return name;\r\n } else if (ch <= ' ') {\r\n if (!ch) error(\"Found EOF while looking for a key name (check your syntax)\");\r\n else if (space < 0) space = name.length;\r\n } else if (isPunctuatorChar(ch)) {\r\n error(\"Found '\" + ch + \"' where a key name was expected (check your syntax or use quotes if the key name includes {}[],: or whitespace)\");\r\n } else {\r\n name += ch;\r\n }\r\n next();\r\n }\r\n }\r\n\r\n function white() {\r\n while (ch) {\r\n // Skip whitespace.\r\n while (ch && ch <= ' ') next();\r\n // Hjson allows comments\r\n if (ch === '#' || ch === '/' && peek(0) === '/') {\r\n while (ch && ch !== '\\n') next();\r\n } else if (ch === '/' && peek(0) === '*') {\r\n next(); next();\r\n while (ch && !(ch === '*' && peek(0) === '/')) next();\r\n if (ch) { next(); next(); }\r\n } else break;\r\n }\r\n }\r\n\r\n function tfnns() {\r\n // Hjson strings can be quoteless\r\n // returns string, true, false, or null.\r\n var value = ch;\r\n if (isPunctuatorChar(ch))\r\n error(\"Found a punctuator character '\" + ch + \"' when expecting a quoteless string (check your syntax)\");\r\n\r\n for(;;) {\r\n next();\r\n // (detection of ml strings was moved to string())\r\n var isEol = ch === '\\r' || ch === '\\n' || ch === '';\r\n if (isEol ||\r\n ch === ',' || ch === '}' || ch === ']' ||\r\n ch === '#' ||\r\n ch === '/' && (peek(0) === '/' || peek(0) === '*')\r\n ) {\r\n // this tests for the case of {true|false|null|num}\r\n // followed by { ',' | '}' | ']' | '#' | '//' | '/*' }\r\n // which needs to be parsed as the specified value\r\n var chf = value[0];\r\n switch (chf) {\r\n case 'f': if (value.trim() === \"false\") return false; break;\r\n case 'n': if (value.trim() === \"null\") return null; break;\r\n case 't': if (value.trim() === \"true\") return true; break;\r\n default:\r\n if (chf === '-' || chf >= '0' && chf <= '9') {\r\n var n = tryParseNumber(value);\r\n if (n !== undefined) return n;\r\n }\r\n }\r\n if (isEol) {\r\n // remove any whitespace at the end (ignored in quoteless strings)\r\n return value.trim();\r\n }\r\n }\r\n value += ch;\r\n }\r\n }\r\n\r\n function tryParseNumber(text, stopAtNext) {\r\n // try to parse a number\r\n \r\n var number, string = '', leadingZeros = 0, testLeading = true;\r\n var at = 0;\r\n var ch;\r\n function next() {\r\n ch = text.charAt(at);\r\n at++;\r\n return ch;\r\n }\r\n \r\n next();\r\n if (ch === '-') {\r\n string = '-';\r\n next();\r\n }\r\n while (ch >= '0' && ch <= '9') {\r\n if (testLeading) {\r\n if (ch == '0') leadingZeros++;\r\n else testLeading = false;\r\n }\r\n string += ch;\r\n next();\r\n }\r\n if (testLeading) leadingZeros--; // single 0 is allowed\r\n if (ch === '.') {\r\n string += '.';\r\n while (next() && ch >= '0' && ch <= '9')\r\n string += ch;\r\n }\r\n if (ch === 'e' || ch === 'E') {\r\n string += ch;\r\n next();\r\n if (ch === '-' || ch === '+') {\r\n string += ch;\r\n next();\r\n }\r\n while (ch >= '0' && ch <= '9') {\r\n string += ch;\r\n next();\r\n }\r\n }\r\n \r\n // skip white/to (newline)\r\n while (ch && ch <= ' ') next();\r\n \r\n if (stopAtNext) {\r\n // end scan if we find a punctuator character like ,}] or a comment\r\n if (ch === ',' || ch === '}' || ch === ']' ||\r\n ch === '#' || ch === '/' && (text[at] === '/' || text[at] === '*')) ch = 0;\r\n }\r\n \r\n number = +string;\r\n if (ch || leadingZeros || !isFinite(number)) return undefined;\r\n else return number;\r\n }\r\n \r\n function errorClosingHint(value) {\r\n function search(value, ch) {\r\n var i, k, length, res;\r\n switch (typeof value) {\r\n case 'string':\r\n if (value.indexOf(ch) >= 0) res = value;\r\n break;\r\n case 'object':\r\n if (Object.prototype.toString.apply(value) === '[object Array]') {\r\n for (i = 0, length = value.length; i < length; i++) {\r\n res=search(value[i], ch) || res;\r\n }\r\n } else {\r\n for (k in value) {\r\n if (!Object.prototype.hasOwnProperty.call(value, k)) continue;\r\n res=search(value[k], ch) || res;\r\n }\r\n }\r\n }\r\n return res;\r\n }\r\n \r\n function report(ch) {\r\n var possibleErr=search(value, ch);\r\n if (possibleErr) {\r\n return \"found '\"+ch+\"' in a string value, your mistake could be with:\\n\"+\r\n \" > \"+possibleErr+\"\\n\"+\r\n \" (unquoted strings contain everything up to the next line!)\";\r\n } else return \"\";\r\n }\r\n \r\n return report('}') || report(']');\r\n }\r\n\r\n function array() {\r\n // Parse an array value.\r\n // assuming ch === '['\r\n \r\n var array = [];\r\n try {\r\n next();\r\n white();\r\n if (ch === ']') {\r\n next();\r\n return array; // empty array\r\n }\r\n \r\n while (ch) {\r\n array.push(value());\r\n white();\r\n // in Hjson the comma is optional and trailing commas are allowed\r\n // note that we do not keep comments before the , if there are any\r\n if (ch === ',') { next(); white(); }\r\n if (ch === ']') {\r\n next();\r\n return array;\r\n }\r\n white();\r\n }\r\n \r\n error(\"End of input while parsing an array (missing ']')\");\r\n } catch (e) {\r\n e.hint=e.hint||errorClosingHint(array);\r\n throw e;\r\n }\r\n }\r\n\r\n function object(withoutBraces) {\r\n // Parse an object value.\r\n \r\n var key = \"\", object = {};\r\n \r\n try {\r\n if (!withoutBraces) {\r\n // assuming ch === '{'\r\n next();\r\n }\r\n\r\n white();\r\n if (ch === '}' && !withoutBraces) {\r\n next();\r\n return object; // empty object\r\n }\r\n while (ch) {\r\n key = keyname();\r\n white();\r\n if (ch !== ':') error(\"Expected ':' instead of '\" + ch + \"'\");\r\n next();\r\n // duplicate keys overwrite the previous value\r\n object[key] = value();\r\n white();\r\n // in Hjson the comma is optional and trailing commas are allowed\r\n // note that we do not keep comments before the , if there are any\r\n if (ch === ',') { next(); white(); }\r\n if (ch === '}' && !withoutBraces) {\r\n next();\r\n return object;\r\n }\r\n white();\r\n }\r\n \r\n if (withoutBraces) return object;\r\n else error(\"End of input while parsing an object (missing '}')\");\r\n } catch (e) {\r\n e.hint=e.hint||errorClosingHint(object);\r\n throw e;\r\n }\r\n }\r\n\r\n function value() {\r\n // Parse a Hjson value. It could be an object, an array, a string, a number or a word.\r\n \r\n white();\r\n switch (ch) {\r\n case '{': return object();\r\n case '[': return array();\r\n case \"'\":\r\n case '\"': return string(true);\r\n default: return tfnns();\r\n }\r\n }\r\n\r\n function checkTrailing(v) {\r\n white();\r\n if (ch) error(\"Syntax error, found trailing characters\");\r\n return v;\r\n }\r\n\r\n function rootValue() {\r\n white();\r\n switch (ch) {\r\n case '{': return checkTrailing(object());\r\n case '[': return checkTrailing(array());\r\n default: return checkTrailing(value());\r\n }\r\n }\r\n\r\n if (typeof source!==\"string\") throw new Error(\"source is not a string\");\r\n text = source;\r\n resetAt();\r\n return rootValue();\r\n };\r\n}","dmx.Flow = dmx.createClass({\r\n constructor: function (parent) {\r\n if (!(this instanceof dmx.Flow)) {\r\n return new dmx.Flow(parent);\r\n }\r\n\r\n if (!window.Promise) {\r\n console.warn('Promises are not supported, flows can not be used');\r\n }\r\n\r\n this._execStep = this._execStep.bind(this);\r\n\r\n this.scope = new dmx.DataScope({}, parent);\r\n this.output = {};\r\n },\r\n\r\n run: function (flow) {\r\n this.output = {};\r\n\r\n return this._exec(flow.exec || flow).then(() => {\r\n if (dmx.debug) {\r\n console.debug('finished', this.output);\r\n }\r\n return this.output;\r\n });\r\n },\r\n\r\n _each: function (arr, fn) {\r\n return Promise.resolve(arr).then((arr) => {\r\n arr = Array.isArray(arr) ? arr : [arr];\r\n\r\n return arr\r\n .reduce((prev, curr, i) => {\r\n return prev.then(() => {\r\n return fn(curr, i, arr.length).then((result) => {\r\n if (result) {\r\n arr[i] = result;\r\n }\r\n });\r\n });\r\n }, Promise.resolve())\r\n .then(() => {\r\n return arr;\r\n });\r\n });\r\n },\r\n\r\n _exec: function (flow) {\r\n if (flow.steps) {\r\n var promise = this._each(flow.steps, this._execStep);\r\n\r\n if (flow.catch) {\r\n promise.catch((err) => {\r\n return this._each(flow.catch, self._execStep);\r\n });\r\n }\r\n\r\n return promise;\r\n }\r\n\r\n return this._each(flow, this._execStep);\r\n },\r\n\r\n _execStep: function (step) {\r\n for (let name in step) {\r\n if (step.hasOwnProperty(name) && dmx.__actions[name]) {\r\n const action = dmx.__actions[name].bind(this);\r\n const options = step[name];\r\n const timerName = name + Date.now();\r\n\r\n if (dmx.debug) {\r\n console.debug('exec action', name, options);\r\n console.time(timerName);\r\n }\r\n\r\n if (options.disabled) {\r\n return Promise.resolve();\r\n }\r\n\r\n return Promise.resolve(action(options)).then((output) => {\r\n if (dmx.debug) {\r\n console.debug('finished exec action', name, options);\r\n console.timeEnd(timerName);\r\n }\r\n\r\n if (options.name) {\r\n if (dmx.debug) {\r\n console.debug('set data', options.name, output);\r\n }\r\n\r\n this.scope.set(options.name, output);\r\n\r\n if (options.output) {\r\n if (dmx.debug) {\r\n console.debug('set output', options.name, output);\r\n }\r\n this.output[options.name] = output;\r\n }\r\n }\r\n });\r\n } else {\r\n throw new Error('Action ' + name + ' was not found.');\r\n }\r\n }\r\n },\r\n\r\n parse: function (value) {\r\n if (value == null) return value;\r\n\r\n value = value.valueOf();\r\n\r\n if (typeof value == 'object') {\r\n var obj = value.slice ? [] : {};\r\n\r\n for (var key in value) {\r\n if (value.hasOwnProperty(key)) {\r\n obj[key] = this.parse(value[key], this.scope);\r\n }\r\n }\r\n\r\n return obj;\r\n }\r\n\r\n if (typeof value == 'string' && value.indexOf('{{') != -1) {\r\n return dmx.parse(value, this.scope);\r\n }\r\n\r\n return value;\r\n },\r\n});\r\n\r\ndmx.Flow.run = function (flow, data) {\r\n var instance = new dmx.Flow(data);\r\n return instance.run(flow);\r\n};\r\n","dmx.Component(\"app\", {\r\n\r\n initialData: {\r\n query: {},\r\n },\r\n\r\n events: {\r\n ready: Event,\r\n load: Event,\r\n },\r\n\r\n init () {\r\n this.dispatchLoad = this.dispatchEvent.bind(this, \"load\");\r\n this._parseQuery = this._parseQuery.bind(this);\r\n\r\n // is this remove needed?\r\n window.addEventListener(\"load\", this.dispatchLoad, { once: true });\r\n window.addEventListener(\"load\", this._parseQuery);\r\n window.addEventListener(\"popstate\", this._parseQuery);\r\n window.addEventListener(\"pushstate\", this._parseQuery);\r\n window.addEventListener(\"replacestate\", this._parseQuery);\r\n\r\n this._parseQuery();\r\n\r\n queueMicrotask(() => this.dispatchEvent(\"ready\"));\r\n },\r\n\r\n destroy () {\r\n window.removeEventListener(\"load\", this.dispatchLoad);\r\n window.removeEventListener(\"load\", this._parseQuery);\r\n window.removeEventListener(\"popstate\", this._parseQuery);\r\n window.removeEventListener(\"pushstate\", this._parseQuery);\r\n window.removeEventListener(\"replacestate\", this._parseQuery);\r\n },\r\n\r\n _parseQuery () {\r\n let querystring = \"\";\r\n\r\n if (window.location.search) {\r\n querystring = window.location.search.slice(1);\r\n } else if (window.location.hash.indexOf(\"?\")) {\r\n querystring = window.location.hash.slice(\r\n window.location.hash.indexOf(\"?\") + 1\r\n );\r\n if (querystring.indexOf(\"#\") > 0) {\r\n querystring = querystring.slice(0, querystring.indexOf(\"#\"));\r\n }\r\n }\r\n\r\n let query = querystring.split(\"&\").reduce(function (query, part) {\r\n var p = part.replace(/\\+/g, \" \").split(\"=\");\r\n if (p[0]) {\r\n query[decodeURIComponent(p[0])] = decodeURIComponent(p[1] || \"\");\r\n }\r\n return query;\r\n }, {});\r\n\r\n let base = document.querySelector('meta[name=\"ac:base\"]');\r\n let route = document.querySelector('meta[name=\"ac:route\"]');\r\n if (route && route.content) {\r\n let keys = [];\r\n let path = route.content;\r\n\r\n if (base && base.content) {\r\n path = base.content.replace(/\\/$/, \"\") + path;\r\n }\r\n\r\n let re = dmx.pathToRegexp(path, keys, { end: false });\r\n let match = re.exec(decodeURI(window.location.pathname));\r\n\r\n if (match) {\r\n keys.forEach(function (key, index) {\r\n query[key.name] = match[index + 1];\r\n });\r\n }\r\n }\r\n\r\n this.set(\"query\", query);\r\n },\r\n\r\n});\r\n","dmx.Component('form', {\r\n\r\n attributes: {\r\n novalidate: {\r\n type: Boolean,\r\n default: false\r\n },\r\n },\r\n\r\n methods: {\r\n submit (direct) {\r\n this._submit(direct);\r\n },\r\n\r\n reset () {\r\n this._reset();\r\n },\r\n\r\n validate () {\r\n this._validate();\r\n },\r\n },\r\n\r\n events: {\r\n invalid: Event,\r\n submit: Event,\r\n },\r\n\r\n init (node) {\r\n this._submitHandler = this._submitHandler.bind(this);\r\n this._resetHandler = this._resetHandler.bind(this);\r\n\r\n node.noValidate = true;\r\n node.addEventListener('submit', this._submitHandler);\r\n node.addEventListener('reset', this._resetHandler);\r\n },\r\n\r\n destroy () {\r\n this.$node.removeEventListener('submit', this._submitHandler);\r\n this.$node.removeEventListener('reset', this._resetHandler);\r\n },\r\n\r\n _submitHandler (event) {\r\n event.preventDefault();\r\n this._submit();\r\n },\r\n\r\n _resetHandler (event) {\r\n // remove this when validation is rewritten\r\n if (dmx.validateReset) dmx.validateReset(this.$node);\r\n if (window.grecaptcha && this.$node.querySelector('.g-recaptcha')) {\r\n grecaptcha.reset();\r\n }\r\n },\r\n\r\n _submit (direct) {\r\n if (direct) {\r\n return this._formSubmit();\r\n }\r\n\r\n if (this.props.novalidate || this._validate()) {\r\n if (this.dispatchEvent('submit', { cancelable: true })) {\r\n this._formSubmit();\r\n }\r\n } else {\r\n this.dispatchEvent('invalid');\r\n this._focusFirstInvalid();\r\n }\r\n },\r\n\r\n _reset () {\r\n this._formReset();\r\n },\r\n\r\n _validate () {\r\n if (dmx.validate) return dmx.validate(this.$node);\r\n Array.from(this.$node.elements).forEach(node => node.dirty = true);\r\n return this.$node.checkValidity();\r\n },\r\n\r\n _formSubmit () {\r\n HTMLFormElement.prototype.submit.call(this.$node);\r\n },\r\n\r\n _formReset () {\r\n HTMLFormElement.prototype.reset.call(this.$node);\r\n },\r\n\r\n _focusFirstInvalid () {\r\n const elm = Array.from(this.$node.elements).find(elm => !elm.validity.valid);\r\n if (elm) elm.focus();\r\n },\r\n\r\n _parseJsonForm () {\r\n const result = {};\r\n\r\n for (const element of this.$node.elements) {\r\n if (element.name && !element.disabled) {\r\n const steps = parseSteps(element.name.replace(/\\[\\]$/, \"\"));\r\n let context = result;\r\n\r\n for (const step of steps) {\r\n const type = element.type;\r\n\r\n if (type == \"number\") {\r\n if (element.value) {\r\n context = setValue(\r\n context,\r\n step,\r\n context[step.key],\r\n +element.value\r\n );\r\n }\r\n } else if (type == \"radio\" || type == \"checkbox\") {\r\n if (element.getAttribute(\"value\")) {\r\n if (element.checked) {\r\n context = setValue(\r\n context,\r\n step,\r\n context[step.key],\r\n element.value\r\n );\r\n }\r\n } else {\r\n context = setValue(\r\n context,\r\n step,\r\n context[step.key],\r\n element.checked\r\n );\r\n }\r\n } else if (type == \"select-multiple\") {\r\n context = setValue(\r\n context,\r\n step,\r\n context[step.key],\r\n Array.from(element.selectedOptions).map((opt) => opt.value)\r\n );\r\n } else {\r\n context = setValue(context, step, context[step.key], element.value);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n\r\n function parseSteps(name) {\r\n const steps = [],\r\n org = name;\r\n const re = /^\\[([^\\]]*)\\]/;\r\n const reNumeric = /^\\d+$/;\r\n\r\n name = name.replace(/^([^\\[]+)/, (m, p1) => {\r\n steps.push({ type: \"object\", key: p1 });\r\n return \"\";\r\n });\r\n\r\n if (!name) {\r\n steps[0].last = true;\r\n return steps;\r\n }\r\n\r\n while (name) {\r\n if (re.test(name)) {\r\n name = name.replace(re, (m, p1) => {\r\n if (!p1) {\r\n steps[steps.length - 1].append = true;\r\n } else if (reNumeric.test(p1)) {\r\n steps.push({ type: \"array\", key: +p1 });\r\n } else {\r\n steps.push({ type: \"object\", key: p1 });\r\n }\r\n\r\n return \"\";\r\n });\r\n\r\n continue;\r\n }\r\n\r\n return { type: \"object\", key: org, last: true };\r\n }\r\n\r\n for (let i = 0, n = steps.length; i < n; i++) {\r\n const step = steps[i];\r\n\r\n if (i + 1 < n) step.nextType = steps[i + 1].type;\r\n else step.last = true;\r\n }\r\n\r\n return steps;\r\n }\r\n\r\n function setValue(context, step, current, value) {\r\n if (step.last) {\r\n if (current === undefined) {\r\n context[step.key] = step.append ? [value] : value;\r\n } else if (Array.isArray(current)) {\r\n context[step.key].push(value);\r\n } else if (typeof current == \"object\") {\r\n return setValue(\r\n current,\r\n { type: \"object\", key: \"\", last: true },\r\n current[\"\"],\r\n value\r\n );\r\n } else {\r\n context[step.key] = [current, value];\r\n }\r\n\r\n return context;\r\n }\r\n\r\n if (current === undefined) {\r\n return (context[step.key] = step.nextType == \"array\" ? [] : {});\r\n } else if (Array.isArray(current)) {\r\n if (step.nextType == \"array\") return current;\r\n const obj = {};\r\n for (let i = 0, n = current.length; i < n; i++) {\r\n if (current[i] !== undefined) obj[i] = current[i];\r\n }\r\n return (context[step.key] = obj);\r\n } else if (typeof current == \"object\") {\r\n return context[step.key];\r\n }\r\n\r\n return (context[step.key] = { \"\": current });\r\n }\r\n },\r\n\r\n});\r\n","dmx.Component('form-element', {\r\n\r\n initialData: {\r\n disabled: false,\r\n focused: false,\r\n invalid: false,\r\n validationMessage: '',\r\n value: '',\r\n },\r\n\r\n attributes: {\r\n value: {\r\n type: String,\r\n default: '',\r\n alwaysUpdate: true,\r\n },\r\n\r\n disabled: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n\r\n methods: {\r\n setValue (value) {\r\n this._setValue(value);\r\n },\r\n\r\n focus () {\r\n this._focus();\r\n },\r\n\r\n disable (disable) {\r\n this._disable(disable);\r\n },\r\n\r\n validate () {\r\n this._validate();\r\n },\r\n },\r\n\r\n events: {\r\n updated: Event,\r\n changed: Event,\r\n },\r\n\r\n init (node) {\r\n this._inputHandler = this._inputHandler.bind(this);\r\n this._changeHandler = this._changeHandler.bind(this);\r\n this._invalidHandler = this._invalidHandler.bind(this);\r\n this._resetHandler = this._resetHandler.bind(this);\r\n this._focusHandler = this._focusHandler.bind(this);\r\n this._blurHandler = this._blurHandler.bind(this);\r\n\r\n node.value = this.props.value || '';\r\n node.defaultValue = node.value ;\r\n \r\n node.addEventListener('input', this._inputHandler);\r\n node.addEventListener('change', this._changeHandler);\r\n node.addEventListener('invalid', this._invalidHandler);\r\n node.addEventListener('focus', this._focusHandler);\r\n node.addEventListener('blur', this._blurHandler);\r\n \r\n if (node.form) {\r\n this._form = node.form;\r\n this._form.addEventListener('reset', this._resetHandler);\r\n }\r\n\r\n if (this.props.disabled) {\r\n this._disable(this.props.disabled);\r\n }\r\n \r\n this.set('value', this.props.value);\r\n if (this.$node === document.activeElement) {\r\n this.set('focused', true);\r\n }\r\n },\r\n\r\n destroy () {\r\n this.$node.removeEventListener('input', this._inputHandler);\r\n this.$node.removeEventListener('change', this._changeHandler);\r\n this.$node.removeEventListener('invalid', this._invalidHandler);\r\n this.$node.removeEventListener('focus', this._focusHandler);\r\n this.$node.removeEventListener('blur', this._blurHandler);\r\n if (this._form) {\r\n this._form.removeEventListener('reset', this._resetHandler);\r\n this._form = null;\r\n }\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has('value')) {\r\n this._setValue(this.props.value, true);\r\n }\r\n\r\n if (updatedProps.has('disabled')) {\r\n this._disable(this.props.disabled);\r\n }\r\n },\r\n\r\n _setValue (value, defaultValue) {\r\n this.$node.value = value || '';\r\n if (defaultValue) this.$node.defaultValue = value || '';\r\n this.set('value', value);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n },\r\n\r\n _focus () {\r\n this.$node.focus();\r\n },\r\n\r\n _disable (disable) {\r\n this.$node.disabled = disable;\r\n this.set('disabled', this.$node.disabled);\r\n },\r\n\r\n _validate () {\r\n dmx.validate(this.$node);\r\n\r\n if (this.$node.dirty) {\r\n this.set({\r\n invalid: !this.$node.validity.valid,\r\n validationMessage: this.$node.validationMessage,\r\n });\r\n }\r\n },\r\n\r\n _inputHandler (event) {\r\n if (this.$node.dirty) this._validate();\r\n\r\n dmx.nextTick(() => {\r\n if (!this.$node) return;\r\n if (this.data.value !== this.$node.value) {\r\n this.set('value', this.$node.value);\r\n if (event) this.dispatchEvent('changed');\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n });\r\n },\r\n\r\n _changeHandler (event) {\r\n if (this.$node.dirty) this._validate();\r\n\r\n dmx.nextTick(() => {\r\n if (!this.$node) return;\r\n if (this.data.value !== this.$node.value) {\r\n this.set('value', this.$node.value);\r\n if (event) this.dispatchEvent('changed');\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n });\r\n },\r\n\r\n _invalidHandler (event) {\r\n this.set({\r\n invalid: !this.$node.validity.valid,\r\n validationMessage: this.$node.validationMessage,\r\n });\r\n },\r\n\r\n _resetHandler (event) {\r\n if (!this.$node) return;\r\n this.$node.dirty = false;\r\n this.set({\r\n invalid: false,\r\n validationMessage: '',\r\n });\r\n this._changeHandler(event);\r\n },\r\n\r\n _focusHandler (event) {\r\n this.set('focused', true);\r\n },\r\n\r\n _blurHandler (event) {\r\n this.set('focused', false);\r\n },\r\n\r\n});\r\n","dmx.Component('textarea', {\r\n\r\n extends: 'form-element',\r\n\r\n init (node) {\r\n if (!this.props.value) {\r\n const value = this.$node.value;\r\n this.props.value = value.includes('{{') ? this.parse(value) : value;\r\n }\r\n\r\n dmx.Component('form-element').prototype.init.call(this, node);\r\n },\r\n\r\n});\r\n","dmx.Component('input', { extends: 'form-element' });\r\n","dmx.Component('input-file', {\r\n\r\n extends: 'form-element',\r\n\r\n attributes: {\r\n imageMaxWidth: {\r\n type: Number,\r\n default: null,\r\n },\r\n\r\n imageMaxHeight: {\r\n type: Number,\r\n default: null,\r\n },\r\n\r\n imageType: {\r\n type: String,\r\n default: null, // defaults to original image format\r\n enum: ['png', 'jpeg', 'webp'],\r\n },\r\n\r\n imageQuality: {\r\n type: Number,\r\n default: null,\r\n },\r\n },\r\n\r\n initialData: {\r\n file: null,\r\n },\r\n\r\n _imageTypes: {\r\n png: 'image/png', \r\n jpeg: 'image/jpeg',\r\n webp: 'image/webp',\r\n 'image/png': 'image/png',\r\n 'image/jpeg': 'image/jpeg',\r\n 'image/webp': 'image/webp',\r\n },\r\n\r\n _imageExtensions: {\r\n 'image/png': 'png',\r\n 'image/jpeg': 'jpg',\r\n 'image/webp': 'webp',\r\n },\r\n\r\n _setValue (value) {\r\n console.warn('Can not set value of a file input!');\r\n },\r\n\r\n _changeHandler (event) {\r\n dmx.Component('form-element').prototype._changeHandler.call(this, event);\r\n\r\n this._updateData();\r\n \r\n if (this.$node.files.length && (this.props.imageMaxWidth || this.props.imageMaxHeight || this.props.imageType)) {\r\n this._resizeImage();\r\n }\r\n },\r\n\r\n _resizeImage () {\r\n const file = this.$node.files[0];\r\n\r\n if (file && file.type.startsWith('image/')) {\r\n const blobUrl = URL.createObjectURL(file);\r\n const img = new Image();\r\n img.src = blobUrl;\r\n img.onerror = () => URL.revokeObjectURL(blobUrl);\r\n img.onload = () => {\r\n URL.revokeObjectURL(blobUrl);\r\n\r\n const { imageMaxWidth, imageMaxHeight, imageType, imageQuality } = this.props;\r\n\r\n let width = img.width;\r\n let height = img.height;\r\n let ratio = width / height;\r\n let needResize = false;\r\n\r\n if (imageMaxWidth && width > imageMaxWidth) {\r\n width = imageMaxWidth;\r\n height = ~~(width / ratio);\r\n needResize = true;\r\n }\r\n\r\n if (imageMaxHeight && height > imageMaxHeight) {\r\n height = imageMaxHeight;\r\n width = ~~(height * ratio);\r\n needResize = true;\r\n }\r\n\r\n const newType = imageType ? this._imageTypes[imageType] : file.type;\r\n\r\n if (newType !== file.type || needResize) {\r\n const canvas = document.createElement('canvas');\r\n const ctx = canvas.getContext('2d');\r\n \r\n canvas.width = width;\r\n canvas.height = height;\r\n \r\n ctx.drawImage(img, 0, 0, width, height);\r\n \r\n canvas.toBlob(blob => {\r\n if (blob == null) {\r\n return console.error('Could not resize image!');\r\n }\r\n const container = new DataTransfer();\r\n const newName = file.name.replace(/\\.\\w+$/, '.' + this._imageExtensions[blob.type]);\r\n const newFile = new File([blob], newName, { type: blob.type });\r\n container.items.add(newFile);\r\n this.$node.files = container.files;\r\n this._updateData();\r\n }, newType, imageQuality ? imageQuality / 100 : undefined);\r\n }\r\n };\r\n }\r\n },\r\n\r\n _updateData () {\r\n let data = null;\r\n \r\n if (this.$node.files.length) {\r\n const self = this;\r\n const file = this.$node.files[0];\r\n\r\n data = {\r\n date: (file.lastModified ? new Date(file.lastModified) : file.lastModifiedDate).toISOString(),\r\n name: file.name,\r\n size: file.size,\r\n type: file.type,\r\n get dataUrl () {\r\n if (!file._dataUrl) {\r\n dmx.fileUtils.blobToDataURL(file).then(dataUrl => {\r\n file._dataUrl = dataUrl;\r\n self.set('file', Object.assign({}, data, { dataUrl }));\r\n }).catch(error => {\r\n console.error(error);\r\n });\r\n }\r\n\r\n return null;\r\n },\r\n };\r\n }\r\n\r\n this.set('file', data);\r\n }\r\n\r\n});\r\n","dmx.Component('input-file-multiple', {\r\n\r\n extends: 'form-element',\r\n\r\n attributes: {\r\n imageMaxWidth: {\r\n type: Number,\r\n default: null,\r\n },\r\n\r\n imageMaxHeight: {\r\n type: Number,\r\n default: null,\r\n },\r\n\r\n imageType: {\r\n type: String,\r\n default: null, // defaults to original image format\r\n enum: ['png', 'jpeg', 'webp'],\r\n },\r\n\r\n imageQuality: {\r\n type: Number,\r\n default: null,\r\n },\r\n },\r\n\r\n initialData: {\r\n files: [],\r\n },\r\n\r\n _imageTypes: {\r\n png: 'image/png', \r\n jpeg: 'image/jpeg',\r\n webp: 'image/webp',\r\n 'image/png': 'image/png',\r\n 'image/jpeg': 'image/jpeg',\r\n 'image/webp': 'image/webp',\r\n },\r\n\r\n _imageExtensions: {\r\n 'image/png': 'png',\r\n 'image/jpeg': 'jpg',\r\n 'image/webp': 'webp',\r\n },\r\n\r\n _setValue (value) {\r\n console.warn('Can not set value of a file input!');\r\n },\r\n\r\n _changeHandler (event) {\r\n dmx.Component('form-element').prototype._changeHandler.call(this, event);\r\n\r\n this._updateData();\r\n\r\n if (this.$node.files.length && (this.props.imageMaxWidth || this.props.imageMaxHeight || this.props.imageType)) {\r\n this._resizeImages();\r\n }\r\n },\r\n\r\n _resizeImages () {\r\n const files = Array.from(this.$node.files);\r\n\r\n Promise.all(files.map(file => {\r\n return new Promise(resolve => {\r\n if (!file.type.startsWith('image/')) {\r\n resolve(file);\r\n return;\r\n }\r\n\r\n const blobUrl = URL.createObjectURL(file);\r\n const img = new Image();\r\n img.src = blobUrl;\r\n img.onerror = () => URL.revokeObjectURL(blobUrl);\r\n img.onload = () => {\r\n URL.revokeObjectURL(blobUrl);\r\n\r\n const { imageMaxWidth, imageMaxHeight, imageType, imageQuality } = this.props;\r\n\r\n let width = img.width;\r\n let height = img.height;\r\n let ratio = width / height;\r\n let needResize = false;\r\n\r\n if (imageMaxWidth && width > imageMaxWidth) {\r\n width = imageMaxWidth;\r\n height = ~~(width / ratio);\r\n needResize = true;\r\n }\r\n\r\n if (imageMaxHeight && height > imageMaxHeight) {\r\n height = imageMaxHeight;\r\n width = ~~(height * ratio);\r\n needResize = true;\r\n }\r\n\r\n const newType = imageType ? this._imageTypes[imageType] : file.type;\r\n \r\n if (newType !== file.type || needResize) {\r\n const canvas = document.createElement('canvas');\r\n const ctx = canvas.getContext('2d');\r\n \r\n canvas.width = width;\r\n canvas.height = height;\r\n \r\n ctx.drawImage(img, 0, 0, width, height);\r\n \r\n canvas.toBlob(blob => {\r\n if (blob == null) {\r\n return console.error('Could not resize image!');\r\n }\r\n const newName = file.name.replace(/\\.\\w+$/, '.' + this._imageExtensions[blob.type]);\r\n const newFile = new File([blob], newName, { type: blob.type });\r\n resolve(newFile);\r\n }, newType, imageQuality ? imageQuality / 100 : undefined);\r\n } else {\r\n resolve(file);\r\n }\r\n };\r\n });\r\n })).then(files => {\r\n const container = new DataTransfer();\r\n for (let file of files) {\r\n container.items.add(file);\r\n }\r\n this.$node.files = container.files;\r\n this._updateData();\r\n });\r\n },\r\n\r\n _updateData () {\r\n let files = [];\r\n \r\n if (this.$node.files.length) {\r\n const self = this;\r\n\r\n files = Array.from(this.$node.files).map((file, index) => {\r\n const data = {\r\n date: (file.lastModified ? new Date(file.lastModified) : file.lastModifiedDate).toISOString(),\r\n name: file.name,\r\n size: file.size,\r\n type: file.type,\r\n get dataUrl () {\r\n if (!file._dataUrl) {\r\n loading = true;\r\n dmx.fileUtils.blobToDataURL(file).then(dataUrl => {\r\n file._dataUrl = dataUrl;\r\n files = dmx.clone(files);\r\n files[index].dataUrl = dataUrl;\r\n self.set('files', files);\r\n }).catch(error => {\r\n console.error(error);\r\n });\r\n }\r\n\r\n return null;\r\n },\r\n };\r\n\r\n return data;\r\n });\r\n }\r\n\r\n this.set('files', files);\r\n },\r\n\r\n});\r\n","dmx.Component('button', {\r\n\r\n extends: 'form-element',\r\n\r\n attributes: {\r\n type: {\r\n type: String,\r\n default: 'button',\r\n enum: ['button', 'reset', 'submit'],\r\n },\r\n },\r\n\r\n init (node) {\r\n dmx.Component('form-element').prototype.init.call(this, node);\r\n \r\n node.type = this.props.type;\r\n },\r\n\r\n});\r\n","dmx.Component('radio', {\r\n\r\n extends: 'form-element',\r\n\r\n initialData: {\r\n checked: false,\r\n },\r\n\r\n attributes: {\r\n checked: {\r\n type: Boolean,\r\n default: false,\r\n alwaysUpdate: true,\r\n },\r\n },\r\n\r\n methods: {\r\n select (check, triggerEvent) {\r\n this._select(check);\r\n if (triggerEvent) {\r\n dmx.nextTick(() => {\r\n this.dispatchEvent('changed');\r\n this.dispatchEvent(this.$node.checked ? 'checked' : 'unchecked');\r\n });\r\n }\r\n },\r\n },\r\n\r\n events: {\r\n checked: Event,\r\n unchecked: Event,\r\n },\r\n\r\n init (node) {\r\n dmx.Component('form-element').prototype.init.call(this, node);\r\n\r\n node.type = 'radio';\r\n node.checked = this.props.checked;\r\n node.defaultChecked = this.props.checked;\r\n\r\n if (this.props.checked) {\r\n this.set('checked', true);\r\n }\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n dmx.Component('form-element').prototype.performUpdate.call(this, updatedProps);\r\n\r\n if (updatedProps.has('checked')) {\r\n if (this.$node.checked != this.props.checked) {\r\n this.$node.defaultChecked = this.props.checked;\r\n this.$node.checked = this.props.checked;\r\n this.set('checked', this.props.checked);\r\n this.$node.dispatchEvent(new Event('radio', { bubbles: true }));\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n }\r\n },\r\n\r\n _select (check) {\r\n this.$node.checked = (check !== false);\r\n this.set('checked', this.$node.checked);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n },\r\n\r\n _changeHandler (event) {\r\n if (this.$node.dirty) this._validate();\r\n\r\n dmx.nextTick(() => {\r\n if (!this.$node) return;\r\n this.set('checked', this.$node.checked);\r\n this.dispatchEvent('changed');\r\n this.dispatchEvent(this.$node.checked ? 'checked' : 'unchecked');\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n\r\n // trigger change on other radios with the same name and current form\r\n if (this.$node.checked && this.$node.form) {\r\n for (const elem of this.$node.form.elements) {\r\n if (elem == this.$node) continue;\r\n if (elem.type == 'radio' && elem.name == this.$node.name) {\r\n elem.dispatchEvent(new Event('change', { bubbles: true }));\r\n }\r\n }\r\n }\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('radio-group', {\r\n\r\n initialData: {\r\n value: null,\r\n },\r\n\r\n attributes: {\r\n value: {\r\n type: String,\r\n default: null,\r\n alwaysUpdate: true,\r\n },\r\n },\r\n\r\n methods: {\r\n setValue (value) {\r\n this._setValue(value);\r\n },\r\n },\r\n\r\n events: {\r\n updated: Event,\r\n },\r\n\r\n init (node) {\r\n this._changeHandler = this._changeHandler.bind(this);\r\n\r\n node.addEventListener('change', this._changeHandler);\r\n node.addEventListener('radio', this._changeHandler);\r\n },\r\n\r\n render (node) {\r\n this.$parse();\r\n\r\n this._setValue(this.props.value, true);\r\n\r\n this._mutationObserver = new MutationObserver((mutationList) => {\r\n let value = this.props.value;\r\n if (value == null) value = '';\r\n value = value.toString();\r\n\r\n for (let mutation of mutationList) {\r\n for (let node of mutation.addedNodes) {\r\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\r\n\r\n requestAnimationFrame(() => {\r\n if (node.tagName === 'INPUT' && node.type === 'radio') {\r\n node.checked = node.value == value;\r\n node.defaultChecked = node.checked;\r\n } else {\r\n node.querySelectorAll('input[type=radio]').forEach(checkbox => {\r\n checkbox.checked = node.value == value;\r\n checkbox.defaultChecked = checkbox.checked;\r\n });\r\n }\r\n\r\n requestAnimationFrame(() => {\r\n this._updateValue();\r\n });\r\n });\r\n }\r\n }\r\n });\r\n \r\n this._mutationObserver.observe(node, { subtree: true, childList: true });\r\n },\r\n\r\n destroy () {\r\n this._mutationObserver.disconnect();\r\n this.$node.removeEventListener('change', this._changeHandler);\r\n this.$node.removeEventListener('radio', this._changeHandler);\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has('value')) {\r\n this._setValue(this.props.value, true);\r\n }\r\n },\r\n\r\n _setValue (value, defaultValue) {\r\n if (value == null) value = '';\r\n value = value.toString();\r\n \r\n this._radios().forEach(radio => {\r\n radio.checked = radio.value == value;\r\n if (defaultValue) radio.defaultChecked = radio.checked;\r\n });\r\n\r\n this._updateValue();\r\n },\r\n\r\n _updateValue () {\r\n const value = this._radios().filter(radio => !radio.disabled && radio.checked).map(radio => radio.value);\r\n\r\n if (!dmx.equal(this.data.value, value[0])) {\r\n this.set('value', value[0] || null);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n },\r\n\r\n _radios () {\r\n return Array.from(this.$node.querySelectorAll('input[type=radio]'));\r\n },\r\n\r\n _changeHandler (event) {\r\n this._updateValue();\r\n },\r\n\r\n});\r\n","dmx.Component('checkbox', {\r\n\r\n extends: 'form-element',\r\n\r\n initialData: {\r\n checked: false,\r\n },\r\n\r\n attributes: {\r\n checked: {\r\n type: Boolean,\r\n default: false,\r\n alwaysUpdate: true,\r\n },\r\n },\r\n\r\n methods: {\r\n select (check, triggerEvent) {\r\n this._select(check);\r\n if (triggerEvent) {\r\n dmx.nextTick(() => {\r\n this.dispatchEvent('changed');\r\n this.dispatchEvent(this.$node.checked ? 'checked' : 'unchecked');\r\n });\r\n }\r\n },\r\n },\r\n\r\n events: {\r\n checked: Event,\r\n unchecked: Event,\r\n },\r\n\r\n init (node) {\r\n dmx.Component('form-element').prototype.init.call(this, node);\r\n\r\n node.type = 'checkbox';\r\n node.checked = this.props.checked;\r\n node.defaultChecked = this.props.checked;\r\n\r\n if (this.props.checked) {\r\n this.set('checked', true);\r\n }\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n dmx.Component('form-element').prototype.performUpdate.call(this, updatedProps);\r\n\r\n if (updatedProps.has('checked')) {\r\n if (this.$node.checked != this.props.checked) {\r\n this.$node.defaultChecked = this.props.checked;\r\n this.$node.checked = this.props.checked;\r\n this.set('checked', this.props.checked);\r\n this.$node.dispatchEvent(new Event('checkbox', { bubbles: true }));\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n }\r\n },\r\n\r\n _select (check) {\r\n this.$node.checked = (check !== false);\r\n this.set('checked', this.$node.checked);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n },\r\n\r\n _changeHandler (event) {\r\n if (this.$node.dirty) this._validate();\r\n\r\n dmx.nextTick(() => {\r\n if (!this.$node) return;\r\n this.set('checked', this.$node.checked);\r\n this.dispatchEvent('changed');\r\n if (event.type != 'reset') {\r\n this.dispatchEvent(this.$node.checked ? 'checked' : 'unchecked');\r\n }\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('checkbox-group', {\r\n\r\n initialData: {\r\n value: [],\r\n },\r\n\r\n attributes: {\r\n value: {\r\n type: Array,\r\n default: [],\r\n alwaysUpdate: true,\r\n },\r\n },\r\n\r\n methods: {\r\n setValue (value) {\r\n this._setValue(value);\r\n },\r\n },\r\n\r\n events: {\r\n updated: Event,\r\n },\r\n\r\n init (node) {\r\n this._changeHandler = this._changeHandler.bind(this);\r\n\r\n node.addEventListener('change', this._changeHandler);\r\n node.addEventListener('checkbox', this._changeHandler);\r\n },\r\n\r\n render (node) {\r\n this.$parse();\r\n\r\n this._setValue(this.props.value, true);\r\n\r\n this._mutationObserver = new MutationObserver((mutationList) => {\r\n let value = this.props.value;\r\n if (value == null) value = [];\r\n if (!Array.isArray(value)) value = [value];\r\n value = value.map(v => v.toString());\r\n\r\n for (let mutation of mutationList) {\r\n for (let node of mutation.addedNodes) {\r\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\r\n\r\n requestAnimationFrame(() => {\r\n if (node.tagName === 'INPUT' && node.type === 'checkbox') {\r\n node.checked = value.includes(node.value);\r\n node.defaultChecked = node.checked;\r\n } else {\r\n node.querySelectorAll('input[type=checkbox]').forEach(checkbox => {\r\n checkbox.checked = value.includes(checkbox.value);\r\n checkbox.defaultChecked = checkbox.checked;\r\n });\r\n }\r\n\r\n requestAnimationFrame(() => {\r\n this._updateValue();\r\n });\r\n });\r\n }\r\n }\r\n });\r\n\r\n this._mutationObserver.observe(node, { subtree: true, childList: true });\r\n },\r\n\r\n destroy () {\r\n this._mutationObserver.disconnect();\r\n this.$node.removeEventListener('change', this._changeHandler);\r\n this.$node.removeEventListener('checkbox', this._changeHandler);\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has('value')) {\r\n this._setValue(this.props.value, true);\r\n }\r\n },\r\n\r\n _setValue (value, defaultValue) {\r\n if (value == null) value = [];\r\n if (!Array.isArray(value)) value = [value];\r\n value = value.map(v => v.toString());\r\n\r\n this._checkboxes().forEach(checkbox => {\r\n checkbox.checked = value.includes(checkbox.value);\r\n if (defaultValue) checkbox.defaultChecked = checkbox.checked;\r\n });\r\n\r\n this._updateValue();\r\n },\r\n\r\n _updateValue () {\r\n const value = this._checkboxes().filter(checkbox => !checkbox.disabled && checkbox.checked).map(checkbox => checkbox.value);\r\n\r\n if (!dmx.equal(this.data.value, value)) {\r\n this.set('value', value);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n },\r\n\r\n _checkboxes () {\r\n return Array.from(this.$node.querySelectorAll('input[type=checkbox]'));\r\n },\r\n\r\n _changeHandler (event) {\r\n this._updateValue();\r\n },\r\n\r\n});\r\n","dmx.Component('select', {\r\n\r\n extends: 'form-element',\r\n\r\n initialData: {\r\n selectedIndex: -1,\r\n selectedValue: '',\r\n selectedText: '',\r\n },\r\n\r\n attributes: {\r\n options: {\r\n type: [Array, Object, Number],\r\n default: null,\r\n },\r\n\r\n optiontext: {\r\n type: String,\r\n default: '$value',\r\n },\r\n\r\n optionvalue: {\r\n type: String,\r\n default: '$value',\r\n },\r\n },\r\n\r\n methods: {\r\n setSelectedIndex (index) {\r\n this.$node.selectedIndex = index;\r\n this._updateValue();\r\n },\r\n },\r\n\r\n init (node) {\r\n this._options = [];\r\n\r\n if (!this.props.value) {\r\n this.props.value = this.$node.value;\r\n this._updateValue();\r\n }\r\n\r\n this._mutationObserver = new MutationObserver((a) => {\r\n if (!this._updatingOptions) {\r\n this._updateValue();\r\n }\r\n });\r\n this._mutationObserver.observe(this.$node, { subtree: true, childList: true });\r\n\r\n dmx.Component('form-element').prototype.init.call(this, node);\r\n },\r\n\r\n render (node) {\r\n this.$parse();\r\n this._renderOptions();\r\n let value = this.props.value;\r\n if (value == null) value = '';\r\n Array.from(this.$node.options).forEach(option => {\r\n option.toggleAttribute('selected', (option.value == value));\r\n option.selected = (option.value == value);\r\n option.defaultSelected = option.selected;\r\n });\r\n this._updateValue();\r\n },\r\n\r\n destroy () {\r\n this._mutationObserver.disconnect();\r\n dmx.Component('form-element').prototype.destroy.call(this);\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n dmx.Component('form-element').prototype.performUpdate.call(this, updatedProps);\r\n\r\n if (updatedProps.has('options') || updatedProps.has('optiontext') || updatedProps.has('optionvalue')) {\r\n this._renderOptions();\r\n }\r\n },\r\n\r\n _setValue (value, defaultValue) {\r\n if (value == null) value = '';\r\n value = value.toString();\r\n\r\n \r\n if (defaultValue) {\r\n Array.from(this.$node.options).forEach(option => {\r\n option.toggleAttribute('selected', option.value == value);\r\n });\r\n } else {\r\n const selectedIndex = Array.from(this.$node.options).findIndex(option => option.value == value);\r\n this.$node.selectedIndex = selectedIndex;\r\n }\r\n\r\n this._updateValue();\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n },\r\n\r\n _updateValue () {\r\n const selectedIndex = this.$node.selectedIndex;\r\n const selected = this.$node.options[selectedIndex] || { value: '', text: '' };\r\n\r\n this.set({\r\n selectedIndex: selectedIndex,\r\n selectedValue: selected.value,\r\n selectedText: selected.text,\r\n value: selected.value,\r\n });\r\n },\r\n\r\n _renderOptions () {\r\n this._options.forEach(option => option.remove());\r\n this._options = [];\r\n\r\n if (this.props.options) {\r\n this._updatingOptions = true;\r\n dmx.repeatItems(this.props.options).forEach(option => {\r\n const node = document.createElement('option');\r\n node.value = dmx.parse(this.props.optionvalue, dmx.DataScope(option, this));\r\n node.textContent = dmx.parse(this.props.optiontext, dmx.DataScope(option, this));\r\n if (node.value == this.props.value) node.selected = true;\r\n this.$node.append(node);\r\n this._options.push(node);\r\n });\r\n this._updatingOptions = false;\r\n }\r\n\r\n this._updateValue();\r\n },\r\n\r\n _inputHandler (event) {\r\n // do nothing\r\n },\r\n\r\n _changeHandler (event) {\r\n if (this.$node.dirty) {\r\n this._validate();\r\n }\r\n\r\n dmx.nextTick(() => {\r\n if (this.data.selectedIndex !== this.$node.selectedIndex) {\r\n this._updateValue();\r\n this.dispatchEvent('changed');\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('select-multiple', {\r\n\r\n extends: 'select',\r\n\r\n initialData: {\r\n value: [],\r\n },\r\n\r\n attributes: {\r\n value: {\r\n type: Array,\r\n default: null,\r\n alwaysUpdate: true,\r\n },\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n dmx.Component('select').prototype.performUpdate.call(this, updatedProps);\r\n\r\n if (updatedProps.has('value')) {\r\n this._setValue(this.props.value, true);\r\n }\r\n },\r\n\r\n _setValue (value, defaultValue) {\r\n if (value == null) value = '';\r\n if (!Array.isArray(value)) value = [value];\r\n value = value.map(v => v.toString());\r\n\r\n Array.from(this.$node.options).forEach(option => {\r\n const selected = value.includes(option.value);\r\n if (defaultValue) {\r\n option.toggleAttribute('selected', selected);\r\n } else {\r\n option.selected = selected;\r\n }\r\n });\r\n\r\n this._updateValue();\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n },\r\n\r\n _getValue () {\r\n return Array.from(this.$node.selectedOptions).map(option => option.value);\r\n },\r\n\r\n _updateValue () {\r\n const value = this._getValue(); //Array.from(this.$node.options).filter(option => option.selected).map(option => option.value);\r\n\r\n dmx.batch(() => {\r\n dmx.Component('select').prototype._updateValue.call(this);\r\n this.set('value', value);\r\n });\r\n },\r\n\r\n _changeHandler (event) {\r\n if (this.$node.dirty) this._validate();\r\n\r\n dmx.nextTick(() => {\r\n if (this.data.selectedIndex !== this.$node.selectedIndex || !dmx.equal(this.data.value, this._getValue())) {\r\n this._updateValue();\r\n this.dispatchEvent('changed');\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('value', {\r\n\r\n initialData: {\r\n value: null,\r\n },\r\n\r\n attributes: {\r\n value: {\r\n default: null,\r\n },\r\n },\r\n\r\n methods: {\r\n setValue (value) {\r\n if (this.data.value !== value) {\r\n this.set('value', value);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n },\r\n },\r\n\r\n events: {\r\n updated: Event,\r\n },\r\n\r\n render: false,\r\n\r\n init (node) {\r\n this.set('value', this.props.value);\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has('value')) {\r\n this.set('value', this.props.value);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n },\r\n\r\n});","dmx.Component(\"repeat\", {\r\n\r\n initialData: {\r\n items: [],\r\n },\r\n\r\n attributes: {\r\n repeat: {\r\n type: [Array, Object, Number],\r\n default: null,\r\n },\r\n\r\n key: {\r\n type: String,\r\n default: \"\",\r\n },\r\n\r\n rerender: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n\r\n events: {\r\n update: Event,\r\n updated: Event,\r\n },\r\n\r\n render: false,\r\n\r\n init (node) {\r\n this.prevItems = [];\r\n this.childKeys = new Map();\r\n this.$template = document.createDocumentFragment();\r\n while (this.$node.hasChildNodes()) {\r\n this.$template.appendChild(this.$node.firstChild);\r\n }\r\n if (this.props.repeat) {\r\n this.performUpdate(new Map([['repeat', undefined]]));\r\n }\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has(\"key\")) this._rerender = true;\r\n if (!updatedProps.has(\"repeat\")) return;\r\n\r\n this.dispatchEvent(\"update\");\r\n\r\n if (this.props.rerender || this._rerender) {\r\n this._rerender = false;\r\n this._clear();\r\n }\r\n\r\n var RepeatItem = dmx.Component(\"repeat-item\");\r\n var repeat = dmx.clone(this.props.repeat);\r\n var items = dmx.repeatItems(repeat);\r\n\r\n if (items.length) {\r\n if (\r\n !this.props.rerender &&\r\n this.props.key &&\r\n items[0].hasOwnProperty(this.props.key) &&\r\n this.prevItems.length\r\n ) {\r\n // keyed repeater (https://github.com/localvoid/kivi/blob/master/lib/vnode.ts#L1320-L1513)\r\n var key = this.props.key;\r\n var a = this.prevItems;\r\n var b = this._clone(items);\r\n var aStart = 0;\r\n var bStart = 0;\r\n var aEnd = a.length - 1;\r\n var bEnd = b.length - 1;\r\n var i, j, nextPos;\r\n\r\n outer: while (true) {\r\n // remove same keys from start\r\n while (a[aStart][key] === b[bStart][key]) {\r\n this.childKeys.get(b[bStart][key]).set(b[bStart]);\r\n aStart++;\r\n bStart++;\r\n if (aStart > aEnd || bStart > bEnd) {\r\n break outer;\r\n }\r\n }\r\n\r\n // remove same keys at end\r\n while (a[aEnd][key] === b[bEnd][key]) {\r\n this.childKeys.get(b[bEnd][key]).set(b[bEnd]);\r\n aEnd--;\r\n bEnd--;\r\n if (aStart > aEnd || bStart > bEnd) {\r\n break outer;\r\n }\r\n }\r\n\r\n // move from right to left\r\n if (a[aEnd][key] === b[bStart][key]) {\r\n this.childKeys.get(b[bStart][key]).set(b[bStart]);\r\n this._moveChild(b[bStart][key], a[aStart][key]);\r\n aEnd--;\r\n bStart++;\r\n if (aStart > aEnd || bStart > bEnd) {\r\n break;\r\n }\r\n continue;\r\n }\r\n\r\n // move from left to right\r\n if (a[aStart][key] === b[bEnd][key]) {\r\n nextPos = bEnd + 1;\r\n this.childKeys.get(b[bEnd][key]).set(b[bEnd]);\r\n this._moveChild(b[bEnd][key], b[nextPos] && b[nextPos][key]);\r\n aStart++;\r\n bEnd--;\r\n if (aStart > aEnd || bStart > bEnd) {\r\n break;\r\n }\r\n continue;\r\n }\r\n\r\n break;\r\n }\r\n\r\n if (aStart > aEnd) {\r\n // insert rest from b\r\n nextPos = bEnd + 1;\r\n while (bStart <= bEnd) {\r\n this._insertChild(b[bStart++], b[nextPos] && b[nextPos][key]);\r\n }\r\n } else if (bStart > bEnd) {\r\n // remove rest from a\r\n while (aStart <= aEnd) {\r\n this._removeChild(a[aStart++][key]);\r\n }\r\n } else {\r\n var aLength = aEnd - aStart + 1;\r\n var bLength = bEnd - bStart + 1;\r\n var aNullable = a;\r\n var sources = new Array(bLength).fill(-1);\r\n\r\n var moved = false;\r\n var pos = 0;\r\n var synced = 0;\r\n\r\n if (bLength <= 4 || aLength * bLength <= 16) {\r\n for (i = aStart; i <= aEnd; i++) {\r\n if (synced < bLength) {\r\n for (j = bStart; j <= bEnd; j++) {\r\n if (a[i][key] === b[j][key]) {\r\n sources[j - bStart] = i;\r\n\r\n if (pos > j) {\r\n moved = true;\r\n } else {\r\n pos = j;\r\n }\r\n\r\n this.childKeys.get(b[j][key]).set(b[j]);\r\n\r\n synced++;\r\n aNullable[i] = null;\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n } else {\r\n var keyIndex = {};\r\n\r\n for (i = bStart; i <= bEnd; i++) {\r\n keyIndex[b[i][key]] = i;\r\n }\r\n\r\n for (i = aStart; i <= aEnd; i++) {\r\n if (synced < bLength) {\r\n j = keyIndex[a[i][key]];\r\n\r\n if (j !== undefined) {\r\n sources[j - bStart] = i;\r\n\r\n if (pos > j) {\r\n moved = true;\r\n } else {\r\n pos = j;\r\n }\r\n\r\n this.childKeys.get(b[j][key]).set(b[j]);\r\n\r\n synced++;\r\n aNullable[i] = null;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (aLength === a.length && synced === 0) {\r\n this._clear();\r\n while (bStart < bLength) {\r\n this._insertChild(b[bStart++], null);\r\n }\r\n } else {\r\n i = aLength - synced;\r\n while (i > 0) {\r\n if (aNullable[aStart] !== null) {\r\n this._removeChild(a[aStart][key]);\r\n i--;\r\n }\r\n aStart++;\r\n }\r\n\r\n if (moved) {\r\n var seq = this._lis(sources);\r\n j = seq.length - 1;\r\n for (i = bLength - 1; i >= 0; i--) {\r\n if (sources[i] === -1) {\r\n pos = i + bStart;\r\n nextPos = pos + 1;\r\n this._insertChild(b[pos], b[nextPos] && b[nextPos][key]);\r\n } else {\r\n if (j < 0 || i !== seq[j]) {\r\n pos = i + bStart;\r\n nextPos = pos + 1;\r\n this._moveChild(b[pos][key], b[nextPos] && b[nextPos][key]);\r\n } else {\r\n j--;\r\n }\r\n }\r\n }\r\n } else if (synced !== bLength) {\r\n for (i = bLength - 1; i >= 0; i--) {\r\n if (sources[i] === -1) {\r\n pos = i + bStart;\r\n nextPos = pos + 1;\r\n this._insertChild(b[pos], b[nextPos] && b[nextPos][key]);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n } else {\r\n if (this.children.length > items.length) {\r\n // remove some children\r\n this.children.splice(items.length).forEach((child) => {\r\n child.$destroy();\r\n });\r\n }\r\n\r\n if (this.children.length) {\r\n // update existing children\r\n this.children.forEach((child, i) => {\r\n child.set(items[i]);\r\n });\r\n }\r\n\r\n if (items.length > this.children.length) {\r\n // add new children\r\n const fragment = document.createDocumentFragment();\r\n const toParse = new Set();\r\n\r\n for (var i = this.children.length; i < items.length; i++) {\r\n var child = new RepeatItem(\r\n this.$template.cloneNode(true),\r\n this,\r\n items[i]\r\n );\r\n child.$nodes.forEach((node) => {\r\n fragment.appendChild(node);\r\n //this.$node.appendChild(node);\r\n //child.$parse(node);\r\n toParse.add(child);\r\n });\r\n this.children.push(child);\r\n }\r\n\r\n this.$node.appendChild(fragment);\r\n\r\n for (const child of toParse) {\r\n child.$nodes.forEach((node) => {\r\n child.$parse(node);\r\n });\r\n }\r\n }\r\n }\r\n } else {\r\n this._clear();\r\n }\r\n\r\n if (this.props.key) {\r\n this.prevItems = this._clone(items);\r\n for (let child of this.children) {\r\n this.childKeys.set(child.data[this.props.key], child);\r\n }\r\n }\r\n\r\n //this.set('items', items);\r\n this.set(\r\n \"items\",\r\n this.children.map((child) => child.data)\r\n );\r\n\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n },\r\n\r\n _lis (a) {\r\n var p = a.slice(0);\r\n var result = [];\r\n result.push(0);\r\n var u, v;\r\n\r\n for (var i = 0, il = a.length; i < il; i++) {\r\n if (a[i] === -1) {\r\n continue;\r\n }\r\n\r\n var j = result[result.length - 1];\r\n if (a[j] < a[i]) {\r\n p[i] = j;\r\n result.push(i);\r\n continue;\r\n }\r\n\r\n u = 0;\r\n v = result.length - 1;\r\n\r\n while (u < v) {\r\n var c = ((u + v) / 2) | 0;\r\n if (a[result[c]] < a[i]) {\r\n u = c + 1;\r\n } else {\r\n v = c;\r\n }\r\n }\r\n\r\n if (a[i] < a[result[u]]) {\r\n if (u > 0) {\r\n p[i] = result[u - 1];\r\n }\r\n result[u] = i;\r\n }\r\n }\r\n\r\n u = result.length;\r\n v = result[u - 1];\r\n\r\n while (u-- > 0) {\r\n result[u] = v;\r\n v = p[v];\r\n }\r\n\r\n return result;\r\n },\r\n\r\n _clear () {\r\n this.prevItems = [];\r\n this.childKeys.clear();\r\n this.$node.innerHTML = '';\r\n this.children.splice(0).forEach((child) => {\r\n child.$destroy();\r\n });\r\n },\r\n\r\n _insertChild (data, before) {\r\n var RepeatItem = dmx.Component(\"repeat-item\");\r\n var child = new RepeatItem(this.$template.cloneNode(true), this, data);\r\n\r\n child.$nodes.forEach((node) => {\r\n if (!before) {\r\n this.$node.appendChild(node);\r\n } else {\r\n if (this.childKeys.has(before)) {\r\n this.$node.insertBefore(node, this.childKeys.get(before).$nodes[0]);\r\n } else {\r\n console.warn(\r\n \"(insert) can not insert node before key \" + before + \"!\"\r\n );\r\n }\r\n }\r\n\r\n child.$parse(node);\r\n });\r\n\r\n this.childKeys.set(data[this.props.key], child);\r\n this.children.push(child);\r\n },\r\n\r\n _moveChild (key, before) {\r\n var child = this.childKeys.get(key);\r\n\r\n if (child) {\r\n if (this.childKeys.has(before)) {\r\n child.$nodes.forEach((node) => {\r\n this.$node.insertBefore(node, this.childKeys.get(before).$nodes[0]);\r\n });\r\n } else {\r\n child.$nodes.forEach((node) => {\r\n this.$node.appendChild(node);\r\n });\r\n }\r\n } else {\r\n console.warn(\"(move) child with key \" + key + \" not found!\");\r\n }\r\n },\r\n\r\n _removeChild (key) {\r\n var child = this.childKeys.get(key);\r\n if (child) {\r\n child.$destroy();\r\n this.children.splice(this.children.indexOf(child), 1);\r\n this.childKeys.delete(key);\r\n } else {\r\n console.warn(\"(remove) child with key \" + key + \" not found!\");\r\n }\r\n },\r\n\r\n _clone (o) {\r\n return dmx.clone(o);\r\n },\r\n});\r\n","dmx.Component(\"repeat-item\", {\r\n\r\n constructor: function (fragment, parent, data, name) {\r\n this.parent = parent;\r\n this.children = [];\r\n this.listeners = {};\r\n this.props = {};\r\n\r\n this.__disposables = [];\r\n this.__childDisposables = [];\r\n\r\n this.updatedProps = new Map();\r\n this.updateRequested = false;\r\n\r\n this.isInitialized = true;\r\n this.isDestroyed = false;\r\n\r\n this.data = dmx.signalProxy(data);\r\n this.seed = parent.seed;\r\n\r\n this.name = name || \"repeatItem\";\r\n this.$nodes = [];\r\n for (var i = 0; i < fragment.childNodes.length; i++) {\r\n this.$nodes.push(fragment.childNodes[i]);\r\n }\r\n },\r\n\r\n destroy () {\r\n for (const node of this.$nodes) {\r\n if (this.parent && this.parent.props && this.parent.props.key) {\r\n const event = new Event('remove', { cancelable: true });\r\n \r\n if (node.dispatchEvent(event)) {\r\n dmx.dom.remove(node);\r\n }\r\n } else {\r\n node.remove();\r\n }\r\n }\r\n },\r\n});\r\n","dmx.Component('fetch', {\r\n\r\n initialData: {\r\n status: 0,\r\n data: null,\r\n links: {},\r\n paging: {},\r\n headers: {},\r\n state: {\r\n executing: false,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n },\r\n uploadProgress: {\r\n position: 0,\r\n percent: 0,\r\n total: 0,\r\n },\r\n downloadProgress: {\r\n position: 0,\r\n percent: 0,\r\n total: 0,\r\n },\r\n lastError: {\r\n status: 0,\r\n message: '',\r\n response: null,\r\n },\r\n },\r\n\r\n attributes: {\r\n timeout: {\r\n type: Number,\r\n default: 0,\r\n },\r\n\r\n method: {\r\n type: String,\r\n default: 'GET',\r\n },\r\n\r\n url: {\r\n type: String,\r\n default: '',\r\n },\r\n\r\n params: {\r\n type: Object,\r\n default: {},\r\n },\r\n\r\n headers: {\r\n type: Object,\r\n default: {},\r\n },\r\n\r\n data: {\r\n type: Object,\r\n default: {},\r\n },\r\n\r\n dataType: {\r\n type: String,\r\n default: 'auto',\r\n enum: ['auto', 'json', 'text'],\r\n },\r\n\r\n noload: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n cache: {\r\n type: String,\r\n default: '',\r\n },\r\n\r\n ttl: {\r\n // cache ttl in seconds (default 1 day)\r\n type: Number,\r\n default: 86400,\r\n },\r\n\r\n credentials: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n\r\n methods: {\r\n abort () {\r\n this._abort();\r\n },\r\n\r\n load (params, reload) {\r\n const options = {};\r\n if (params) options.params = params;\r\n if (reload) options.ttl = 0;\r\n this._fetch(options);\r\n },\r\n\r\n reset () {\r\n this._abort();\r\n this._resetData(true);\r\n },\r\n },\r\n\r\n events: {\r\n start: Event,\r\n done: Event,\r\n error: Event,\r\n invalid: Event,\r\n unauthorized: Event,\r\n forbidden: Event,\r\n abort: Event,\r\n success: Event,\r\n upload: ProgressEvent,\r\n download: ProgressEvent,\r\n },\r\n\r\n _statusEvents: {\r\n 200: 'success',\r\n 400: 'invalid',\r\n 401: 'unauthorized',\r\n 403: 'forbidden',\r\n },\r\n\r\n render: false,\r\n\r\n init (node) {\r\n this._fetch = dmx.debounce(this._fetch.bind(this));\r\n\r\n this._loadHandler = this._loadHandler.bind(this);\r\n this._abortHandler = this._abortHandler.bind(this);\r\n this._errorHandler = this._errorHandler.bind(this);\r\n this._timeoutHandler = this._timeoutHandler.bind(this);\r\n this._downloadProgressHandler = this._progressHandler.bind(this, 'download');\r\n this._uploadProgressHandler = this._progressHandler.bind(this, 'upload');\r\n\r\n this._xhr = new XMLHttpRequest();\r\n this._xhr.addEventListener('load', this._loadHandler);\r\n this._xhr.addEventListener('abort', this._abortHandler);\r\n this._xhr.addEventListener('error', this._errorHandler);\r\n this._xhr.addEventListener('timeout', this._timeoutHandler);\r\n this._xhr.addEventListener('progress', this._downloadProgressHandler);\r\n this._xhr.upload.addEventListener('progress', this._uploadProgressHandler);\r\n\r\n if (!this.props.noload && this.props.url) {\r\n this._fetch();\r\n }\r\n },\r\n\r\n destroy () {\r\n this._xhr.removeEventListener('load', this._loadHandler);\r\n this._xhr.removeEventListener('abort', this._abortHandler);\r\n this._xhr.removeEventListener('error', this._errorHandler);\r\n this._xhr.removeEventListener('timeout', this._timeoutHandler);\r\n this._xhr.removeEventListener('progress', this._downloadProgressHandler);\r\n this._xhr.upload.removeEventListener('progress', this._uploadProgressHandler);\r\n this._xhr.abort();\r\n this._xhr = null;\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (!this.props.noload && this.props.url) {\r\n // if url or params are changed\r\n if (updatedProps.has('url') || updatedProps.has('params')) {\r\n this._fetch();\r\n }\r\n }\r\n },\r\n\r\n // TODO: deprecate this, use JSON or expression instead\r\n $parseAttributes (node) {\r\n dmx.BaseComponent.prototype.$parseAttributes.call(this, node);\r\n\r\n dmx.dom.getAttributes(node).forEach(({ name, argument, value }) => {\r\n if (argument && value && ['param', 'header'].includes(name)) {\r\n this.$watch(value, value => {\r\n this.props[name + 's'] = Object.assign({}, this.props[name + 's'], { [argument]: value });\r\n });\r\n }\r\n\r\n if (argument && value && name == 'data') {\r\n this.$watch(value, value => {\r\n this.props.data = Object.assign({}, this.props.data, { [argument]: value });\r\n });\r\n }\r\n });\r\n },\r\n\r\n _abort () {\r\n if (this._xhr) {\r\n this._xhr.abort();\r\n }\r\n },\r\n\r\n _resetData (clearData) {\r\n const data = {\r\n status: 0,\r\n headers: {},\r\n state: {\r\n executing: false,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n },\r\n uploadProgress: {\r\n position: 0,\r\n total: 0,\r\n percent: 0,\r\n },\r\n downloadProgress: {\r\n position: 0,\r\n total: 0,\r\n percent: 0,\r\n },\r\n lastError: {\r\n status: 0,\r\n message: \"\",\r\n response: null,\r\n },\r\n };\r\n\r\n if (clearData) {\r\n data.data = null;\r\n }\r\n\r\n this.set(data);\r\n },\r\n\r\n _fetch (options) {\r\n this._abort();\r\n\r\n options = dmx.extend(true, this.props, options || {});\r\n \r\n let qs = Object.keys(options.params)\r\n .filter(key => options.params[key] != null)\r\n .map(key => {\r\n let value = options.params[key];\r\n if (typeof value === 'string' && value.startsWith('{{')) {\r\n value = this.parse(value);\r\n }\r\n return encodeURIComponent(key) + '=' + encodeURIComponent(value);\r\n })\r\n .join('&');\r\n\r\n this._resetData();\r\n this.dispatchEvent('start');\r\n \r\n this._url = options.url;\r\n\r\n if (qs) {\r\n this._url += (this._url.includes('?') ? '&' : '?') + qs;\r\n }\r\n\r\n if (window.WebviewProxy) {\r\n // Cordova webview proxy plugin\r\n this._url = window.WebviewProxy.convertProxyUrl(this._url);\r\n }\r\n\r\n if (this.props.cache) {\r\n const cache = this.parse(`${this.props.cache}.data[\"${this._url}\"]`);\r\n if (cache) {\r\n if (Date.now() - cache.created >= options.ttl * 1000) {\r\n this.parse(`${this.props.cache}.remove(\"${this._url}\")`);\r\n } else {\r\n this.set({\r\n headers: cache.headers || {},\r\n paging: cache.paging || {},\r\n links: cache.links || {},\r\n data: cache.data,\r\n });\r\n this.dispatchEvent('success');\r\n this.dispatchEvent('done');\r\n return;\r\n }\r\n }\r\n }\r\n\r\n this.set('state', {\r\n executing: true,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n });\r\n\r\n let data = null;\r\n let method = this.props.method.toUpperCase();\r\n\r\n if (method !== 'GET') {\r\n if (this.props.dataType === 'text') {\r\n data = this.props.data.toString();\r\n } else if (this.props.dataType === 'json') {\r\n data = JSON.stringify(this.props.data);\r\n } else {\r\n if (method === 'POST') {\r\n data = new FormData();\r\n\r\n Object.keys(this.props.data).forEach(key => {\r\n let value = this.props.data[key];\r\n\r\n if (Array.isArray(value)) {\r\n if (!/\\[\\]$/.text(value)) key += '[]';\r\n value.forEach(val => data.append(key, val));\r\n } else {\r\n data.set(key, value);\r\n }\r\n });\r\n } else {\r\n data = this.props.data.toString();\r\n }\r\n }\r\n }\r\n\r\n this._xhr.open(method, this._url);\r\n this._xhr.timeout = options.timeout * 1000;\r\n if (this.props.dataType === 'json' || this.props.dataType === 'text') {\r\n this._xhr.setRequestHeader('Content-Type', 'application/' + this.props.dataType);\r\n }\r\n for (const header in this.props.headers) {\r\n this._xhr.setRequestHeader(header, this.props.headers[header]);\r\n }\r\n this._xhr.setRequestHeader('accept', 'application/json');\r\n if (this.props.credentials) {\r\n this._xhr.withCredentials = true;\r\n }\r\n\r\n if (this.serverconnect && method !== 'GET') {\r\n const csrf_token = document.querySelector('meta[name=\"csrf-token\"]');\r\n if (csrf_token) {\r\n this._xhr.setRequestHeader('X-CSRF-Token', csrf_token.content);\r\n }\r\n }\r\n\r\n try {\r\n this._xhr.send(data);\r\n } catch (err) {\r\n this._done(err);\r\n }\r\n },\r\n\r\n _done (err) {\r\n this._resetData();\r\n\r\n if (err) {\r\n this.set('lastError', {\r\n status: 0,\r\n message: err.message,\r\n response: null,\r\n });\r\n\r\n this.dispatchEvent('error');\r\n this.dispatchEvent('done');\r\n return;\r\n }\r\n\r\n let response = this._xhr.responseText;\r\n\r\n try {\r\n response = JSON.parse(response);\r\n } catch (err) {\r\n if (this._xhr.status < 400) {\r\n this.set('lastError', {\r\n status: 0,\r\n message: 'Response was not valid JSON',\r\n response: response,\r\n });\r\n\r\n this.dispatchEvent('error');\r\n this.dispatchEvent('done');\r\n return;\r\n }\r\n }\r\n\r\n this._parseHeaders();\r\n\r\n if (this._xhr.status < 400) {\r\n this.set({\r\n status: this._xhr.status,\r\n data: response,\r\n });\r\n\r\n this.dispatchEvent('success');\r\n this.dispatchEvent('done');\r\n\r\n if (this.props.cache) {\r\n this.parse(`${this.props.cache}.set(\"${this._url}\", { headers: headers, paging: paging, links: links, data: data, created: ${Date.now()} })`)\r\n }\r\n return;\r\n }\r\n\r\n this.set({\r\n status: this._xhr.status,\r\n lastError: {\r\n status: this._xhr.status,\r\n message: this._xhr.statusText,\r\n response: response,\r\n }\r\n });\r\n\r\n this.dispatchEvent(this._statusEvents[this._xhr.status] || 'error');\r\n this.dispatchEvent('done');\r\n },\r\n\r\n _parseHeaders() {\r\n try {\r\n const strHeaders = this._xhr.getAllResponseHeaders();\r\n const arrHeaders = strHeaders.trim().split(/[\\r\\n]+/);\r\n\r\n this.set('headers', arrHeaders.reduce((headers, line) => {\r\n const parts = line.split(': ');\r\n const name = parts.shift();\r\n const value = parts.join(': ');\r\n\r\n headers[name] = value;\r\n\r\n return headers;\r\n }, {}));\r\n } catch (err) {\r\n console.warn('Error parsing response headers', err);\r\n return;\r\n }\r\n\r\n this._parseLinkHeaders();\r\n },\r\n\r\n _parseLinkHeaders () {\r\n try {\r\n const linkHeader = Object.keys(this.data.headers).find(header => header.toLowerCase() === 'link');\r\n\r\n if (linkHeader) {\r\n this.set('links', this.data.headers[linkHeader].split(/,\\s* {\r\n try {\r\n const match = link.match(/]*)>(.*)/);\r\n const linkUrl = new URL(match[1]);\r\n const parts = match[2].split(';');\r\n const qs = linkUrl.search.slice(1).split('&').reduce((acc, x) => {\r\n const p = x.split('=');\r\n if (p[0]) acc[decodeURIComponent[p[0]]] = decodeURIComponent(p[1] || '');\r\n return acc;\r\n }, {});\r\n \r\n let info = parts.slice(1).reduce((acc, part) => {\r\n const match = part.match(/\\s*(.+)\\s*=\\s*\"?([^\"]+)\"?/);\r\n if (match) acc[match[1]] = match[2];\r\n return acc;\r\n }, {});\r\n\r\n info = Object.assign({}, qs, info);\r\n info.url = linkUrl.toString();\r\n\r\n return info;\r\n } catch (err) {\r\n console.warn('Error parsing link header part', err);\r\n return null;\r\n }\r\n })).filter(x => x && x.rel).reduce((acc, x) => {\r\n x.rel.split(/\\s+/).forEach(rel => acc[rel] = Object.assign(x, { rel }));\r\n return acc;\r\n }, {});\r\n }\r\n } catch (err) {\r\n console.warn('Error parsing link header', err);\r\n return;\r\n }\r\n\r\n this._parsePaging();\r\n },\r\n\r\n _parsePaging () {\r\n try {\r\n const paging = {\r\n page: 1,\r\n pages: 1,\r\n items: 0,\r\n has: {\r\n first: false,\r\n prev: false,\r\n next: false,\r\n last: false,\r\n },\r\n };\r\n\r\n const { first, prev, next, last } = this.data.links;\r\n\r\n if (prev || next) {\r\n if (last && last.page) {\r\n paging.pages = +last.page;\r\n } else if (prev && prev.page) {\r\n paging.pages = +prev.page + 1;\r\n }\r\n\r\n const countHeader = Object.keys(this.data.headers).find(header => {\r\n header = header.toLowerCase();\r\n return header === 'x-total' || header === 'x-count' || header === 'x-total-count';\r\n });\r\n\r\n if (countHeader) {\r\n paging.items = +this.data.headers[countHeader];\r\n }\r\n\r\n if (prev && prev.page) {\r\n paging.page = +prev.page + 1;\r\n } else if (next && next.page) {\r\n paging.page = +next.page - 1;\r\n }\r\n\r\n paging.has = {\r\n first: !!first,\r\n prev: !!prev,\r\n next: !!next,\r\n last: !!last,\r\n };\r\n }\r\n\r\n this.set('paging', paging);\r\n } catch (err) {\r\n console.warn('Error parsing paging', err);\r\n }\r\n },\r\n\r\n _loadHandler (event) {\r\n this._done();\r\n },\r\n\r\n _abortHandler (event) {\r\n this._resetData();\r\n this.dispatchEvent('abort');\r\n this.dispatchEvent('done');\r\n },\r\n\r\n _errorHandler (event) {\r\n this._done(Error('Failed to execute'));\r\n },\r\n\r\n _timeoutHandler (event) {\r\n this._done(Error('Execution timeout'));\r\n },\r\n\r\n _progressHandler (type, event) {\r\n event.loaded = event.loaded || event.position;\r\n\r\n const percent = event.lengthComputable ? Math.ceil(event.loaded * 100 / event.total) : 0;\r\n\r\n this.set({\r\n state: {\r\n executing: true,\r\n uploading: type === 'upload' && percent < 100,\r\n processing: type === 'upload' && percent === 100,\r\n downloading: type === 'download',\r\n },\r\n [type + 'Progress']: {\r\n position: event.loaded,\r\n total: event.total,\r\n percent: percent,\r\n },\r\n });\r\n\r\n this.dispatchEvent(type, {\r\n lengthComputable: event.lengthComputable,\r\n loaded: event.loaded,\r\n total: event.total,\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('serverconnect', {\r\n\r\n extends: 'fetch',\r\n\r\n attributes: {\r\n sockets: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n\r\n init (node) {\r\n this.serverconnect = true;\r\n \r\n if (this.props.sockets && dmx.Socket) {\r\n this._refresh = this._refresh.bind(this);\r\n this._event = this.props.url.replace(/^(.*?)api\\//, '')\r\n this._socket = dmx.Socket('/api');\r\n this._socket.on(this._event, this._refresh);\r\n }\r\n\r\n dmx.Component('fetch').prototype.init.call(this, node);\r\n },\r\n\r\n destroy () {\r\n if (this._socket) {\r\n this._socket.off(this._event, this._refresh);\r\n }\r\n\r\n dmx.Component('fetch').prototype.destroy.call(this);\r\n },\r\n\r\n _fetch (options) {\r\n if (this._socket && this._socket.connected) {\r\n this._refresh(options && options.params);\r\n } else {\r\n dmx.Component('fetch').prototype._fetch.call(this, options);\r\n }\r\n },\r\n\r\n _refresh (params) {\r\n params = dmx.extend(true, {}, this.props.params, params || {});\r\n\r\n this.dispatchEvent('start');\r\n this.set('state', {\r\n executing: true,\r\n uploading: false,\r\n processing: true,\r\n downloading: false,\r\n });\r\n\r\n this._socket.emit(this._event, params, response => {\r\n this.set({\r\n status: response.status,\r\n data: response.data,\r\n state: {\r\n executing: false,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n },\r\n });\r\n\r\n this.dispatchEvent(this._statusEvents[response.status] || 'error');\r\n this.dispatchEvent('done');\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('serverconnect-form', {\r\n \r\n extends: 'form',\r\n\r\n initialData: {\r\n status: 0,\r\n data: null,\r\n headers: {},\r\n state: {\r\n executing: false,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n },\r\n uploadProgress: {\r\n position: 0,\r\n total: 0,\r\n percent: 0,\r\n },\r\n downloadProgress: {\r\n position: 0,\r\n total: 0,\r\n percent: 0,\r\n },\r\n lastError: {\r\n status: 0,\r\n message: '',\r\n response: null,\r\n },\r\n },\r\n\r\n attributes: {\r\n timeout: {\r\n type: Number,\r\n default: 0,\r\n },\r\n\r\n autosubmit: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n params: {\r\n type: Object,\r\n default: {},\r\n },\r\n\r\n headers: {\r\n type: Object,\r\n default: {},\r\n },\r\n\r\n postData: {\r\n type: String,\r\n default: 'form',\r\n },\r\n\r\n credentials: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n\r\n methods: {\r\n abort () {\r\n this._abort();\r\n },\r\n\r\n reset (clearData) {\r\n this._reset();\r\n\r\n if (clearData) {\r\n this._abort();\r\n this._resetData(true);\r\n }\r\n }\r\n },\r\n\r\n events: {\r\n start: Event, // when starting an ajax call\r\n done: Event, // when ajax call completed (success and error)\r\n error: Event, // server error or javascript error (json parse or network transport) or timeout error\r\n unauthorized: Event, // 401 status from server\r\n forbidden: Event, // 403 status from server\r\n abort: Event, // ajax call was aborted\r\n success: Event, // successful ajax call,\r\n upload: ProgressEvent, // on upload progress\r\n download: ProgressEvent, // on download progress\r\n },\r\n\r\n init (node) {\r\n dmx.Component('form').prototype.init.call(this, node);\r\n\r\n this._loadHandler = this._loadHandler.bind(this);\r\n this._abortHandler = this._abortHandler.bind(this);\r\n this._errorHandler = this._errorHandler.bind(this);\r\n this._timeoutHandler = this._timeoutHandler.bind(this);\r\n this._downloadProgressHandler = this._progressHandler.bind(this, 'download');\r\n this._uploadProgressHandler = this._progressHandler.bind(this, 'upload');\r\n\r\n this._xhr = new XMLHttpRequest();\r\n this._xhr.addEventListener('load', this._loadHandler);\r\n this._xhr.addEventListener('abort', this._abortHandler);\r\n this._xhr.addEventListener('error', this._errorHandler);\r\n this._xhr.addEventListener('timeout', this._timeoutHandler);\r\n this._xhr.addEventListener('progress', this._downloadProgressHandler);\r\n this._xhr.upload.addEventListener('progress', this._uploadProgressHandler);\r\n\r\n this._extendNode(node);\r\n\r\n if (this.props.autosubmit) {\r\n dmx.nextTick(() => this._submit());\r\n }\r\n },\r\n\r\n destroy () {\r\n dmx.Component('form').prototype.destroy.call(this);\r\n\r\n this._xhr.removeEventListener('load', this._loadHandler);\r\n this._xhr.removeEventListener('abort', this._abortHandler);\r\n this._xhr.removeEventListener('error', this._errorHandler);\r\n this._xhr.removeEventListener('timeout', this._timeoutHandler);\r\n this._xhr.removeEventListener('progress', this._downloadProgressHandler);\r\n this._xhr.upload.removeEventListener('progress', this._uploadProgressHandler);\r\n this._xhr = null;\r\n },\r\n\r\n // TODO: deprecate this, use JSON or expression instead\r\n $parseAttributes (node) {\r\n dmx.BaseComponent.prototype.$parseAttributes.call(this, node);\r\n\r\n dmx.dom.getAttributes(node).forEach(({ name, argument, value }) => {\r\n if (argument && value && ['param', 'header'].includes(name)) {\r\n this.$watch(value, value => {\r\n this.props[name + 's'] = Object.assign({}, this.props[name + 's'], { [argument]: value });\r\n });\r\n }\r\n });\r\n },\r\n\r\n _extendNode (node) {\r\n node.dmxExtraData = {};\r\n node.dmxExtraElements = [];\r\n },\r\n\r\n _abort () {\r\n this._xhr.abort();\r\n },\r\n\r\n _resetData (clearData) {\r\n const data = {\r\n status: 0,\r\n headers: {},\r\n state: {\r\n executing: false,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n },\r\n uploadProgress: {\r\n position: 0,\r\n total: 0,\r\n percent: 0,\r\n },\r\n downloadProgress: {\r\n position: 0,\r\n total: 0,\r\n percent: 0,\r\n },\r\n lastError: {\r\n status: 0,\r\n message: \"\",\r\n response: null,\r\n },\r\n };\r\n\r\n if (clearData) {\r\n data.data = null;\r\n }\r\n\r\n this.set(data);\r\n },\r\n\r\n _formSubmit () {\r\n this._send();\r\n },\r\n\r\n _send () {\r\n // abort any previous request\r\n this._abort();\r\n \r\n const method = this.$node.method.toUpperCase();\r\n const action = this.$node.action;\r\n \r\n let data = null;\r\n \r\n let qs = Object.keys(this.props.params)\r\n .filter(key => this.props.params[key] != null)\r\n .map(key => {\r\n let value = this.props.params[key];\r\n if (typeof value === 'string' && value.startsWith('{{')) {\r\n value = this.parse(value);\r\n }\r\n return encodeURIComponent(key) + '=' + encodeURIComponent(value);\r\n })\r\n .join('&');\r\n\r\n if (method === 'GET') {\r\n if (qs.length) qs += '&';\r\n\r\n qs += Array.from(this.$node.elements)\r\n .filter(element => !element.disabled && ((element.type !== 'radio' && element.type !== 'checkbox') || element.checked))\r\n .map(element => encodeURIComponent(element.name) + '=' + encodeURIComponent(element.value))\r\n .join('&');\r\n } else if (this.props.postData === 'json') {\r\n data = this._parseJsonForm();\r\n\r\n if (this.$node.dmxExtraData) {\r\n Object.assign(data, this.$node.dmxExtraData);\r\n }\r\n\r\n data = JSON.stringify(data);\r\n } else {\r\n data = new FormData(this.$node);\r\n\r\n if (this.$node.dmxExtraData) {\r\n for (let key in this.$node.dmxExtraData) {\r\n let value = this.$node.dmxExtraData[key];\r\n\r\n if (Array.isArray(value)) {\r\n if (!/\\[\\]$/.test(key)) key += '[]';\r\n value.forEach(value => data.append(key, value));\r\n } else {\r\n data.set(key, value);\r\n }\r\n }\r\n }\r\n }\r\n\r\n this._resetData();\r\n this.dispatchEvent('start');\r\n\r\n this.set('state', {\r\n executing: true,\r\n uploading: false,\r\n processing: false,\r\n downloading: false,\r\n });\r\n\r\n let url = action;\r\n\r\n if (qs) {\r\n url += (url.includes('?') ? '&' : '?') + qs;\r\n }\r\n\r\n if (window.WebviewProxy) {\r\n // Cordova webview proxy plugin\r\n url = window.WebviewProxy.convertProxyUrl(url);\r\n }\r\n\r\n this._xhr.open(method, url);\r\n this._xhr.timeout = this.props.timeout * 1000;\r\n if (this.props.postData === 'json') {\r\n this._xhr.setRequestHeader('Content-Type', 'application/json');\r\n }\r\n for (const header in this.props.headers) {\r\n this._xhr.setRequestHeader(header, this.props.headers[header]);\r\n }\r\n this._xhr.setRequestHeader('accept', 'application/json');\r\n if (this.props.credentials) {\r\n this._xhr.withCredentials = true;\r\n }\r\n\r\n const csrf_token = document.querySelector('meta[name=\"csrf-token\"]');\r\n if (csrf_token) {\r\n this._xhr.setRequestHeader('X-CSRF-Token', csrf_token.content);\r\n }\r\n\r\n try {\r\n this._xhr.send(data);\r\n } catch (err) {\r\n this._done(err);\r\n }\r\n },\r\n\r\n _done (err) {\r\n this._resetData();\r\n\r\n if (err) {\r\n this.set('lastError', {\r\n status: 0,\r\n message: err.message,\r\n response: null,\r\n });\r\n\r\n this.dispatchEvent('error');\r\n this.dispatchEvent('done');\r\n return;\r\n }\r\n\r\n let response = this._xhr.responseText;\r\n\r\n try {\r\n response = JSON.parse(response);\r\n } catch (err) {\r\n if (this._xhr.status < 400) {\r\n this.set('lastError', {\r\n status: 0,\r\n message: 'Response was not valid JSON',\r\n response: response,\r\n });\r\n\r\n this.dispatchEvent('error');\r\n this.dispatchEvent('done');\r\n return;\r\n }\r\n }\r\n\r\n try {\r\n const strHeaders = this._xhr.getAllResponseHeaders();\r\n const arrHeaders = strHeaders.trim().split(/[\\r\\n]+/);\r\n\r\n this.set('headers', arrHeaders.reduce((headers, line) => {\r\n const parts = line.split(': ');\r\n const name = parts.shift();\r\n const value = parts.join(': ');\r\n\r\n headers[name] = value;\r\n\r\n return headers;\r\n }, {}));\r\n } catch (err) {\r\n console.warn('Error parsing response headers', err);\r\n }\r\n\r\n // reset form validation\r\n if (dmx.validateReset) dmx.validateReset(this.$node);\r\n if (window.grecaptcha && this.$node.querySelector('.g-recaptcha')) {\r\n grecaptcha.reset();\r\n }\r\n\r\n if (this._xhr.status < 400) {\r\n this.set({\r\n status: this._xhr.status,\r\n data: response,\r\n });\r\n\r\n this.dispatchEvent('success');\r\n this.dispatchEvent('done');\r\n return;\r\n }\r\n\r\n this.set({\r\n status: this._xhr.status,\r\n lastError: {\r\n status: this._xhr.status,\r\n message: this._xhr.statusText,\r\n response: response,\r\n }\r\n });\r\n\r\n if (this._xhr.status === 400) {\r\n this.dispatchEvent('invalid');\r\n\r\n // server-side validation error\r\n if (response.form && dmx.validate.setMessage) {\r\n for (const name in response.form) {\r\n const element = this.$node.querySelector(`[name=\"${name}\"]`);\r\n if (element) {\r\n const message = response.form[name];\r\n dmx.validate.setMessage(element, message);\r\n }\r\n }\r\n } else if (dmx.debug) {\r\n // console warning for debug purpose\r\n console.warn('400 error, no form errors in response.', response);\r\n }\r\n } else if (this._xhr.status === 401) {\r\n this.dispatchEvent('unauthorized');\r\n } else if (this._xhr.status === 403) {\r\n this.dispatchEvent('forbidden');\r\n } else {\r\n this.dispatchEvent('error');\r\n }\r\n\r\n this.dispatchEvent('done');\r\n },\r\n\r\n _loadHandler (event) {\r\n this._done();\r\n },\r\n\r\n _abortHandler (event) {\r\n this._resetData();\r\n this.dispatchEvent('abort');\r\n this.dispatchEvent('done');\r\n },\r\n\r\n _errorHandler (event) {\r\n this._done(Error('Failed to execute'));\r\n },\r\n\r\n _timeoutHandler (event) {\r\n this._done(Error('Execution timeout'));\r\n },\r\n\r\n _progressHandler (type, event) {\r\n event.loaded = event.loaded || event.position;\r\n\r\n const percent = event.lengthComputable ? Math.ceil(event.loaded * 100 / event.total) : 0;\r\n\r\n this.set({\r\n state: {\r\n executing: true,\r\n uploading: type === 'upload' && percent < 100,\r\n processing: type === 'upload' && percent === 100,\r\n downloading: type === 'download',\r\n },\r\n [type + 'Progress']: {\r\n position: event.loaded,\r\n total: event.total,\r\n percent: percent,\r\n },\r\n });\r\n\r\n this.dispatchEvent(type, {\r\n lengthComputable: event.lengthComputable,\r\n loaded: event.loaded,\r\n total: event.total,\r\n });\r\n },\r\n\r\n});\r\n","dmx.Component('if', {\r\n\r\n attributes: {\r\n condition: {\r\n type: Boolean,\r\n default: false\r\n },\r\n },\r\n\r\n events: {\r\n show: Event,\r\n hide: Event,\r\n },\r\n\r\n init (node) {\r\n this._shown = false;\r\n this._template = document.createDocumentFragment();\r\n\r\n while (node.firstChild) {\r\n this._template.appendChild(node.firstChild);\r\n }\r\n },\r\n\r\n render (node) {\r\n if (this.props.condition) {\r\n this._show();\r\n }\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n this.props.condition ? this._show() : this._hide();\r\n },\r\n\r\n destroy () {\r\n this._template = null;\r\n },\r\n\r\n _show () {\r\n if (this._shown) return;\r\n\r\n const template = this._template.cloneNode(true);\r\n this.$node.appendChild(template);\r\n this.$parse();\r\n this.dispatchEvent('show');\r\n this._shown = true;\r\n },\r\n\r\n _hide () {\r\n if (!this._shown) return;\r\n\r\n if (this.effects) {\r\n this.effects.forEach((effect) => effect());\r\n this.effects = null;\r\n }\r\n\r\n Array.from(this.$node.childNodes).forEach(node => {\r\n const event = new Event('remove', { cancelable: true });\r\n if (node.dispatchEvent(event)) node.remove();\r\n });\r\n \r\n this.$destroyChildren();\r\n this.dispatchEvent('hide');\r\n this._shown = false;\r\n }\r\n});\r\n","dmx.Component('datetime', {\r\n\r\n initialData: {\r\n datetime: null,\r\n },\r\n\r\n attributes: {\r\n interval: {\r\n type: String,\r\n default: 'seconds',\r\n enum: ['seconds', 'minutes', 'hours', 'days']\r\n },\r\n\r\n utc: {\r\n type: Boolean,\r\n default: false\r\n },\r\n },\r\n\r\n init () {\r\n this._tick = this._tick.bind(this);\r\n this._tick();\r\n },\r\n\r\n destroy () {\r\n if (this._timeout) clearTimeout(this._timeout);\r\n if (this._animationFrame) cancelAnimationFrame(this._animationFrame);\r\n },\r\n\r\n _tick () {\r\n this.set('datetime', this._datetime());\r\n\r\n switch (this.props.interval) {\r\n case 'seconds': return this._timeout = setTimeout(this._tick, 1000);\r\n case 'minutes': return this._timeout = setTimeout(this._tick, 60000);\r\n case 'hours': return this._timeout = setTimeout(this._tick, 3600000);\r\n case 'days': return this._timeout = setTimeout(this._tick, 86400000);\r\n default: return this._animationFrame = requestAnimationFrame(this._tick);\r\n }\r\n },\r\n\r\n _datetime () {\r\n const now = new Date();\r\n const pad = (n, d) => ('0000' + n).slice(-d);\r\n const utc = this.props.utc;\r\n \r\n const year = utc ? now.getUTCFullYear() : now.getFullYear();\r\n const month = (utc ? now.getUTCMonth() : now.getMonth()) + 1;\r\n const day = utc ? now.getUTCDate() : now.getDate();\r\n const hours = utc ? now.getUTCHours() : now.getHours();\r\n const minutes = utc ? now.getUTCMinutes() : now.getMinutes();\r\n const seconds = utc ? now.getUTCSeconds() : now.getSeconds();\r\n\r\n const dateString = pad(year, 4) + '-' + pad(month, 2) + '-' + pad(day, 2);\r\n const tz = utc ? 'Z' : '';\r\n\r\n switch (this.props.interval) {\r\n case 'days': return dateString + 'T00:00:00' + tz;\r\n case 'hours': return dateString + 'T' + pad(hours, 2) + ':00:00' + tz;\r\n case 'minutes': return dateString + 'T' + pad(hours, 2) + ':' + pad(minutes, 2) + ':00' + tz;\r\n }\r\n\r\n return dateString + 'T' + pad(hours, 2) + ':' + pad(minutes, 2) + ':' + pad(seconds, 2) + tz;\r\n },\r\n\r\n});\r\n","dmx.Component('api-action', { extends: 'fetch' });\r\n","dmx.Component('api-form', { extends: 'serverconnect-form' });\r\n","dmx.Component(\"array\", {\r\n\r\n initialData: {\r\n items: [],\r\n count: 0,\r\n },\r\n\r\n attributes: {\r\n items: {\r\n type: Array,\r\n default: [],\r\n },\r\n },\r\n\r\n events: {\r\n updated: Event,\r\n },\r\n\r\n methods: {\r\n add (newItem) {\r\n this._splice(this._count(), 0, newItem);\r\n },\r\n\r\n addUniq (newItem) {\r\n // Only add when not exists\r\n if (this._indexOf(newItem) == -1) {\r\n this._splice(this._count(), 0, newItem);\r\n }\r\n },\r\n\r\n insert (index, newItem) {\r\n this._splice(index, 0, newItem);\r\n },\r\n\r\n insertBefore (item, newItem) {\r\n const index = this._indexOf(item);\r\n if (index != -1) {\r\n this._splice(index, 0, newItem);\r\n }\r\n },\r\n\r\n insertAfter (item, newItem) {\r\n const index = this._indexOf(item);\r\n if (index != -1) {\r\n this._splice(index + 1, 0, newItem);\r\n }\r\n },\r\n\r\n replace (item, newItem) {\r\n const index = this._indexOf(item);\r\n if (index != -1) {\r\n this._splice(index, 1, newItem);\r\n }\r\n },\r\n\r\n replaceAt (index, newItem) {\r\n this._splice(index, 1, newItem);\r\n },\r\n\r\n remove (item) {\r\n const index = this._indexOf(item);\r\n if (index != -1) {\r\n this._splice(index, 1);\r\n }\r\n },\r\n\r\n removeAt (index) {\r\n this._splice(index, 1);\r\n },\r\n\r\n reverse () {\r\n this._reverse();\r\n },\r\n\r\n sort () {\r\n this._sort();\r\n },\r\n\r\n empty () {\r\n this._updateData([]);\r\n },\r\n },\r\n\r\n render: false,\r\n\r\n init () {\r\n const arr = dmx.array(this.props.items);\r\n\r\n this.set({\r\n items: arr,\r\n count: arr.length,\r\n });\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has(\"items\")) {\r\n this._updateData(dmx.array(this.props.items));\r\n }\r\n },\r\n\r\n _count () {\r\n return this.data.items.length;\r\n },\r\n\r\n _indexOf (item) {\r\n return this.data.items.indexOf(item);\r\n },\r\n\r\n _splice (index, remove, item) {\r\n const arr = dmx.clone(this.data.items);\r\n\r\n if (item !== undefined) {\r\n arr.splice(index, remove, item);\r\n } else {\r\n arr.splice(index, remove);\r\n }\r\n\r\n this._updateData(arr);\r\n },\r\n\r\n _reverse () {\r\n const arr = dmx.clone(this.data.items);\r\n arr.reverse();\r\n this._updateData(arr);\r\n },\r\n\r\n _sort () {\r\n const arr = dmx.clone(this.data.items);\r\n arr.sort();\r\n this._updateData(arr);\r\n },\r\n\r\n _updateData (arr) {\r\n if (!dmx.equal(this.data.items, arr)) {\r\n this.set({\r\n items: arr,\r\n count: arr.length,\r\n });\r\n\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n },\r\n\r\n});\r\n","dmx.Component('group', {});\r\n","dmx.Component('flow', {\r\n\r\n initialData: {\r\n data: null,\r\n running: false,\r\n lastError: null,\r\n },\r\n\r\n attributes: {\r\n src: {\r\n type: String,\r\n default: null,\r\n },\r\n\r\n preload: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n autorun: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n params: {\r\n type: Object,\r\n default: {},\r\n },\r\n },\r\n\r\n methods: {\r\n run (param, throwError) {\r\n return this._run(param, throwError);\r\n },\r\n\r\n runSub (param) {\r\n return this._runSub(param);\r\n },\r\n },\r\n\r\n events: {\r\n start: Event,\r\n done: Event,\r\n error: Event,\r\n },\r\n \r\n render: false,\r\n\r\n init (node) {\r\n if (this.props.src) {\r\n if (this.props.preload || this.props.autorun) {\r\n this._load(this.props.src, this.props.autorun).catch(console.error);\r\n }\r\n } else {\r\n try {\r\n this._flow = this._parse(node.textContent);\r\n if (this.props.autorun) this._run();\r\n } catch (err) {\r\n console.error(err);\r\n }\r\n }\r\n },\r\n\r\n destroy () {\r\n this._destroyed = true;\r\n },\r\n\r\n // TODO: deprecate this, use JSON or expression instead\r\n $parseAttributes (node) {\r\n dmx.BaseComponent.prototype.$parseAttributes.call(this, node);\r\n\r\n dmx.dom.getAttributes(node).forEach(({ name, argument, value }) => {\r\n if (argument && value && name == 'param') {\r\n this.$watch(value, value => {\r\n this.props.params = Object.assign({}, this.props.params, { [argument]: value });\r\n });\r\n }\r\n });\r\n },\r\n\r\n _load (url, run) {\r\n return fetch(url).then(response => {\r\n if (!response.ok || response.status >= 400) {\r\n throw Error(`Could not load flow ${this.name}, status ${response.status} ${response.statusText}`);\r\n }\r\n \r\n return response.text()\r\n }).then(text => {\r\n this._flow = this._parse(text);\r\n if (run) this._run();\r\n });\r\n },\r\n\r\n _parse (str) {\r\n return (window.Hjson ? Hjson : JSON).parse(str);\r\n },\r\n\r\n _runSub (param) {\r\n if (!this._flow) {\r\n if (this.props.src) {\r\n return this._load(this.props.src).then(() => {\r\n this._runFlow(param);\r\n });\r\n }\r\n\r\n throw Error('No flow');\r\n }\r\n\r\n return this._runFlow(param);\r\n },\r\n\r\n _run (param, throwError) {\r\n if (!this._flow) {\r\n if (this.props.src) {\r\n return this._load(this.props.src).then(() => {\r\n this._run(param, throwError);\r\n }).catch(console.error);\r\n }\r\n\r\n console.warn(`Flow ${this.name} is missing.`);\r\n return;\r\n }\r\n\r\n if (this.data.running) {\r\n console.info(`Can't run flow ${this.name} when a previous run didn't finish.`);\r\n return;\r\n }\r\n\r\n this.set({\r\n running: true,\r\n lastError: null,\r\n });\r\n\r\n this.dispatchEvent('start');\r\n\r\n if (dmx.debug) {\r\n console.debug(`Running flow ${this.name} with params`, param);\r\n console.time(`Flow ${this.name}`);\r\n }\r\n return this._runFlow(param).then(data => {\r\n if (dmx.debug) {\r\n console.debug(`Flow ${this.name} finished`, data);\r\n console.timeEnd(`Flow ${this.name}`);\r\n }\r\n this.set({\r\n running: false,\r\n data: data\r\n });\r\n\r\n this.dispatchEvent('done');\r\n\r\n return data;\r\n }).catch(err => {\r\n this.set({\r\n running: false,\r\n lastError: err && err.message,\r\n });\r\n\r\n this.dispatchEvent('error');\r\n\r\n if (throwError) {\r\n throw err;\r\n }\r\n })\r\n },\r\n\r\n _runFlow (param) {\r\n return dmx.Flow.run(this._flow, dmx.DataScope({\r\n $param: Object.assign({}, this.props.params, param),\r\n }, this));\r\n },\r\n\r\n});\r\n","dmx.Component('toggle', {\r\n\r\n initialData: {\r\n checked: false,\r\n },\r\n\r\n attributes: {\r\n checked: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n },\r\n\r\n methods: {\r\n check () {\r\n this.props.checked = true;\r\n },\r\n\r\n uncheck () {\r\n this.props.checked = false;\r\n },\r\n\r\n toggle () {\r\n this.props.checked = !this.data.checked;\r\n }\r\n },\r\n\r\n events: {\r\n updated: Event,\r\n },\r\n\r\n render: false,\r\n\r\n init (node) {\r\n this.set('checked', this.props.checked);\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has('checked')) {\r\n this.set('checked', this.props.checked);\r\n dmx.nextTick(() => this.dispatchEvent(\"updated\"));\r\n }\r\n },\r\n\r\n});\r\n","dmx.Component('form-data', {\r\n\r\n attributes: {\r\n name: {\r\n type: String,\r\n default: 'data',\r\n },\r\n\r\n data: {\r\n type: [Array, Object], // can be anything\r\n default: null,\r\n },\r\n },\r\n\r\n init (node) {\r\n this._formdataHandler = this._formdataHandler.bind(this);\r\n this._form = node.closest('form');\r\n \r\n if (this._form) {\r\n this._form.addEventListener('formdata', this._formdataHandler);\r\n }\r\n },\r\n\r\n destroy () {\r\n if (this._form) {\r\n this._form.removeEventListener('formdata', this._formdataHandler);\r\n }\r\n },\r\n\r\n _formdataHandler (event) {\r\n const formData = event.formData;\r\n const data = this.props.data;\r\n\r\n this._appendData(formData, data, this.props.name);\r\n },\r\n\r\n _appendData (formData, data, prefix = '') {\r\n if (Array.isArray(data)) {\r\n data.forEach((value, index) => {\r\n this._appendData(formData, value, `${prefix}[${index}]`);\r\n });\r\n } else if (typeof data === 'object') {\r\n for (const key in data) {\r\n this._appendData(formData, data[key], `${prefix}[${key}]`);\r\n }\r\n } else {\r\n formData.append(prefix, data);\r\n }\r\n },\r\n\r\n});","dmx.Attribute(\"bind\", \"mounted\", function (node, attr) {\r\n const name = attr.argument;\r\n const isToggle = dmx.reToggleAttribute.test(name);\r\n\r\n this.$watch(attr.value, value => {\r\n if (isToggle) {\r\n node.toggleAttribute(name, !!value);\r\n } else {\r\n if (name === \"style\" && typeof value === \"object\") {\r\n return Object.assign(node.style, value);\r\n }\r\n\r\n if (value == null) {\r\n return node.removeAttribute(name);\r\n }\r\n\r\n node.setAttribute(name, value);\r\n\r\n if (name === \"src\") {\r\n if (node.nodeName === \"VIDEO\" || node.nodeName === \"AUDIO\") {\r\n node.load();\r\n } else if (node.nodeName === \"SOURCE\" && node.parentNode) {\r\n node.parentNode.load();\r\n }\r\n }\r\n }\r\n });\r\n});\r\n","dmx.Attribute(\"on\", \"mounted\", function (node, attr) {\r\n if (!node.dmxOn) {\r\n node.dmxOn = {\r\n component: this,\r\n };\r\n }\r\n\r\n node.dmxOn[attr.argument] = true;\r\n\r\n return dmx.eventListener(node, attr.argument, function (event) {\r\n // jQuery event\r\n if (event.originalEvent) event = event.originalEvent;\r\n\r\n const retValue = dmx.parse(attr.value, dmx.DataScope({\r\n $event: event.$data,\r\n $originalEvent: event,\r\n }, node.dmxOn.component));\r\n\r\n return retValue;\r\n }, attr.modifiers);\r\n});\r\n","dmx.Attribute('repeat', 'before', function (node, attr) {\r\n const comment = document.createComment('Repeat Attribute');\r\n const template = document.createDocumentFragment();\r\n const RepeatItem = dmx.Component('repeat-item');\r\n\r\n node.parentNode.replaceChild(comment, node);\r\n node.removeAttribute(attr.fullName);\r\n\r\n template.append(node);\r\n\r\n let children = [];\r\n\r\n this.$watch(attr.value, repeat => {\r\n const items = dmx.repeatItems(repeat);\r\n\r\n if (items.length > 10000) {\r\n console.warn('More than 10000 repeat items, we limit the result!');\r\n //items.splice(10000, items.length);\r\n items.length = 10000;\r\n }\r\n\r\n if (attr.modifiers.fast) {\r\n if (children.length > items.length) {\r\n // destroy children\r\n children.splice(items.length).forEach(child => child.$destroy());\r\n }\r\n\r\n if (children.length) {\r\n // update existing children\r\n children.forEach((child, i) => child.set(items[i]));\r\n }\r\n\r\n if (items.length > children.length) {\r\n // add new children\r\n const fragment = document.createDocumentFragment();\r\n const toParse = new Set();\r\n\r\n items.slice(children.length).forEach(item => {\r\n const child = new RepeatItem(template.cloneNode(true), this, item);\r\n \r\n fragment.appendChild(child.$nodes[0]);\r\n //comment.parentNode.insertBefore(child.$nodes[0], comment);\r\n //child.$parse(child.$nodes[0]);\r\n toParse.add(child);\r\n\r\n children.push(child);\r\n this.$addChild(child);\r\n });\r\n\r\n comment.parentNode.insertBefore(fragment, comment);\r\n\r\n for (const child of toParse) {\r\n child.$parse(child.$nodes[0]);\r\n }\r\n\r\n if (attr.argument) {\r\n this.set(attr.argument, items);\r\n }\r\n }\r\n } else {\r\n const fragment = document.createDocumentFragment();\r\n const toParse = new Set();\r\n\r\n children.splice(0).forEach(child => child.$destroy());\r\n\r\n for (const item of items) {\r\n const child = new RepeatItem(template.cloneNode(true), this, item);\r\n \r\n fragment.append(child.$nodes[0]);\r\n //comment.parentNode.insertBefore(child.$nodes[0], comment);\r\n //child.$parse(child.$nodes[0]);\r\n toParse.add(child);\r\n\r\n children.push(child);\r\n this.$addChild(child);\r\n }\r\n\r\n comment.parentNode.insertBefore(fragment, comment);\r\n\r\n for (const child of toParse) {\r\n child.$parse(child.$nodes[0]);\r\n }\r\n\r\n if (attr.argument) {\r\n this.set(attr.argument, items);\r\n }\r\n }\r\n });\r\n});\r\n","dmx.Attribute(\"class\", \"mounted\", function (node, attr) {\r\n if (!node.dmxClass) {\r\n node.dmxClass = {\r\n component: this,\r\n };\r\n }\r\n\r\n this.$watch(attr.value, toggle => {\r\n node.dmxClass[attr.argument] = toggle;\r\n node.classList[toggle ? \"add\" : \"remove\"](attr.argument);\r\n });\r\n});\r\n","dmx.Attribute(\"hide\", \"mounted\", function (node, attr) {\r\n if (node.dmxHide) return;\r\n\r\n node.dmxHide = {\r\n component: this,\r\n initial: {\r\n display: node.style.getPropertyValue(\"display\"),\r\n priority: node.style.getPropertyPriority(\"display\"),\r\n },\r\n hide: null,\r\n };\r\n\r\n this.$watch(attr.value, hide => {\r\n node.dmxHide.hide = hide;\r\n\r\n const { initial } = node.dmxHide;\r\n const display = hide ? 'none' : initial.display;\r\n const priority = hide ? 'important' : initial.priority;\r\n\r\n node.style.setProperty('display', display, priority);\r\n });\r\n});\r\n","dmx.Attribute('show', 'mounted', function(node, attr) {\r\n if (node.dmxShow) return;\r\n\r\n node.dmxShow = {\r\n component: this,\r\n initial: {\r\n display: node.style.getPropertyValue('display'),\r\n priority: node.style.getPropertyPriority('display'),\r\n },\r\n show: null,\r\n };\r\n \r\n this.$watch(attr.value, show => {\r\n node.dmxShow.show = show;\r\n \r\n const { initial } = node.dmxShow;\r\n const display = show ? initial.display : 'none';\r\n const priority = show ? initial.priority : 'important';\r\n\r\n node.style.setProperty('display', display, priority);\r\n });\r\n});\r\n","dmx.Attribute(\"html\", \"mounted\", function (node, attr) {\r\n if (node.dmxHtml) return;\r\n\r\n node.dmxHtml = {\r\n component: this,\r\n };\r\n\r\n this.$watch(attr.value, html => {\r\n node.innerHTML = html != null ? String(html) : \"\";\r\n });\r\n});\r\n","dmx.Attribute(\"text\", \"mounted\", function (node, attr) {\r\n if (node.dmxText) return;\r\n\r\n node.dmxText = {\r\n component: this,\r\n };\r\n\r\n this.$watch(attr.value, text => {\r\n node.innerText = text != null ? String(text) : \"\";\r\n });\r\n});\r\n","dmx.Attribute(\"style\", \"mounted\", function (node, attr) {\r\n if (!node.dmxStyle) {\r\n node.dmxStyle = {\r\n component: this,\r\n };\r\n }\r\n\r\n const important = attr.modifiers.important ? \"important\" : \"\";\r\n\r\n this.$watch(attr.value, value => {\r\n node.dmxStyle[attr.argument] = value;\r\n if (value != null) {\r\n node.style.setProperty(attr.argument, value, important);\r\n }\r\n });\r\n});\r\n","dmx.Formatters('global', {\r\n\r\n // json(obj:Any):String\r\n json: function(obj) {\r\n return JSON.stringify(obj);\r\n },\r\n\r\n // log(obj:Any):Any\r\n log: function(obj) {\r\n console.log(obj);\r\n return obj;\r\n },\r\n\r\n // run(flow:Any, param:Any):Undefined\r\n run: function(flow, param) {\r\n var scope = dmx.DataScope({\r\n $param: param\r\n }, this);\r\n\r\n dmx.Flow.run(flow, scope);\r\n }\r\n\r\n});\r\n","dmx.Actions({\r\n\r\n subflow (options) {\r\n const subflow = this.parse(options.flow);\r\n const param = this.parse(options.param);\r\n\r\n return this.parse(subflow + \".runSub(\" + JSON.stringify(param) + \")\");\r\n },\r\n\r\n comment (options) {\r\n if (dmx.debug) {\r\n console.debug(options.message);\r\n }\r\n },\r\n\r\n wait (options) {\r\n const delay = this.parse(options.delay);\r\n\r\n if (typeof delay != \"number\") {\r\n throw new Error(\"wait: Invalid delay\");\r\n }\r\n\r\n return new Promise((resolve) => {\r\n setTimeout(resolve, delay);\r\n });\r\n },\r\n\r\n now (options) {\r\n return new Date().toISOString();\r\n },\r\n\r\n random (options) {\r\n let lower = this.parse(options.lower);\r\n let upper = this.parse(options.upper);\r\n let floating = !!this.parse(options.floating);\r\n\r\n if (typeof lower != \"number\" || !isFinite(lower)) {\r\n lower = 0;\r\n }\r\n\r\n if (typeof upper != \"number\" || !isFinite(upper)) {\r\n upper = 1;\r\n }\r\n\r\n let rnd = lower + Math.random() * (upper - lower);\r\n\r\n if (!floating && Math.floor(lower) == lower && Math.floor(upper) == upper) {\r\n rnd = Math.round(rnd);\r\n }\r\n\r\n return rnd;\r\n },\r\n\r\n confirm (options) {\r\n const message = this.parse(options.message);\r\n\r\n if (typeof message != \"string\") {\r\n throw new Error(\"confirm: Invalid message\");\r\n }\r\n\r\n const result = confirm(message);\r\n\r\n if (result) {\r\n if (options.then) {\r\n return this._exec(options.then).then(() => result);\r\n }\r\n } else if (options.else) {\r\n return this._exec(options.else).then(() => result);\r\n }\r\n\r\n return result;\r\n },\r\n\r\n prompt (options) {\r\n const message = this.parse(options.message);\r\n\r\n if (typeof message != \"string\") {\r\n throw new Error(\"prompt: Invalid message\");\r\n }\r\n\r\n return prompt(message);\r\n },\r\n\r\n alert (options) {\r\n const message = this.parse(options.message);\r\n\r\n if (typeof message != \"string\") {\r\n throw new Error(\"alert: Invalid message\");\r\n }\r\n\r\n return alert(message);\r\n },\r\n\r\n repeat (options) {\r\n let items = dmx.clone(this.parse(options.repeat));\r\n\r\n if (!items) return;\r\n\r\n if (typeof items == \"boolean\") {\r\n items = items ? [0] : [];\r\n } else if (typeof items == \"string\") {\r\n items = items.split(/\\s*,\\s*/);\r\n } else if (typeof items == \"number\") {\r\n var arr = [];\r\n for (var i = 0; i < items; i++) {\r\n arr.push(i + 1);\r\n }\r\n items = arr;\r\n }\r\n\r\n if (typeof items != \"object\") {\r\n throw new Error(\"repeat: data is not repeatable\");\r\n }\r\n\r\n const parentScope = this.scope;\r\n const parentOutput = this.output;\r\n return this._each(items, (value, index) => {\r\n this.scope = new dmx.DataScope(\r\n Object.assign(\r\n {\r\n $value: value,\r\n $index: index,\r\n $name: index,\r\n $key: index,\r\n $number: index + 1,\r\n $oddeven: index % 2,\r\n },\r\n value\r\n ),\r\n parentScope\r\n );\r\n\r\n this.output = {};\r\n\r\n if (Array.isArray(options.outputFields) && value instanceof Object) {\r\n options.outputFields.forEach((field) => {\r\n this.output[field] = value[field];\r\n });\r\n }\r\n\r\n return this._exec(options.exec).then(() => {\r\n var output = this.output;\r\n this.scope = parentScope;\r\n this.output = parentOutput;\r\n return output;\r\n });\r\n });\r\n },\r\n\r\n condition (options) {\r\n const result = !!this.parse(options.if);\r\n\r\n if (result) {\r\n if (options.then) {\r\n return this._exec(options.then).then(() => result);\r\n }\r\n } else if (options.else) {\r\n return this._exec(options.else).then(() => result);\r\n }\r\n\r\n return result;\r\n },\r\n\r\n conditions (options) {\r\n if (Array.isArray(options.conditions)) {\r\n for (let i = 0; i < options.conditions.length; i++) {\r\n const condition = options.conditions[i];\r\n\r\n if (this.parse(condition.when)) {\r\n return this._exec(condition.then);\r\n }\r\n }\r\n }\r\n },\r\n\r\n select (options) {\r\n const expression = this.parse(options.expression);\r\n\r\n if (Array.isArray(options.cases)) {\r\n for (let i = 0; i < options.cases.length; i++) {\r\n const item = options.cases[i];\r\n\r\n if (this.parse(item.value) == expression) {\r\n return this._exec(item.exec);\r\n }\r\n }\r\n }\r\n },\r\n\r\n group (options) {\r\n if (options.name) {\r\n const parentOutput = this.output;\r\n this.output = {};\r\n\r\n return this._exec(options.exec).then(() => {\r\n var output = this.output;\r\n self.output = parentOutput;\r\n return output;\r\n });\r\n }\r\n\r\n return this._exec(options.exec);\r\n },\r\n\r\n while (options) {\r\n const loop = () => {\r\n return new Promise((resolve) => {\r\n if (!this.parse(options.condition)) return resolve();\r\n this._exec(options.exec).then(loop).then(resolve);\r\n });\r\n };\r\n\r\n return loop();\r\n },\r\n\r\n switch (options) {\r\n /*\r\n {\r\n switch: {\r\n expression: \"{{myVar}}\",\r\n cases: [\r\n { case: 1, exec: { console.log: { message: \"Case 1\" }}}\r\n { case: 2, exec: { console.log: { message: \"Case 2\" }}}\r\n { case: 3, exec: { console.log: { message: \"Case 3\" }}}\r\n ],\r\n default: { console.log: { message: \"Default Case\" }}\r\n }\r\n }\r\n */\r\n const expression = this.parse(options.expression);\r\n for (let i = 0; i < options.cases.length; i++) {\r\n if (this.parse(options.cases[i].case) === expression) {\r\n return this._exec(options.cases[i].exec);\r\n }\r\n }\r\n if (options.default) {\r\n return this._exec(options.default);\r\n }\r\n },\r\n\r\n tryCatch (options) {\r\n return Promise.resolve(this._exec(options.try)).catch(() => {\r\n return this._exec(options.catch);\r\n });\r\n },\r\n\r\n run (options) {\r\n if (!options.action) {\r\n throw new Error(\"run: missing action\");\r\n }\r\n\r\n return this.parse(options.action);\r\n },\r\n\r\n runJS (options) {\r\n if (!options.function) {\r\n throw new Error(\"runJS: missing function\");\r\n }\r\n\r\n const func = this.parse(options.function);\r\n const args = this.parse(options.args);\r\n\r\n return window[func].apply(null, args);\r\n },\r\n\r\n assign (options) {\r\n return this.parse(options.value);\r\n },\r\n\r\n setGlobal (options) {\r\n const key = this.parse(options.key);\r\n const value = this.parse(options.value);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"setGlobal: key must be a string\");\r\n }\r\n\r\n dmx.global.set(key, value);\r\n\r\n return value;\r\n },\r\n\r\n setSession (options) {\r\n const key = this.parse(options.key);\r\n const value = this.parse(options.value);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"setSession: key must be a string\");\r\n }\r\n\r\n sessionStorage.setItem(key, JSON.stringify(value));\r\n\r\n return value;\r\n },\r\n\r\n getSession (options) {\r\n const key = this.parse(options.key);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"getSession: key must be a string\");\r\n }\r\n\r\n return JSON.parse(sessionStorage.getItem(key));\r\n },\r\n\r\n removeSession (options) {\r\n const key = this.parse(options.key);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"removeSession: key must be a string\");\r\n }\r\n\r\n sessionStorage.removeItem(key);\r\n\r\n return true;\r\n },\r\n\r\n setStorage (options) {\r\n const key = this.parse(options.key);\r\n const value = this.parse(options.value);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"setStorage: key must be a string\");\r\n }\r\n\r\n localStorage.setItem(key, JSON.stringify(value));\r\n\r\n return value;\r\n },\r\n\r\n getStorage (options) {\r\n const key = this.parse(options.key);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"getStorage: key must be a string\");\r\n }\r\n\r\n const value = localStorage.getItem(key);\r\n\r\n if (value == null) {\r\n return null;\r\n }\r\n\r\n return JSON.parse(value);\r\n },\r\n\r\n removeStorage (options) {\r\n const key = this.parse(options.key);\r\n\r\n if (typeof key != \"string\") {\r\n throw new Error(\"removeStorage: key must be a string\");\r\n }\r\n\r\n localStorage.removeItem(key);\r\n\r\n return true;\r\n },\r\n\r\n fetch (options) {\r\n let url = this.parse(options.url);\r\n let method = this.parse(options.method);\r\n let timeout = this.parse(options.timeout);\r\n let dataType = this.parse(options.dataType);\r\n let data = this.parse(options.data);\r\n let params = this.parse(options.params);\r\n let headers = this.parse(options.headers);\r\n let credentials = this.parse(options.credentials);\r\n let body = null;\r\n\r\n if (typeof url != \"string\") {\r\n throw new Error(\"fetch: invalid url \" + url);\r\n }\r\n\r\n if (![\"GET\", \"POST\", \"PUT\", \"DELETE\"].includes(method)) {\r\n //throw new Error('fetch: invalid method ' + method);\r\n method = \"GET\";\r\n }\r\n\r\n if (![\"auto\", \"json\", \"text\"].includes(dataType)) {\r\n dataType = \"auto\";\r\n }\r\n\r\n if (typeof timeout != \"number\") {\r\n timeout = 0;\r\n }\r\n\r\n if (!headers) {\r\n headers = {};\r\n }\r\n\r\n if (typeof params == \"object\") {\r\n for (var param in params) {\r\n if (params.hasOwnProperty(param) && params[param] != null) {\r\n url +=\r\n (url.indexOf(\"?\") != -1 ? \"&\" : \"?\") +\r\n decodeURIComponent(param) +\r\n \"=\" +\r\n decodeURIComponent(params[param]);\r\n }\r\n }\r\n }\r\n\r\n if (method != \"GET\") {\r\n if (dataType == \"text\") {\r\n if (!headers[\"Content-Type\"]) {\r\n headers[\"Content-Type\"] = \"application/text\";\r\n }\r\n body = data.toString();\r\n } else if (dataType == \"json\") {\r\n if (!headers[\"Content-Type\"]) {\r\n headers[\"Content-Type\"] = \"application/json\";\r\n }\r\n body = JSON.stringify(data);\r\n } else {\r\n if (method == \"POST\") {\r\n body = new FormData();\r\n\r\n if (typeof data == \"object\" && !Array.isArray(data)) {\r\n for (var key in data) {\r\n if (data.hasOwnProperty(key)) {\r\n var value = data[key];\r\n\r\n if (Array.isArray(value)) {\r\n if (!/\\[\\]$/.test(key)) {\r\n key += \"[]\";\r\n }\r\n for (var i in value) {\r\n if (value.hasOwnProperty(i)) {\r\n body.append(key, value[i]);\r\n }\r\n }\r\n } else {\r\n body.set(key, value);\r\n }\r\n }\r\n }\r\n }\r\n } else if (data) {\r\n if (!headers[\"Content-Type\"]) {\r\n headers[\"Content-Type\"] = \"application/text\";\r\n }\r\n body = data.toString();\r\n }\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n var xhr = new XMLHttpRequest();\r\n\r\n xhr.onerror = reject;\r\n xhr.onabort = reject;\r\n xhr.ontimeout = reject;\r\n xhr.onload = function () {\r\n var response = xhr.responseText;\r\n var headers = (function (raw) {\r\n var arr = raw.trim().split(/[\\r\\n]+/);\r\n\r\n return arr.reduce(function (headers, line) {\r\n var parts = line.split(\": \");\r\n var header = parts.shift();\r\n var value = parts.join(\": \");\r\n\r\n headers[header.toLowerCase()] = value;\r\n\r\n return headers;\r\n }, {});\r\n })(xhr.getAllResponseHeaders());\r\n\r\n if (/^application\\/json/.test(headers[\"content-type\"])) {\r\n response = JSON.parse(response);\r\n }\r\n\r\n resolve({\r\n status: xhr.status,\r\n headers: headers,\r\n data: response,\r\n });\r\n };\r\n\r\n xhr.open(method, url);\r\n\r\n xhr.timeout = timeout * 1000;\r\n\r\n for (var header in headers) {\r\n if (headers.hasOwnProperty(header)) {\r\n xhr.setRequestHeader(header, headers[header]);\r\n }\r\n }\r\n\r\n if (credentials) {\r\n xhr.withCredentials = true;\r\n }\r\n\r\n xhr.send(body);\r\n });\r\n },\r\n});\r\n\r\n// aliasses\r\ndmx.__actions[\"setValue\"] = dmx.__actions[\"assign\"];\r\ndmx.__actions[\"api\"] = dmx.__actions[\"fetch\"];\r\ndmx.__actions[\"api.send\"] = dmx.__actions[\"fetch\"];\r\ndmx.__actions[\"serverConnect\"] = dmx.__actions[\"fetch\"];\r\n","dmx.Actions({\r\n /**\r\n * Add new columns to the collection\r\n * @param {Object} options\r\n * @param {Object[]} options.collection - The collection\r\n * @param {Object.} options.add - Object with column name as key and the value\r\n * @param {boolean} [options.overwrite=false] - Overwrite existing columns\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.addColumns': function(options) {\r\n var collection = this.parse(options.collection);\r\n var add = options.add;\r\n var overwrite = !!this.parse(options.overwrite);\r\n\r\n if (!collection.length) return [];\r\n \r\n var output = [];\r\n \r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n var row = dmx.clone(collection[i]);\r\n\r\n for (var column in add) {\r\n if (add.hasOwnProperty(column)) {\r\n var scope = new dmx.DataScope(row, this.scope);\r\n\r\n if (overwrite || row[column] == null) {\r\n row[column] = dmx.parse(add[column], scope);\r\n }\r\n }\r\n }\r\n\r\n output.push(row);\r\n }\r\n\r\n return output;\r\n },\r\n\r\n /**\r\n * Remove entire specified columns from the collection\r\n * @param {Object} options\r\n * @param {Object[]} options.collection - The collection\r\n * @param {string[]} options.columns - The columns\r\n * @param {boolean} [options.keep=false] - Keep or remove the columns\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.filterColumns': function(options) {\r\n var collection = this.parse(options.collection);\r\n var columns = this.parse(options.columns);\r\n var keep = !!this.parse(options.keep);\r\n\r\n if (!collection.length) return [];\r\n\r\n var output = [];\r\n\r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n var row = collection[i];\r\n var newRow = {};\r\n\r\n for (var column in row) {\r\n if (row.hasOwnProperty(column)) {\r\n if (columns.includes(column)) {\r\n if (keep) {\r\n newRow[column] = dmx.clone(row[column]);\r\n }\r\n } else if (!keep) {\r\n newRow[column] = dmx.clone(row[column]);\r\n }\r\n }\r\n }\r\n\r\n output.push(newRow);\r\n }\r\n\r\n return output;\r\n },\r\n\r\n /**\r\n * Rename columns in the collection\r\n * @param {Object} options\r\n * @param {Object[]} options.collection - The collection\r\n * @param {Object.} options.rename - Object with old name as key and new name as value\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.renameColumns': function(options) {\r\n var collection = this.parse(options.collection);\r\n var rename = this.parse(options.rename);\r\n\r\n if (!collection.length) return [];\r\n \r\n var output = [];\r\n\r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n var row = collection[i];\r\n var newRow = {};\r\n\r\n for (var column in row) {\r\n if (row.hasOwnProperty(column)) {\r\n newRow[rename[column] || column] = dmx.clone(row[column]);\r\n }\r\n }\r\n\r\n output.push(newRow);\r\n }\r\n\r\n return output;\r\n },\r\n\r\n /**\r\n * Fills empty rows with the row above's value\r\n * @param {Object} options\r\n * @param {Object[]} options.collection - The collection\r\n * @param {string[]} options.columns - The columns to fill\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.fillDown': function(options) {\r\n var collection = this.parse(options.collection);\r\n var columns = this.parse(options.columns);\r\n\r\n if (!collection.length) return [];\r\n\r\n var output = [];\r\n var toFill = {};\r\n\r\n for (var i = 0, l = columns.length; i < l; i++) {\r\n toFill[columns[i]] = null;\r\n }\r\n\r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n var row = dmx.clone(collection[i]);\r\n\r\n for (var column in toFill) {\r\n if (toFill.hasOwnProperty(column)) {\r\n if (row[column] == null) {\r\n row[column] = toFill[column];\r\n } else {\r\n toFill[column] = row[column];\r\n }\r\n }\r\n }\r\n\r\n output.push(row);\r\n }\r\n\r\n return output;\r\n },\r\n\r\n /**\r\n * Add new rows to the collection\r\n * @param {Object} options\r\n * @param {Object[]} options.collection - The collection\r\n * @param {Object[]} options.rows - The rows to add\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.addRows': function(options) {\r\n var collection = this.parse(options.collection);\r\n var rows = this.parse(options.rows);\r\n\r\n return dmx.clone(collection).concat(dmx.clone(rows));\r\n },\r\n\r\n /**\r\n * Add row numbers to the collection\r\n * @param {Object} options \r\n * @param {Object[]} options.collection - The collection\r\n * @param {string} options.column - The name for the column\r\n * @param {number} options.startAt - The row number to start with\r\n * @param {boolean} [options.desc=false] - Order descending\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.addRowNumbers': function(options) {\r\n var collection = this.parse(options.collection);\r\n var column = this.parse(options.column);\r\n var startAt = this.parse(options.startAt);\r\n var desc = !!this.parse(options.desc);\r\n\r\n var output = [];\r\n\r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n var row = dmx.clone(collection[i]);\r\n row[column] = desc ? l + startAt - i : startAt + i;\r\n output.push(row);\r\n }\r\n\r\n return output;\r\n },\r\n\r\n /**\r\n * Join two collections (Left join)\r\n * @param {Object} options\r\n * @param {Object[]} options.collection1 - Left collection\r\n * @param {Object[]} options.collection2 - Right collection\r\n * @param {Object.} options.matches - Columns to match\r\n * @param {boolean} [options.matchAll=false] - Match columns using AND instead of OR\r\n * @returns {Object[]} - New collection\r\n */\r\n 'colections.join': function(options) {\r\n var collection1 = this.parse(options.collection1);\r\n var collection2 = this.parse(options.collection2);\r\n var matches = this.parse(options.matches);\r\n var matchAll = !!this.parse(options.matchAll);\r\n\r\n var output = [];\r\n\r\n for (var i = 0, l = collection1.length; i < l; i++) {\r\n var row = dmx.clone(collection1[i]);\r\n\r\n for (var j = 0, l2 = collection2.length; j < l2; j++) {\r\n var row2 = collection2[j];\r\n var hasMatch = false;\r\n\r\n for (var match in matches) {\r\n if (matches.hasOwnProperty(match)) {\r\n if (row[match] == row2[matches[match]]) {\r\n hasMatch = true;\r\n if (!matchAll) break;\r\n } else if (matchAll) {\r\n hasMatch = false;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (hasMatch) {\r\n for (var column in row2) {\r\n if (row2.hasOwnProperty(column)) {\r\n // TODO duplicate row from collection1 when multiple matches exist in collection2\r\n // TODO check for duplicate column names\r\n row[column] = dmx.clone(row2[column]);\r\n }\r\n }\r\n break;\r\n }\r\n }\r\n\r\n output.push(row);\r\n }\r\n\r\n return output;\r\n },\r\n\r\n /**\r\n * Normalize all rows, add missing columns with NULL value\r\n * @param {Object} options\r\n * @param {Object[]} options.collection - The collection\r\n * @returns {Object[]} - New collection\r\n */\r\n 'collections.mormalize': function(options) {\r\n var collection = this.parse(options.collection);\r\n\r\n var columns = [];\r\n var output = [];\r\n\r\n // first collect all columns from collection\r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n for (var column in collection[i]) {\r\n if (collection[i].hasOwnProperty(column)) {\r\n if (columns.indexOf(column) == -1) {\r\n columns.push(column);\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (var i = 0, l = collection.length; i < l; i++) {\r\n var row = {};\r\n\r\n for (var j = 0, l2 = columns.length; j < l2; j++) {\r\n var column = columns[j];\r\n var value = dmx.clone(collection[column]);\r\n row[column] = value != null ? value : null;\r\n }\r\n\r\n output.push(row);\r\n }\r\n\r\n return output;\r\n }\r\n\r\n});","dmx.Actions({\r\n\r\n 'console.log': function(options) {\r\n console.log(this.parse(options.message));\r\n },\r\n\r\n 'console.info': function(options) {\r\n console.info(this.parse(options.message));\r\n },\r\n\r\n 'console.warn': function(options) {\r\n console.warn(this.parse(options.message));\r\n },\r\n\r\n 'console.error': function(options) {\r\n console.error(this.parse(options.message));\r\n },\r\n\r\n 'console.count': function(options) {\r\n console.count(this.parse(options.label));\r\n },\r\n\r\n 'console.time': function(options) {\r\n console.time(this.parse(options.label));\r\n },\r\n\r\n 'console.timeEnd': function(options) {\r\n console.timeEnd(this.parse(options.label));\r\n },\r\n\r\n 'console.group': function(options) {\r\n console.group();\r\n },\r\n\r\n 'console.groupEnd': function(options) {\r\n console.groupEnd();\r\n },\r\n\r\n 'console.clear': function(options) {\r\n console.clear();\r\n }\r\n\r\n});"]} \ No newline at end of file diff --git a/public/dmxAppConnect/dmxRouting/dmxRouting.js b/public/dmxAppConnect/dmxRouting/dmxRouting.js new file mode 100644 index 0000000..a70c2fe --- /dev/null +++ b/public/dmxAppConnect/dmxRouting/dmxRouting.js @@ -0,0 +1,8 @@ +/*! + App Connect Routing + Version: 3.0.5 + (c) 2024 Wappler.io + @build 2024-09-30 10:49:02 + */ +dmx.config.mapping.a="link",(()=>{const t=dmx.routing;dmx.routing={router:"hybrid",base:"",routes:[],getRoutes(){return this.routes.filter((function(t){return!t.app||t.app==dmx.app.name}))},getBase(){if(this.base)return this.base;var t=document.querySelector("base[href]");return t?t.getAttribute("href"):""},getUrlInfo(){var t="hash"==this.router?new URL(window.location.hash.slice(2),window.location.origin):window.location;return{path:t.pathname||"/",query:t.search.slice(1),hash:t.hash.slice(1)}},match(t,e,r){t=t||this.getUrlInfo().path,e=e||this.getRoutes();var i=dmx.routing.getBase();i&&(t=t.replace(i,"").replace(/^\/?/,"/"));for(var n=0;nt.replace(/\/$/,"")+"/"+e.replace(/^\//,""),evalScripts(t){window.grecaptcha&&t.querySelectorAll(".g-recaptcha").forEach((t=>{grecaptcha.render(t)}));try{t.querySelectorAll('script[type="text/javascript"],script:not([type])').forEach((t=>{try{const e=document.createElement("script");e.type="text/javascript",t.src&&(e.src=t.src),t.innerHTML&&(e.innerHTML=t.innerHTML),t.parentNode.replaceChild(e,t)}catch(e){console.error("Error executing script "+t.src,e)}}))}catch(t){console.error("An error occurred while trying to execute scripts",t)}}},t&&Object.assign(dmx.routing,t)})(),dmx.Component("route",{initialData:{isExact:!1,isMatch:!1,loading:!1,params:{},path:"",url:""},attributes:{path:{type:String,default:"*"},exact:{type:Boolean,default:!1},url:{type:String,default:""}},events:{show:Event,hide:Event,error:Event,unauthorized:Event,forbidden:Event,notfound:Event},render:!1,init(t){this._locationHandler=this._locationHandler.bind(this),this._error=this._error.bind(this),this._cache=new Map,this._content=t.innerHTML,this._keys=[],this._re=dmx.pathToRegexp(this.props.path,this._keys,{end:this.props.exact}),this._shown=!1,t.innerHTML="",window.addEventListener("popstate",this._locationHandler),window.addEventListener("pushstate",this._locationHandler),window.addEventListener("replacestate",this._locationHandler),window.addEventListener("hashchange",this._locationHandler),this._locationHandler()},destroy(){window.removeEventListener("popstate",this._locationHandler),window.removeEventListener("pushstate",this._locationHandler),window.removeEventListener("replacestate",this._locationHandler),window.removeEventListener("hashchange",this._locationHandler)},performUpdate(t){this.set({path:this.props.path,isExact:this.props.exact})},_load(){this._hide();const t=this.props.url;if(this._cache.has(t))return this._loaded=t,this._content=this._cache.get(t),void this._show();this.set("loading",!0),this._abortController=new AbortController,fetch(t,{credentials:"same-origin",signal:this._abortController.signal}).then((t=>t.text())).then((e=>{this.set("loading",!1),this._loaded=t,this._content=e,this._cache.set(t,e),this._show()})).catch(this._error)},_show(){this._shown||(this._abortController&&this._abortController.abort(),this.$node.innerHTML=this._content,this.$parse(),dmx.routing.evalScripts(this.$node),this._shown=!0,this.dispatchEvent("show"))},_hide(){this._shown&&(this._abortController&&this._abortController.abort(),this.effects&&(this.effects.forEach((t=>t())),this.effects=null),this.$destroyChildren(),this.$node.innerHTML="",this._shown=!1,this.dispatchEvent("hide"))},_error(t){this.set("loading",!1),this.dispatchEvent("error"),dmx.debug&&console.error(t)},_locationHandler(t){let e=dmx.routing.getUrlInfo().path;if("hybrid"==dmx.routing.router){let t=document.querySelector('meta[name="ac:base"]'),r=document.querySelector('meta[name="ac:route"]');t&&t.content&&(e=e.replace(t.content.replace(/\/$/,""),"")),r&&r.content&&(e=e.replace(dmx.pathToRegexp(r.content,[],{end:!1}),"").replace(/^(\/+)?/,"/"))}const r=this._re.exec(e);if(r){if(this.set({isMatch:!0,url:r[0],params:this._keys.reduce(((t,e,i)=>(t[e.name]=r[i+1],t)),{})}),this.data.loading&&this.props.url===this._loaded)return;this.props.url&&this.props.url!==this._loaded?this._load():this._show()}else this.set({isMatch:!1,url:"",params:{}}),this._hide()}}),dmx.Component("view",{initialData:{loading:!1,params:null},events:{load:Event,error:Event,unauthorized:Event,forbidden:Event,notfound:Event},init(t){window.__WAPPLER__||(this._locationHandler=this._locationHandler.bind(this),window.addEventListener("popstate",this._locationHandler),window.addEventListener("pushstate",this._locationHandler),window.addEventListener("replacestate",this._locationHandler),window.addEventListener("hashchange",this._locationHandler),"hybrid"==dmx.routing.router?this._url=location.pathname:this._locationHandler())},destroy(){window.removeEventListener("popstate",this._locationHandler),window.removeEventListener("pushstate",this._locationHandler),window.removeEventListener("replacestate",this._locationHandler),window.removeEventListener("hashchange",this._locationHandler)},_load(t){this._url==t&&this.data.loading||this._url!=t&&(this.set("loading",!0),this._url=t,this._abortController&&this._abortController.abort(),this._abortController=new AbortController,fetch(t+(t.includes("?")?"&":"?")+"fragment=true",{credentials:"same-origin",headers:[["Accept","text/fragment+html"]],signal:this._abortController.signal}).then((t=>{if(this.set("loading",!1),200==t.status||0==t.status)t.text().then((t=>{this._show(t),this.dispatchEvent("load"),dmx.app._parseQuery()})).catch((t=>{console.error(t),this.dispatchEvent("error")}));else if(222==t.status)t.text().then((t=>{location.assign(t)})).catch((t=>{console.error(t),this.dispatchEvent("error")}));else{const e={401:"unauthorized",403:"forbidden",404:"notfound"};this.dispatchEvent(e[t.status]||"error")}})).catch((t=>{console.error(t),this.set("loading",!1),this.dispatchEvent("error")})))},_show(t){this.effects&&(this.effects.forEach((t=>t())),this.effects=null),this.$destroyChildren(),this.$node.innerHTML=t,this.$parse(),dmx.routing.evalScripts(this.$node)},_locationHandler(t){if("hybrid"==dmx.routing.router)this._load(location.pathname);else{let t=dmx.routing.getUrlInfo().path,e=dmx.routing.getRoutes(),r=this.parent;for(;r;){if(r.routes){e=r.routes;break}r=r.parent}const i=dmx.routing.match(t,e,r);i?(this.path=i.path,this.routes=i.routes,this.set("params",i.params),this._load(i.url)):console.warn("Route for "+t+" not found")}}}),dmx.Component("link",{attributes:{internal:{type:Boolean,default:!1},href:{type:String,default:null}},init(t){this._clickHandler=this._clickHandler.bind(this),this._stateHandler=this._stateHandler.bind(this),t.addEventListener("click",this._clickHandler),this.props.href&&(this.$node.setAttribute("href",this.props.href),this._fixUrl(t.getAttribute("href"))),t.classList.contains("nav-link")&&(window.addEventListener("popstate",this._stateHandler),window.addEventListener("pushstate",this._stateHandler),window.addEventListener("replacestate",this._stateHandler),window.addEventListener("hashchange",this._stateHandler),this._stateHandler())},performUpdate(t){t.has("href")&&(this.props.href?(this.$node.setAttribute("href",this.props.href),this._fixUrl(this.props.href)):this.$node.removeAttribute("href"))},destroy(){this.$node.removeEventListener("click",this._clickHandler),window.removeEventListener("popstate",this._stateHandler),window.removeEventListener("pushstate",this._stateHandler),window.removeEventListener("replacestate",this._stateHandler),window.removeEventListener("hashchange",this._stateHandler)},_fixUrl(t){if(t.startsWith("./")){let e=this.parent;for(;e;){if(e.routes&&e.path){t=dmx.routing.join("./",t.replace("./",e.path));break}e=e.parent}let r=document.querySelector('meta[name="ac:route"]');if(r&&r.content){let e=r.content,i=document.querySelector('meta[name="ac:base"]');i&&i.content&&(e=i.content.replace(/\/$/,"")+e);let n=dmx.pathToRegexp(e,[],{end:!1}).exec(location.pathname);n&&this.$node.setAttribute("href",t.replace("./",n[0].replace(/(\/+)?$/,"/")))}else this.$node.setAttribute("href",dmx.routing.join(dmx.routing.getBase(),t));this.props.internal=!0}},_navigate(t){if(t.startsWith("#"))return void(location.hash=t);"hash"===dmx.routing.router&&(t="#!"+t);const e=this.$node.title;history.pushState({title:e||document.title},"",t),e&&(document.title=e),window.dispatchEvent(new Event("pushstate"))},_clickHandler(t){const e=this.$node.getAttribute("href");e&&(this.props.internal||e.startsWith("#"))&&!t.ctrlKey&&0===t.button&&(t.preventDefault(),this._navigate(e))},_stateHandler(){const t=this.$node,e=t.getAttribute("href");if(e&&e.startsWith("#"))return;const r=t.href==window.location.href||t.href==window.location.href.split("?")[0].split("#")[0];if(t.classList.toggle("active",r),t.classList.contains("dropdown-item")){const e=t.parentNode.querySelectorAll(".dropdown-item");t.classList.remove("active");for(let r=0;r {\r\n\r\n const settings = dmx.routing;\r\n\r\n dmx.routing = {\r\n router: \"hybrid\", // hybrid (combined server/client routing), path or hash\r\n\r\n base: \"\", // todo: use when router is path (when page is in a subfolder)\r\n\r\n routes: [],\r\n\r\n getRoutes () {\r\n return this.routes.filter(function (route) {\r\n return !route.app || route.app == dmx.app.name;\r\n });\r\n },\r\n\r\n getBase () {\r\n if (this.base) {\r\n return this.base;\r\n } else {\r\n var base = document.querySelector(\"base[href]\");\r\n if (base) return base.getAttribute(\"href\");\r\n }\r\n\r\n return \"\";\r\n },\r\n\r\n getUrlInfo () {\r\n var url =\r\n this.router == \"hash\"\r\n ? new URL(window.location.hash.slice(2), window.location.origin)\r\n : window.location;\r\n\r\n return {\r\n path: url.pathname || \"/\",\r\n query: url.search.slice(1),\r\n hash: url.hash.slice(1),\r\n };\r\n },\r\n\r\n match (path, routes, parent) {\r\n path = path || this.getUrlInfo().path;\r\n routes = routes || this.getRoutes();\r\n\r\n var base = dmx.routing.getBase();\r\n if (base) {\r\n path = path.replace(base, \"\").replace(/^\\/?/, \"/\");\r\n }\r\n\r\n for (var i = 0; i < routes.length; i++) {\r\n if (routes[i].routes) {\r\n routes[i].end = false;\r\n }\r\n\r\n var keys = [];\r\n var re = dmx.pathToRegexp(\r\n dmx.routing.join(\r\n parent && parent.path ? parent.path : \"/\",\r\n routes[i].path\r\n ),\r\n keys,\r\n routes[i]\r\n );\r\n var match = re.exec(decodeURI(path));\r\n\r\n if (match) {\r\n return {\r\n path: match[0],\r\n params: keys.reduce(function (params, key, index) {\r\n params[key.name] = match[index + 1];\r\n return params;\r\n }, {}),\r\n url: routes[i].url,\r\n routes: routes[i].routes || [],\r\n };\r\n }\r\n }\r\n\r\n return null;\r\n },\r\n\r\n join (path1, path2) {\r\n return path1.replace(/\\/$/, \"\") + \"/\" + path2.replace(/^\\//, \"\");\r\n },\r\n\r\n evalScripts(node) {\r\n if (window.grecaptcha) {\r\n node.querySelectorAll(\".g-recaptcha\").forEach(node => { grecaptcha.render(node) });\r\n }\r\n\r\n try {\r\n node.querySelectorAll('script[type=\"text/javascript\"],script:not([type])').forEach(script => {\r\n try {\r\n const newScript = document.createElement(\"script\");\r\n newScript.type = \"text/javascript\";\r\n if (script.src) newScript.src = script.src;\r\n if (script.innerHTML) newScript.innerHTML = script.innerHTML;\r\n script.parentNode.replaceChild(newScript, script);\r\n } catch (e) {\r\n console.error(\"Error executing script \" + script.src, e);\r\n }\r\n });\r\n } catch (e) {\r\n console.error(\"An error occurred while trying to execute scripts\", e);\r\n }\r\n },\r\n };\r\n\r\n if (settings) {\r\n // merge settings\r\n Object.assign(dmx.routing, settings);\r\n }\r\n\r\n})();","dmx.Component('route', {\r\n\r\n initialData: {\r\n isExact: false,\r\n isMatch: false,\r\n loading: false,\r\n params: {}, // Key/value pairs parsed from the URL corresponding to the dynamic segments of the path\r\n path: '', // The path pattern used to match. Useful for building nested Routes\r\n url: '', // The matched portion of the URL. Useful for building nested Links\r\n },\r\n\r\n attributes: {\r\n path: {\r\n type: String,\r\n default: '*',\r\n },\r\n\r\n exact: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n url: {\r\n type: String,\r\n default: '',\r\n },\r\n },\r\n\r\n events: {\r\n show: Event,\r\n hide: Event,\r\n error: Event,\r\n unauthorized: Event,\r\n forbidden: Event,\r\n notfound: Event,\r\n },\r\n\r\n render: false,\r\n\r\n init (node) {\r\n this._locationHandler = this._locationHandler.bind(this);\r\n this._error = this._error.bind(this);\r\n\r\n this._cache = new Map();\r\n this._content = node.innerHTML;\r\n this._keys = [];\r\n this._re = dmx.pathToRegexp(this.props.path, this._keys, { end: this.props.exact });\r\n this._shown = false;\r\n \r\n node.innerHTML = '';\r\n\r\n window.addEventListener(\"popstate\", this._locationHandler);\r\n window.addEventListener(\"pushstate\", this._locationHandler);\r\n window.addEventListener(\"replacestate\", this._locationHandler);\r\n window.addEventListener('hashchange', this._locationHandler);\r\n\r\n this._locationHandler();\r\n },\r\n\r\n destroy () {\r\n window.removeEventListener(\"popstate\", this._locationHandler);\r\n window.removeEventListener(\"pushstate\", this._locationHandler);\r\n window.removeEventListener(\"replacestate\", this._locationHandler);\r\n window.removeEventListener('hashchange', this._locationHandler);\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n this.set({\r\n path: this.props.path,\r\n isExact: this.props.exact,\r\n });\r\n },\r\n\r\n _load () {\r\n this._hide();\r\n\r\n const url = this.props.url;\r\n\r\n if (this._cache.has(url)) {\r\n this._loaded = url;\r\n this._content = this._cache.get(url);\r\n this._show();\r\n return;\r\n }\r\n\r\n this.set('loading', true);\r\n\r\n this._abortController = new AbortController();\r\n\r\n fetch(url, {\r\n credentials: 'same-origin',\r\n signal: this._abortController.signal,\r\n }).then(reponse => reponse.text()).then(content => {\r\n this.set('loading', false);\r\n this._loaded = url;\r\n this._content = content;\r\n this._cache.set(url, content);\r\n this._show();\r\n }).catch(this._error);\r\n },\r\n\r\n _show () {\r\n if (this._shown) return;\r\n\r\n if (this._abortController) {\r\n this._abortController.abort();\r\n }\r\n\r\n this.$node.innerHTML = this._content;\r\n this.$parse();\r\n \r\n dmx.routing.evalScripts(this.$node);\r\n\r\n this._shown = true;\r\n this.dispatchEvent('show');\r\n },\r\n\r\n _hide () {\r\n if (!this._shown) return;\r\n\r\n if (this._abortController) {\r\n this._abortController.abort();\r\n }\r\n\r\n if (this.effects) {\r\n this.effects.forEach((effect) => effect());\r\n this.effects = null;\r\n }\r\n this.$destroyChildren();\r\n this.$node.innerHTML = '';\r\n\r\n this._shown = false;\r\n this.dispatchEvent('hide');\r\n },\r\n\r\n _error (err) {\r\n this.set('loading', false);\r\n this.dispatchEvent('error');\r\n if (dmx.debug) console.error(err);\r\n },\r\n\r\n _locationHandler (event) {\r\n let path = dmx.routing.getUrlInfo().path;\r\n\r\n if (dmx.routing.router == 'hybrid') {\r\n let base = document.querySelector('meta[name=\"ac:base\"]');\r\n let route = document.querySelector('meta[name=\"ac:route\"]');\r\n\r\n if (base && base.content) {\r\n path = path.replace(base.content.replace(/\\/$/, ''), '');\r\n }\r\n\r\n if (route && route.content) {\r\n path = path.replace(dmx.pathToRegexp(route.content, [], { end: false }), '').replace(/^(\\/+)?/, '/');\r\n }\r\n }\r\n\r\n const match = this._re.exec(path);\r\n\r\n if (match) {\r\n this.set({\r\n isMatch: true,\r\n url: match[0],\r\n params: this._keys.reduce((params, key, index) => {\r\n params[key.name] = match[index + 1];\r\n return params;\r\n }, {})\r\n });\r\n\r\n if (this.data.loading && this.props.url === this._loaded) {\r\n // Url is loading\r\n return;\r\n }\r\n\r\n if (this.props.url && this.props.url !== this._loaded) {\r\n this._load();\r\n } else {\r\n this._show();\r\n }\r\n } else {\r\n this.set({\r\n isMatch: false,\r\n url: '',\r\n params: {},\r\n });\r\n\r\n this._hide();\r\n }\r\n },\r\n\r\n});\r\n","dmx.Component(\"view\", {\r\n\r\n initialData: {\r\n loading: false,\r\n params: null,\r\n },\r\n\r\n events: {\r\n load: Event,\r\n error: Event,\r\n unauthorized: Event,\r\n forbidden: Event,\r\n notfound: Event,\r\n },\r\n\r\n init (node) {\r\n if (window.__WAPPLER__) return;\r\n\r\n this._locationHandler = this._locationHandler.bind(this);\r\n\r\n window.addEventListener(\"popstate\", this._locationHandler);\r\n window.addEventListener(\"pushstate\", this._locationHandler);\r\n window.addEventListener(\"replacestate\", this._locationHandler);\r\n window.addEventListener('hashchange', this._locationHandler);\r\n\r\n if (dmx.routing.router == \"hybrid\") {\r\n this._url = location.pathname;\r\n } else {\r\n this._locationHandler();\r\n }\r\n },\r\n\r\n destroy () {\r\n window.removeEventListener(\"popstate\", this._locationHandler);\r\n window.removeEventListener(\"pushstate\", this._locationHandler);\r\n window.removeEventListener(\"replacestate\", this._locationHandler);\r\n window.removeEventListener('hashchange', this._locationHandler);\r\n },\r\n\r\n _load (url) {\r\n if (this._url == url && this.data.loading) {\r\n // Url is loading\r\n return;\r\n }\r\n\r\n if (this._url != url) {\r\n this.set(\"loading\", true);\r\n this._url = url;\r\n\r\n if (this._abortController) {\r\n this._abortController.abort();\r\n }\r\n\r\n this._abortController = new AbortController();\r\n\r\n fetch(url + (url.includes(\"?\") ? \"&\" : \"?\") + \"fragment=true\", {\r\n credentials: 'same-origin',\r\n headers: [['Accept', 'text/fragment+html']],\r\n signal: this._abortController.signal,\r\n }).then(response => {\r\n this.set(\"loading\", false);\r\n\r\n if (response.status == 200 || response.status == 0) {\r\n response.text().then(html => {\r\n this._show(html)\r\n this.dispatchEvent(\"load\");\r\n //window.dispatchEvent(new Event('load'));\r\n dmx.app._parseQuery();\r\n }).catch(err => {\r\n console.error(err);\r\n this.dispatchEvent('error')\r\n });\r\n } else {\r\n if (response.status == 222) {\r\n response.text().then(url => {\r\n location.assign(url);\r\n }).catch(err => {\r\n console.error(err);\r\n this.dispatchEvent('error')\r\n });\r\n } else {\r\n const events = { 401: 'unauthorized', 403: 'forbidden', 404: 'notfound' };\r\n this.dispatchEvent(events[response.status] || 'error');\r\n }\r\n }\r\n }).catch(err => {\r\n console.error(err);\r\n this.set(\"loading\", false);\r\n this.dispatchEvent('error');\r\n });\r\n }\r\n },\r\n\r\n _show (html) {\r\n if (this.effects) {\r\n this.effects.forEach((effect) => effect());\r\n this.effects = null;\r\n }\r\n\r\n this.$destroyChildren();\r\n this.$node.innerHTML = html;\r\n this.$parse();\r\n\r\n dmx.routing.evalScripts(this.$node);\r\n },\r\n\r\n _locationHandler (event) {\r\n if (dmx.routing.router == \"hybrid\") {\r\n this._load(location.pathname);\r\n } else {\r\n let path = dmx.routing.getUrlInfo().path;\r\n let routes = dmx.routing.getRoutes();\r\n let parent = this.parent;\r\n\r\n while (parent) {\r\n if (parent.routes) {\r\n routes = parent.routes;\r\n break;\r\n }\r\n\r\n parent = parent.parent;\r\n }\r\n\r\n const route = dmx.routing.match(path, routes, parent);\r\n\r\n if (route) {\r\n this.path = route.path;\r\n this.routes = route.routes;\r\n this.set(\"params\", route.params);\r\n this._load(route.url);\r\n } else {\r\n console.warn(\"Route for \" + path + \" not found\");\r\n }\r\n }\r\n },\r\n\r\n});\r\n","dmx.Component('link', {\r\n\r\n attributes: {\r\n internal: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n href: {\r\n type: String,\r\n default: null,\r\n },\r\n },\r\n\r\n init (node) {\r\n this._clickHandler = this._clickHandler.bind(this);\r\n this._stateHandler = this._stateHandler.bind(this);\r\n\r\n node.addEventListener('click', this._clickHandler);\r\n\r\n if (this.props.href) {\r\n this.$node.setAttribute('href', this.props.href);\r\n this._fixUrl(node.getAttribute('href'));\r\n }\r\n\r\n if (node.classList.contains('nav-link')) {\r\n window.addEventListener(\"popstate\", this._stateHandler);\r\n window.addEventListener(\"pushstate\", this._stateHandler);\r\n window.addEventListener(\"replacestate\", this._stateHandler);\r\n window.addEventListener('hashchange', this._stateHandler);\r\n this._stateHandler();\r\n }\r\n },\r\n\r\n performUpdate (updatedProps) {\r\n if (updatedProps.has('href')) {\r\n if (this.props.href) {\r\n this.$node.setAttribute('href', this.props.href);\r\n this._fixUrl(this.props.href);\r\n } else {\r\n this.$node.removeAttribute('href');\r\n }\r\n }\r\n },\r\n\r\n destroy () {\r\n this.$node.removeEventListener('click', this._clickHandler);\r\n window.removeEventListener(\"popstate\", this._stateHandler);\r\n window.removeEventListener(\"pushstate\", this._stateHandler);\r\n window.removeEventListener(\"replacestate\", this._stateHandler);\r\n window.removeEventListener('hashchange', this._stateHandler);\r\n },\r\n\r\n _fixUrl (url) {\r\n if (url.startsWith('./')) {\r\n let parent = this.parent;\r\n\r\n while (parent) {\r\n if (parent.routes && parent.path) {\r\n url = dmx.routing.join('./', url.replace('./', parent.path));\r\n break;\r\n }\r\n\r\n parent = parent.parent;\r\n }\r\n\r\n let route = document.querySelector('meta[name=\"ac:route\"]');\r\n if (route && route.content) {\r\n let path = route.content;\r\n let base = document.querySelector('meta[name=\"ac:base\"]');\r\n if (base && base.content) {\r\n path = base.content.replace(/\\/$/, '') + path;\r\n }\r\n let match = dmx.pathToRegexp(path, [], { end: false }).exec(location.pathname);\r\n if (match) {\r\n this.$node.setAttribute('href', url.replace('./', match[0].replace(/(\\/+)?$/, '/')));\r\n }\r\n } else {\r\n this.$node.setAttribute('href', dmx.routing.join(dmx.routing.getBase(), url));\r\n }\r\n\r\n this.props.internal = true;\r\n }\r\n },\r\n\r\n _navigate (url) {\r\n if (url.startsWith('#')) {\r\n location.hash = url;\r\n return;\r\n }\r\n \r\n if (dmx.routing.router === 'hash') {\r\n url = '#!' + url;\r\n }\r\n \r\n const title = this.$node.title;\r\n history.pushState({ title: title || document.title }, '', url);\r\n if (title) document.title = title;\r\n window.dispatchEvent(new Event('pushstate'));\r\n },\r\n\r\n _clickHandler (event) {\r\n const url = this.$node.getAttribute('href');\r\n\r\n if (url && (this.props.internal || url.startsWith('#')) && !event.ctrlKey && event.button === 0) {\r\n event.preventDefault();\r\n this._navigate(url);\r\n }\r\n },\r\n\r\n _stateHandler () {\r\n const node = this.$node;\r\n const url = node.getAttribute('href');\r\n if (url && url.startsWith('#')) return;\r\n const active = node.href == window.location.href || node.href == window.location.href.split(\"?\")[0].split(\"#\")[0];\r\n\r\n node.classList.toggle('active', active);\r\n\r\n if (node.classList.contains('dropdown-item')) {\r\n const items = node.parentNode.querySelectorAll('.dropdown-item');\r\n node.classList.remove('active');\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const match = items[i].href == window.location.href || items[i].href == window.location.href.split(\"?\")[0].split(\"#\")[0];\r\n if (match) {\r\n items[i].classList.add('active');\r\n node.classList.add('active');\r\n } else {\r\n items[i].classList.remove('active');\r\n }\r\n }\r\n }\r\n },\r\n\r\n});\r\n"]} \ No newline at end of file diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..97990fe --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/views/layouts/main.ejs b/views/layouts/main.ejs new file mode 100644 index 0000000..ab2c82a --- /dev/null +++ b/views/layouts/main.ejs @@ -0,0 +1,26 @@ + + + + + + + + Untitled Document + + + + + + + + + +
+ <%- await include(content, locals); %> +
+ + + + + + \ No newline at end of file