From c6a4d370cd54510c2318c475ddf3ec356bd020eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Sat, 3 May 2025 19:12:54 +0200 Subject: [PATCH 001/143] Clean old files --- .env.example | 20 - .gitignore | 19 - .npmrc | 1 - .prettierignore | 4 - .prettierrc | 10 - .vscode/settings.json | 3 - README.md | 48 - eslint.config.js | 36 - package.json | 66 - postcss.config.js | 6 - src/app.css | 1509 ------------ src/app.d.ts | 13 - src/app.html | 43 - src/components/Icon.svelte | 10 - src/components/admin/chart.svelte | 37 - src/components/admin/fileInput.svelte | 82 - src/components/admin/gallery.svelte | 9 - src/components/admin/galleryManage.svelte | 214 -- src/components/admin/message.svelte | 18 - src/components/admin/navigation.svelte | 128 - src/components/admin/project.svelte | 25 - src/components/admin/tagCreate.svelte | 114 - src/components/admin/tagSelect.svelte | 38 - src/components/button.svelte | 22 - src/components/clickOutside.svelte | 34 - src/components/galleryItem.svelte | 107 - src/components/group.svelte | 10 - src/components/input.svelte | 14 - src/components/label.svelte | 8 - src/components/link.svelte | 24 - src/components/navigation.svelte | 119 - src/components/pre.svelte | 8 - src/components/project.svelte | 25 - src/components/select.svelte | 20 - src/components/tag.svelte | 17 - src/components/textArea.svelte | 17 - src/components/timelinePart.svelte | 43 - src/lib/api.ts | 4 - src/lib/functions.ts | 145 -- src/lib/server/api.ts | 21 - src/lib/server/context.ts | 38 - src/lib/server/cookies/main.ts | 97 - src/lib/server/functions.ts | 30 - src/lib/server/routes/_app.ts | 26 - src/lib/server/routes/auth/index.ts | 7 - src/lib/server/routes/auth/login.ts | 53 - src/lib/server/routes/auth/logout.ts | 12 - src/lib/server/routes/details/index.ts | 12 - src/lib/server/routes/equipment/index.ts | 100 - src/lib/server/routes/equipment/type.ts | 14 - src/lib/server/routes/gallery/index.ts | 210 -- src/lib/server/routes/gallery/single.ts | 61 - src/lib/server/routes/project/get.ts | 269 --- src/lib/server/routes/project/index.ts | 7 - src/lib/server/routes/project/list.ts | 7 - src/lib/server/routes/projects.ts | 80 - src/lib/server/routes/stats/getToday.ts | 29 - src/lib/server/routes/stats/getWeek.ts | 29 - src/lib/server/routes/stats/index.ts | 9 - src/lib/server/routes/stats/weekGraph.ts | 67 - src/lib/server/routes/tag.ts | 121 - src/lib/server/routes/tags.ts | 21 - src/lib/server/routes/upload.ts | 88 - src/lib/server/server.ts | 9 - src/lib/server/variables.ts | 20 - src/routes/(adminLayout)/+layout.server.ts | 22 - src/routes/(adminLayout)/+layout.svelte | 49 - .../admin/equipment/+page.server.ts | 8 - .../admin/equipment/+page.svelte | 71 - .../admin/equipment/[id]/+page.server.ts | 9 - .../admin/equipment/[id]/+page.svelte | 145 -- .../admin/equipment/new/+page.server.ts | 8 - .../admin/equipment/new/+page.svelte | 119 - .../admin/gallery/+page.server.ts | 8 - .../(adminLayout)/admin/gallery/+page.svelte | 40 - .../admin/gallery/[id]/+page.server.ts | 9 - .../admin/gallery/[id]/+page.svelte | 27 - .../admin/gallery/new/+page.server.ts | 9 - .../admin/gallery/new/+page.svelte | 18 - .../(adminLayout)/admin/main/+page.server.ts | 16 - .../(adminLayout)/admin/main/+page.svelte | 89 - .../admin/projects/+page.server.ts | 8 - .../(adminLayout)/admin/projects/+page.svelte | 52 - .../admin/projects/[uuid]/+page.server.ts | 19 - .../admin/projects/[uuid]/+page.svelte | 358 --- .../admin/projects/new/+page.svelte | 254 -- .../(adminLayout)/admin/tags/+page.server.ts | 8 - .../(adminLayout)/admin/tags/+page.svelte | 97 - .../admin/tags/[id]/+page.server.ts | 19 - .../admin/tags/[id]/+page.svelte | 84 - src/routes/+error.svelte | 22 - src/routes/+layout.server.ts | 61 - src/routes/+layout.svelte | 21 - src/routes/+page.svelte | 48 - src/routes/about/+page.svelte | 113 - src/routes/admin/+page.server.ts | 15 - src/routes/admin/+page.svelte | 52 - src/routes/api/[...data]/+server.ts | 7 - src/routes/contact/+page.svelte | 62 - src/routes/customImages/[...name]/+server.ts | 114 - src/routes/gallery/+page.server.ts | 8 - src/routes/gallery/+page.svelte | 49 - src/routes/projects/+page.server.ts | 9 - src/routes/projects/+page.svelte | 75 - src/routes/projects/[uuid]/+page.server.ts | 18 - src/routes/projects/[uuid]/+page.svelte | 77 - src/routes/sitemap.xml/+server.ts | 22 - src/types/bootstrap_icons.ts | 2054 ----------------- src/types/database.ts | 95 - src/types/modules.d.ts | 4 - src/types/types.ts | 103 - static/apple-touch-icon-114x114.png | Bin 24749 -> 0 bytes static/apple-touch-icon-120x120.png | Bin 27036 -> 0 bytes static/apple-touch-icon-144x144.png | Bin 36908 -> 0 bytes static/apple-touch-icon-152x152.png | Bin 40489 -> 0 bytes static/apple-touch-icon-57x57.png | Bin 7388 -> 0 bytes static/apple-touch-icon-60x60.png | Bin 8103 -> 0 bytes static/apple-touch-icon-72x72.png | Bin 11116 -> 0 bytes static/apple-touch-icon-76x76.png | Bin 12260 -> 0 bytes static/favicon-128.png | Bin 30185 -> 0 bytes static/favicon-16x16.png | Bin 836 -> 0 bytes static/favicon-196x196.png | Bin 62692 -> 0 bytes static/favicon-32x32.png | Bin 2713 -> 0 bytes static/favicon-96x96.png | Bin 18321 -> 0 bytes static/favicon.ico | Bin 34494 -> 0 bytes static/images/code.png | Bin 105065 -> 0 bytes static/images/code_hr.png | Bin 286840 -> 0 bytes static/images/djs.png | Bin 51307 -> 0 bytes static/images/djs_hr.png | Bin 162996 -> 0 bytes static/images/example_project.png | Bin 1252 -> 0 bytes static/images/fei.png | Bin 27405 -> 0 bytes static/images/fei_hr.png | Bin 11110 -> 0 bytes static/images/icon.webp | Bin 47852 -> 0 bytes static/images/oldPage.png | Bin 425866 -> 0 bytes static/images/oldPage_hr.png | Bin 1322936 -> 0 bytes static/images/pfp.jpeg | Bin 171314 -> 0 bytes static/images/sveltekit.png | Bin 189684 -> 0 bytes static/images/sveltekit_hr.png | Bin 1794767 -> 0 bytes static/images/webBuilder.jpg | Bin 46831 -> 0 bytes static/images/webBuilder_hr.jpg | Bin 131356 -> 0 bytes static/mstile-144x144.png | Bin 36908 -> 0 bytes static/mstile-150x150.png | Bin 109932 -> 0 bytes static/mstile-310x150.png | Bin 210754 -> 0 bytes static/mstile-310x310.png | Bin 405207 -> 0 bytes static/mstile-70x70.png | Bin 30185 -> 0 bytes svelte.config.js | 24 - tailwind.config.js | 50 - tsconfig.json | 18 - vite.config.ts | 6 - 149 files changed, 9180 deletions(-) delete mode 100644 .env.example delete mode 100644 .gitignore delete mode 100644 .npmrc delete mode 100644 .prettierignore delete mode 100644 .prettierrc delete mode 100644 .vscode/settings.json delete mode 100644 README.md delete mode 100644 eslint.config.js delete mode 100644 package.json delete mode 100644 postcss.config.js delete mode 100644 src/app.css delete mode 100644 src/app.d.ts delete mode 100644 src/app.html delete mode 100644 src/components/Icon.svelte delete mode 100644 src/components/admin/chart.svelte delete mode 100644 src/components/admin/fileInput.svelte delete mode 100644 src/components/admin/gallery.svelte delete mode 100644 src/components/admin/galleryManage.svelte delete mode 100644 src/components/admin/message.svelte delete mode 100644 src/components/admin/navigation.svelte delete mode 100644 src/components/admin/project.svelte delete mode 100644 src/components/admin/tagCreate.svelte delete mode 100644 src/components/admin/tagSelect.svelte delete mode 100644 src/components/button.svelte delete mode 100644 src/components/clickOutside.svelte delete mode 100644 src/components/galleryItem.svelte delete mode 100644 src/components/group.svelte delete mode 100644 src/components/input.svelte delete mode 100644 src/components/label.svelte delete mode 100644 src/components/link.svelte delete mode 100644 src/components/navigation.svelte delete mode 100644 src/components/pre.svelte delete mode 100644 src/components/project.svelte delete mode 100644 src/components/select.svelte delete mode 100644 src/components/tag.svelte delete mode 100644 src/components/textArea.svelte delete mode 100644 src/components/timelinePart.svelte delete mode 100644 src/lib/api.ts delete mode 100644 src/lib/functions.ts delete mode 100644 src/lib/server/api.ts delete mode 100644 src/lib/server/context.ts delete mode 100644 src/lib/server/cookies/main.ts delete mode 100644 src/lib/server/functions.ts delete mode 100644 src/lib/server/routes/_app.ts delete mode 100644 src/lib/server/routes/auth/index.ts delete mode 100644 src/lib/server/routes/auth/login.ts delete mode 100644 src/lib/server/routes/auth/logout.ts delete mode 100644 src/lib/server/routes/details/index.ts delete mode 100644 src/lib/server/routes/equipment/index.ts delete mode 100644 src/lib/server/routes/equipment/type.ts delete mode 100644 src/lib/server/routes/gallery/index.ts delete mode 100644 src/lib/server/routes/gallery/single.ts delete mode 100644 src/lib/server/routes/project/get.ts delete mode 100644 src/lib/server/routes/project/index.ts delete mode 100644 src/lib/server/routes/project/list.ts delete mode 100644 src/lib/server/routes/projects.ts delete mode 100644 src/lib/server/routes/stats/getToday.ts delete mode 100644 src/lib/server/routes/stats/getWeek.ts delete mode 100644 src/lib/server/routes/stats/index.ts delete mode 100644 src/lib/server/routes/stats/weekGraph.ts delete mode 100644 src/lib/server/routes/tag.ts delete mode 100644 src/lib/server/routes/tags.ts delete mode 100644 src/lib/server/routes/upload.ts delete mode 100644 src/lib/server/server.ts delete mode 100644 src/lib/server/variables.ts delete mode 100644 src/routes/(adminLayout)/+layout.server.ts delete mode 100644 src/routes/(adminLayout)/+layout.svelte delete mode 100644 src/routes/(adminLayout)/admin/equipment/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/equipment/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/equipment/new/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/equipment/new/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/gallery/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/gallery/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/gallery/new/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/gallery/new/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/main/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/main/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/projects/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/projects/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/projects/new/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/tags/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/tags/+page.svelte delete mode 100644 src/routes/(adminLayout)/admin/tags/[id]/+page.server.ts delete mode 100644 src/routes/(adminLayout)/admin/tags/[id]/+page.svelte delete mode 100644 src/routes/+error.svelte delete mode 100644 src/routes/+layout.server.ts delete mode 100644 src/routes/+layout.svelte delete mode 100644 src/routes/+page.svelte delete mode 100644 src/routes/about/+page.svelte delete mode 100644 src/routes/admin/+page.server.ts delete mode 100644 src/routes/admin/+page.svelte delete mode 100644 src/routes/api/[...data]/+server.ts delete mode 100644 src/routes/contact/+page.svelte delete mode 100644 src/routes/customImages/[...name]/+server.ts delete mode 100644 src/routes/gallery/+page.server.ts delete mode 100644 src/routes/gallery/+page.svelte delete mode 100644 src/routes/projects/+page.server.ts delete mode 100644 src/routes/projects/+page.svelte delete mode 100644 src/routes/projects/[uuid]/+page.server.ts delete mode 100644 src/routes/projects/[uuid]/+page.svelte delete mode 100644 src/routes/sitemap.xml/+server.ts delete mode 100644 src/types/bootstrap_icons.ts delete mode 100644 src/types/database.ts delete mode 100644 src/types/modules.d.ts delete mode 100644 src/types/types.ts delete mode 100644 static/apple-touch-icon-114x114.png delete mode 100644 static/apple-touch-icon-120x120.png delete mode 100644 static/apple-touch-icon-144x144.png delete mode 100644 static/apple-touch-icon-152x152.png delete mode 100644 static/apple-touch-icon-57x57.png delete mode 100644 static/apple-touch-icon-60x60.png delete mode 100644 static/apple-touch-icon-72x72.png delete mode 100644 static/apple-touch-icon-76x76.png delete mode 100644 static/favicon-128.png delete mode 100644 static/favicon-16x16.png delete mode 100644 static/favicon-196x196.png delete mode 100644 static/favicon-32x32.png delete mode 100644 static/favicon-96x96.png delete mode 100644 static/favicon.ico delete mode 100644 static/images/code.png delete mode 100644 static/images/code_hr.png delete mode 100644 static/images/djs.png delete mode 100644 static/images/djs_hr.png delete mode 100644 static/images/example_project.png delete mode 100644 static/images/fei.png delete mode 100644 static/images/fei_hr.png delete mode 100644 static/images/icon.webp delete mode 100644 static/images/oldPage.png delete mode 100644 static/images/oldPage_hr.png delete mode 100644 static/images/pfp.jpeg delete mode 100644 static/images/sveltekit.png delete mode 100644 static/images/sveltekit_hr.png delete mode 100644 static/images/webBuilder.jpg delete mode 100644 static/images/webBuilder_hr.jpg delete mode 100644 static/mstile-144x144.png delete mode 100644 static/mstile-150x150.png delete mode 100644 static/mstile-310x150.png delete mode 100644 static/mstile-310x310.png delete mode 100644 static/mstile-70x70.png delete mode 100644 svelte.config.js delete mode 100644 tailwind.config.js delete mode 100644 tsconfig.json delete mode 100644 vite.config.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index 398bfb3..0000000 --- a/.env.example +++ /dev/null @@ -1,20 +0,0 @@ -#webserver config -HOST=0.0.0.0 -PORT=5178 -ORIGIN=http://localhost:5178 -PUBLIC_ORIGIN=https://myweb.cz -BODY_SIZE_LIMIT=16777216 # max upload size in bytes -#database config -DATABASE_IP=10.10.10.223 -DATABASE_PORT=3306 -DATABASE_USER=superclovek -DATABASE_PASSWORD=tajnyheslo123456 -DATABASE_NAME=website -DATABASE_URL=mysql://superclovek:tajnyheslo123456@10.10.10.223:3306/website -#secret pro JWT (tím se bude podepisovat JWT token - https://jwt.io/) -JWT_SECRET=text -#v sekundách (10 min = 10 * 60) -#expiruje pouze pokud uživatel danou dobu nic nedělá (neprochází stránky) -COOKIE_EXPIRE=1200 -#v sekundách (5 minut = 5 * 60) -PUBLIC_CHECK_COOKIE_INTERVAL=300 \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 2c3b871..0000000 --- a/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example -vite.config.js.timestamp-* -vite.config.ts.timestamp-* - -#custom folders -/projects -.cache - -#lock files -pnpm-lock.yaml -package-lock.json -yarn.lock \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index b6f27f1..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index cc41cea..0000000 --- a/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index f7c913d..0000000 --- a/.prettierrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "useTabs": false, - "tabWidth": 4, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 180, - "semi": true, - "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 72446f4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib" -} diff --git a/README.md b/README.md deleted file mode 100644 index 0d33873..0000000 --- a/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Info - -Source code of my main website [patrick115.eu](https://patrick115.eu) - -## Dev mode - -```bash -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open -``` - -## Build - -Building app: - -```bash -npm run build -``` - -Show builded preview: `npm run preview`. -Start builded app using `npm run start` with [Node Adapter](https://kit.svelte.dev/docs/adapter-node) or config command in package.json using your own [Adapter](https://kit.svelte.dev/docs/adapters) - -## Example ENV file (.env.example) - -```YAML -#webserver config -HOST=0.0.0.0 -PORT=5178 -ORIGIN=http://localhost:5178 -PUBLIC_ORIGIN=https://myweb.cz -BODY_SIZE_LIMIT=16777216 # max upload size in bytes -#database config -DATABASE_IP=10.10.10.223 -DATABASE_PORT=3306 -DATABASE_USER=superclovek -DATABASE_PASSWORD=tajnyheslo123456 -DATABASE_NAME=website -DATABASE_URL=mysql://superclovek:tajnyheslo123456@10.10.10.223:3306/website -#secret pro JWT (tím se bude podepisovat JWT token - https://jwt.io/) -JWT_SECRET=text -#v sekundách (10 min = 10 * 60) -#expiruje pouze pokud uživatel danou dobu nic nedělá (neprochází stránky) -COOKIE_EXPIRE=1200 -#v sekundách (5 minut = 5 * 60) -PUBLIC_CHECK_COOKIE_INTERVAL=300 -``` diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 28faeea..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,36 +0,0 @@ -import prettier from 'eslint-config-prettier'; -import js from '@eslint/js'; -import svelte from 'eslint-plugin-svelte'; -import globals from 'globals'; -import ts from 'typescript-eslint'; - -export default ts.config( - js.configs.recommended, - ...ts.configs.recommended, - ...svelte.configs['flat/recommended'], - prettier, - ...svelte.configs['flat/prettier'], - { - languageOptions: { - globals: { - ...globals.browser, - ...globals.node - } - } - }, - { - files: ['**/*.svelte'], - rules: { - 'svelte/no-at-html-tags': 'off' - }, - - languageOptions: { - parserOptions: { - parser: ts.parser - } - } - }, - { - ignores: ['build/', '.svelte-kit/', 'dist/'] - } -); diff --git a/package.json b/package.json deleted file mode 100644 index 3b5eb87..0000000 --- a/package.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "mainweb", - "version": "0.0.5", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --check . && eslint .", - "format": "prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --write .", - "genDatabaseSchema": "kysely-codegen --out-file ./src/types/database.ts", - "start": "node -r dotenv/config build" - }, - "devDependencies": { - "@eslint/js": "^9.17.0", - "@sveltejs/adapter-node": "^5.2.11", - "@sveltejs/kit": "^2.13.0", - "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@types/bcrypt": "^5.0.2", - "@types/eslint": "9.6.1", - "@types/jsonwebtoken": "^9.0.7", - "@types/path-browserify": "^1.0.3", - "@types/uuid": "^10.0.0", - "@typescript-eslint/eslint-plugin": "^8.18.1", - "@typescript-eslint/parser": "^8.18.1", - "autoprefixer": "^10.4.20", - "eslint": "^9.17.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.46.1", - "globals": "^15.14.0", - "kysely-codegen": "^0.17.0", - "postcss": "^8.4.49", - "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.2", - "prettier-plugin-tailwindcss": "^0.6.9", - "svelte": "^5.14.5", - "svelte-check": "^4.1.1", - "tailwindcss": "^3.4.17", - "tslib": "^2.8.1", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.1", - "vite": "^6.0.4" - }, - "type": "module", - "dependencies": { - "@patrick115/sveltekitapi": "^1.2.12", - "bcrypt": "^5.1.1", - "bootstrap-icons": "^1.11.3", - "chart.js": "^4.4.7", - "date-fns": "^4.1.0", - "dompurify": "^3.2.3", - "dotenv": "^16.4.7", - "jsonwebtoken": "^9.0.2", - "kysely": "^0.27.5", - "mysql2": "^3.11.5", - "path-browserify": "^1.0.1", - "sharp": "^0.33.5", - "simple-json-db": "^2.0.0", - "sweetalert2": "^11.15.2", - "tailwind-merge": "^2.5.5", - "uuid": "^11.0.3", - "zod": "^3.24.1" - } -} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index badd100..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } -}; diff --git a/src/app.css b/src/app.css deleted file mode 100644 index 2f25dd5..0000000 --- a/src/app.css +++ /dev/null @@ -1,1509 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* Google fonts */ -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjEYTLHdQ.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjNYTLHdQ.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjFYTLHdQ.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjKYTLHdQ.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjGYTLHdQ.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjHYTLHdQ.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjJYTI.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreSxf6TF0.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreQhf6TF0.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreShf6TF0.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreRRf6TF0.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreSRf6TF0.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreSBf6TF0.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreRhf6.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvmojLeTY.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvk4jLeTY.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvm4jLeTY.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvlIjLeTY.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvmIjLeTY.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvmYjLeTY.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvl4jL.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveSxf6TF0.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveQhf6TF0.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveShf6TF0.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveRRf6TF0.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveSRf6TF0.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveSBf6TF0.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveRhf6.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eSxf6TF0.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eQhf6TF0.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eShf6TF0.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eRRf6TF0.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eSRf6TF0.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eSBf6TF0.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eRhf6.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrLPTucXtAKPY.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrLPTucHtA.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLDz8Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecmNE.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLCz7Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxEIzIFKw.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxLIzIFKw.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxHIzIFKw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxIIzI.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7WxKOzY.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1Czjs2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvmyNL4U.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjsGyN.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcg72j00.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKew72j00.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcw72j00.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfA72j00.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcQ72j00.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfw72.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3js2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvmyNL4U.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jsGyN.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjs2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvmyNL4U.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjsGyN.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} diff --git a/src/app.d.ts b/src/app.d.ts deleted file mode 100644 index c7c0ed1..0000000 --- a/src/app.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces -declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } -} - -export {}; diff --git a/src/app.html b/src/app.html deleted file mode 100644 index 05ad376..0000000 --- a/src/app.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %sveltekit.head% - - - -
%sveltekit.body%
- - diff --git a/src/components/Icon.svelte b/src/components/Icon.svelte deleted file mode 100644 index d05c9fa..0000000 --- a/src/components/Icon.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - - {@render children?.()} - diff --git a/src/components/admin/chart.svelte b/src/components/admin/chart.svelte deleted file mode 100644 index 2d76f27..0000000 --- a/src/components/admin/chart.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/src/components/admin/fileInput.svelte b/src/components/admin/fileInput.svelte deleted file mode 100644 index 69912de..0000000 --- a/src/components/admin/fileInput.svelte +++ /dev/null @@ -1,82 +0,0 @@ - - -
{ - return; - }} - ondragover={handleDragOver} - ondragenter={handleEnter} - ondragleave={handleLeave} - onclick={() => input.click()} - role="button" - tabindex="0" - class={cls} -> - {@render children()} -
- diff --git a/src/components/admin/gallery.svelte b/src/components/admin/gallery.svelte deleted file mode 100644 index 90b7db1..0000000 --- a/src/components/admin/gallery.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -
- {#each images as image} - gallery item - {/each} -
diff --git a/src/components/admin/galleryManage.svelte b/src/components/admin/galleryManage.svelte deleted file mode 100644 index 7ae070c..0000000 --- a/src/components/admin/galleryManage.svelte +++ /dev/null @@ -1,214 +0,0 @@ - - -
-

- {#if data.id !== undefined} - Úprava {data.alt} - {:else} - Nový obrázek - {/if} -

- - - - - {#if !data.name} - - {:else} - preview of project - {/if} - - - - - - - - - - - - - - - - - - -
- {#each data.equipment as equipment} -
- {equipment.name} - removeEquipment(equipment.id)} class="cursor-pointer text-red-500" name="bi-trash-fill" /> -
- {/each} - -
-
- - - - - - - -
diff --git a/src/components/admin/message.svelte b/src/components/admin/message.svelte deleted file mode 100644 index cf99625..0000000 --- a/src/components/admin/message.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -

- {@render children()} -

diff --git a/src/components/admin/navigation.svelte b/src/components/admin/navigation.svelte deleted file mode 100644 index f16e3ce..0000000 --- a/src/components/admin/navigation.svelte +++ /dev/null @@ -1,128 +0,0 @@ - - - - -{#if $userState.logged} - (folded = true)}> - - -{/if} diff --git a/src/components/admin/project.svelte b/src/components/admin/project.svelte deleted file mode 100644 index 4c29505..0000000 --- a/src/components/admin/project.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - -
- {name} -
-

{name}

-

- {date.toLocaleDateString('cs-CZ')} -

-
-
-
diff --git a/src/components/admin/tagCreate.svelte b/src/components/admin/tagCreate.svelte deleted file mode 100644 index ec3306d..0000000 --- a/src/components/admin/tagCreate.svelte +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - {#if color === 'custom'} - - {/if} - - - - - {tagText ?? 'Nějaký text'} - - - - - - diff --git a/src/components/admin/tagSelect.svelte b/src/components/admin/tagSelect.svelte deleted file mode 100644 index 690ef66..0000000 --- a/src/components/admin/tagSelect.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - -{#if color.startsWith('#')} -
- {@render children()} - -
-{:else} -
- {@render children()} - -
-{/if} diff --git a/src/components/button.svelte b/src/components/button.svelte deleted file mode 100644 index 5949a74..0000000 --- a/src/components/button.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/src/components/clickOutside.svelte b/src/components/clickOutside.svelte deleted file mode 100644 index 1a9c66d..0000000 --- a/src/components/clickOutside.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -
{@render children()}
diff --git a/src/components/galleryItem.svelte b/src/components/galleryItem.svelte deleted file mode 100644 index 79f7379..0000000 --- a/src/components/galleryItem.svelte +++ /dev/null @@ -1,107 +0,0 @@ - - - - {data.alt} -
-

- {data.alt} - {#if admin} - - {/if} -

- -

Datum Pořízení:

-

{formatDate(data.date)}

-
- {#if Object.keys(transformedEquipment).length > 0} - -

Vybavení:

- {#each Object.entries(transformedEquipment) as [name, items]} - -

{name}:

-
    - {#each items as item} - - {item.name} - - {/each} -
-
- {/each} -
- {/if} -
-
diff --git a/src/components/group.svelte b/src/components/group.svelte deleted file mode 100644 index c9eace8..0000000 --- a/src/components/group.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
- {@render children()} -
diff --git a/src/components/input.svelte b/src/components/input.svelte deleted file mode 100644 index faa6816..0000000 --- a/src/components/input.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - (value = ev.currentTarget.value)} -/> diff --git a/src/components/label.svelte b/src/components/label.svelte deleted file mode 100644 index cb28d21..0000000 --- a/src/components/label.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/src/components/link.svelte b/src/components/link.svelte deleted file mode 100644 index 95ebf23..0000000 --- a/src/components/link.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -{#if button} - -{:else} - {@render children()} -{/if} diff --git a/src/components/navigation.svelte b/src/components/navigation.svelte deleted file mode 100644 index 198d97f..0000000 --- a/src/components/navigation.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - - - {currentNavItem?.name} | patrick115.eu - - - - - - - - - - diff --git a/src/components/pre.svelte b/src/components/pre.svelte deleted file mode 100644 index 720b3a2..0000000 --- a/src/components/pre.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -

{@render children()}

diff --git a/src/components/project.svelte b/src/components/project.svelte deleted file mode 100644 index bd60bb5..0000000 --- a/src/components/project.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - Project's Preview -
-

{project.name}

-

{project.date.toLocaleDateString()}

-
-
{@html createSimpleMarkDown(project.description)}
-
- {#each project.tags as tag} - {tag.name} - {/each} -
-
diff --git a/src/components/select.svelte b/src/components/select.svelte deleted file mode 100644 index e15bf24..0000000 --- a/src/components/select.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/src/components/tag.svelte b/src/components/tag.svelte deleted file mode 100644 index 94772ce..0000000 --- a/src/components/tag.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -{#if color.startsWith('#')} - -{:else} - -{/if} diff --git a/src/components/textArea.svelte b/src/components/textArea.svelte deleted file mode 100644 index 61c4cfc..0000000 --- a/src/components/textArea.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/src/components/timelinePart.svelte b/src/components/timelinePart.svelte deleted file mode 100644 index 7bf847b..0000000 --- a/src/components/timelinePart.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -
- {#if typeof src === 'string'} - - {:else} - - {#each Object.keys(src).filter((src) => src !== 'default') as selector} - - {/each} - - - {/if} -
-
- {#if label !== undefined} - {label} - {/if} -
-
- {@render children()} -
diff --git a/src/lib/api.ts b/src/lib/api.ts deleted file mode 100644 index e577a98..0000000 --- a/src/lib/api.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createAPIClient } from '@patrick115/sveltekitapi'; -import type { AppRouter } from './server/routes/_app'; - -export const API = createAPIClient('/api'); diff --git a/src/lib/functions.ts b/src/lib/functions.ts deleted file mode 100644 index 7382f85..0000000 --- a/src/lib/functions.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { browser } from '$app/environment'; -import { goto } from '$app/navigation'; -import DOMPurify from 'dompurify'; -import Swal, { type SweetAlertOptions } from 'sweetalert2'; -import { API } from './api'; -import { getContext } from 'svelte'; -import type { Writable } from 'svelte/store'; -import type { LoginData } from '$/types/types'; - -export const sleep = (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); -}; - -export const SwalAlert = async (data: SweetAlertOptions) => { - if (!browser) { - return { - isConfirmed: false - }; - } - - return Swal.fire({ - toast: true, - position: 'top-end', - timer: 2000, - timerProgressBar: true, - showCancelButton: false, - showConfirmButton: false, - ...data - }); -}; - -export const createSimpleMarkDown = (data: string | undefined) => { - if (!data || !browser) return ''; - let result = data.replaceAll(/\[(.*)\]\((.*)\)/g, '$1'); - result = result.replaceAll(/\*\*(.*)\*\*/g, '$1'); - - DOMPurify.addHook('afterSanitizeAttributes', function(node) { - // set all elements owning target to target=_blank - if ('target' in node) { - node.setAttribute('target', '_blank'); - node.setAttribute('rel', 'noopener'); - } - }); - - return DOMPurify.sanitize(result); -}; - -export const logout = async () => { - const result = await API.auth.logout(); - - if (result.status === false) { - SwalAlert({ - icon: 'error', - title: result.message - }); - return; - } - - getContext>('userState').set({ - logged: false - }); - - goto('/admin'); -}; - -export const getFiles = (files: (File | null)[]) => { - if (files.length == 0) { - SwalAlert({ - icon: 'error', - title: 'Nenahrál jsi žádný soubor' - }); - return []; - } - - const onlyFiles = files.filter((file) => file !== null) as File[]; - - if (onlyFiles.length == 0) { - SwalAlert({ - icon: 'error', - title: 'Nenahrál jsi žádný soubor' - }); - return []; - } - - return onlyFiles; -}; - -export const getDate = (date: string | Date) => { - const d = new Date(date); - - const day = d.getDate().toString().padStart(2, '0'); - const month = (d.getMonth() + 1).toString().padStart(2, '0'); - const year = d.getFullYear(); - - return `${year}-${month}-${day}`; -}; - -export const formatDate = (date: string | Date, withTime = true) => { - const d = new Date(date); - - const day = d.getDate().toString().padStart(2, '0'); - const month = (d.getMonth() + 1).toString().padStart(2, '0'); - const year = d.getFullYear(); - - if (withTime) { - const hours = d.getHours().toString().padStart(2, '0'); - const minutes = d.getMinutes().toString().padStart(2, '0'); - const seconds = d.getSeconds().toString().padStart(2, '0'); - - return `${hours}:${minutes}:${seconds} ${day}.${month}.${year}`; - } - return `${day}.${month}.${year}`; -}; - -export const uploadImage = async (file: File) => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const formData = new FormData(); - formData.append('file', file); - - const result = await API.upload.POST(formData); - - if (!result.status) { - return reject(result.message); - } - - return resolve(result.data); - }); -}; - -export const extractDate = (date: Date, withSeconds = false) => { - const day = date.getDate().toString().padStart(2, '0'); - const month = (date.getMonth() + 1).toString().padStart(2, '0'); - const year = date.getFullYear(); - - const hours = date.getHours().toString().padStart(2, '0'); - const minutes = date.getMinutes().toString().padStart(2, '0'); - - if (withSeconds) { - const seconds = date.getSeconds().toString().padStart(2, '0'); - - return [`${year}-${month}-${day}`, `${hours}:${minutes}:${seconds}`]; - } - return [`${year}-${month}-${day}`, `${hours}:${minutes}`]; -}; diff --git a/src/lib/server/api.ts b/src/lib/server/api.ts deleted file mode 100644 index 25c73fd..0000000 --- a/src/lib/server/api.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { APICreate, MiddleWareError } from '@patrick115/sveltekitapi'; -import type { Context } from './context'; - -export const t = new APICreate(); - -export const router = t.router; -export const procedure = t.procedure; -export const adminProcedure = procedure.use(async ({ next, ctx }) => { - if (!ctx.logged) { - throw new MiddleWareError({ - status: false, - code: 401, - message: 'You need to sign in.' - }); - } - - return next({ - logged: true, - data: ctx.data - }); -}); diff --git a/src/lib/server/context.ts b/src/lib/server/context.ts deleted file mode 100644 index 31f496f..0000000 --- a/src/lib/server/context.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { RegularUser } from '$/types/types'; -import type { AsyncReturnType, CreateContext } from '@patrick115/sveltekitapi'; -import { jwt } from './variables'; - -export const createContext = (async ({ - cookies -}): Promise< - | { - logged: false; - } - | { - logged: true; - data: RegularUser; - } -> => { - const cookie = cookies.get('session'); - - if (!cookie) { - return { - logged: false - }; - } - - const data = jwt.getCookie(cookie); - - if (!data) { - return { - logged: false - }; - } - - return { - logged: true, - data - }; -}) satisfies CreateContext; - -export type Context = AsyncReturnType; diff --git a/src/lib/server/cookies/main.ts b/src/lib/server/cookies/main.ts deleted file mode 100644 index ecc20b9..0000000 --- a/src/lib/server/cookies/main.ts +++ /dev/null @@ -1,97 +0,0 @@ -import jwt from 'jsonwebtoken'; -import path from 'path'; -import JSONdb from 'simple-json-db'; -import { v4 as uuid } from 'uuid'; - -/** - * @author patrick115 (Patrik Mintěl) - * @license MIT - * @version 1.2.0 - * @description Cookie lib - * @homepage https://patrick115.eu - */ - -interface cookie { - expires: number; - values: Type; -} - -export class SessionCookies { - private db: JSONdb>; - - constructor(storage = './cookies.json') { - this.db = new JSONdb>(storage.startsWith('./') || storage.startsWith('../') ? path.join(__dirname, storage) : storage, { - syncOnWrite: true, - jsonSpaces: false, - asyncWrite: false - }); - } - - checkCookies() { - const cookies = Object.entries(this.db.JSON()); - cookies.forEach(([key, value]) => { - if (value.expires < Date.now()) { - this.db.delete(key); - } - }); - } - - getCookie(key: string) { - this.checkCookies(); - return this.db.get(key) as cookie; - } - - updateCookie(id: string, value: T, age: number) { - this.checkCookies(); - - this.db.set(id, { - expires: Date.now() + age, - values: value - }); - - return id; - } - - setCookie(value: T, age: number) { - this.checkCookies(); - - let id = uuid(); - - while (this.db.has(id)) { - id = uuid(); - } - - this.db.set(id, { - expires: Date.now() + age, - values: value - }); - - return id; - } - - deleteCookie(key: string) { - this.checkCookies(); - this.db.delete(key); - } -} - -export class JWTCookies { - private key: string; - - constructor(key: string) { - this.key = key; - } - - setCookie(value: object | string | Buffer): string { - return jwt.sign(value, this.key); - } - - getCookie(token: string): T | null { - try { - return jwt.verify(token, this.key) as T; - } catch (e) { - console.error(e); - return null; - } - } -} diff --git a/src/lib/server/functions.ts b/src/lib/server/functions.ts deleted file mode 100644 index cf25128..0000000 --- a/src/lib/server/functions.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { json } from '@sveltejs/kit'; -import type { z } from 'zod'; - -export const checkData = async (request: Request, obj: z.ZodType): Promise> => { - let data; - - try { - data = await request.json(); - } catch (_) { - return json({ - status: false, - error: 'Invalid data' - }); - } - - const resp = obj.safeParse(data); - - if (resp.success) { - return resp.data; - } - - return json({ - status: false, - error: resp.error - }); -}; - -export const isOk = (data: Response | unknown): data is Response => { - return data instanceof Response; -}; diff --git a/src/lib/server/routes/_app.ts b/src/lib/server/routes/_app.ts deleted file mode 100644 index c226edb..0000000 --- a/src/lib/server/routes/_app.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { router } from '../api'; -import { auth } from './auth'; -import details from './details'; -import equipment from './equipment'; -import gallery from './gallery/index'; -import { project } from './project'; -import { projects } from './projects'; -import stats from './stats'; -import { tag } from './tag'; -import { tags } from './tags'; -import { upload } from './upload'; - -export const Router = router({ - auth, - project, - projects, - upload, - tag, - tags, - gallery, - equipment, - stats, - details -}); - -export type AppRouter = typeof Router; diff --git a/src/lib/server/routes/auth/index.ts b/src/lib/server/routes/auth/index.ts deleted file mode 100644 index 8269c1e..0000000 --- a/src/lib/server/routes/auth/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { login } from './login'; -import { logout } from './logout'; - -export const auth = { - login, - logout -}; diff --git a/src/lib/server/routes/auth/login.ts b/src/lib/server/routes/auth/login.ts deleted file mode 100644 index 9c8ba83..0000000 --- a/src/lib/server/routes/auth/login.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { RegularUser, ResponseWithData } from '$/types/types'; -import { COOKIE_EXPIRE } from '$env/static/private'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import bcrypt from 'bcrypt'; -import { z } from 'zod'; -import { procedure } from '../../api'; -import { conn, jwt } from '../../variables'; - -export const login = procedure.POST.input( - z.object({ - username: z.string(), - password: z.string() - }) -).query(async ({ ev: { cookies }, input }) => { - const userData = await conn.selectFrom('user').selectAll().where('username', '=', input.username).executeTakeFirst(); - - if (!userData) { - return { - status: false, - code: 400, - message: 'Zadal jsi neplatné uživatelské jméno' - } satisfies ErrorApiResponse; - } - - const { password: hashedPassword } = userData; - - const result = bcrypt.compareSync(input.password, hashedPassword); - - if (!result) { - return { - status: false, - code: 400, - message: 'Zadal jsi neplatné heslo' - } satisfies ErrorApiResponse; - } - - const data = { - id: userData.id, - username: userData.username - }; - - const cookie = jwt.setCookie(data); - - cookies.set('session', cookie, { - path: '/', - maxAge: parseInt(COOKIE_EXPIRE) * 100 - }); - - return { - status: true, - data - } satisfies ResponseWithData; -}); diff --git a/src/lib/server/routes/auth/logout.ts b/src/lib/server/routes/auth/logout.ts deleted file mode 100644 index fc9e8f4..0000000 --- a/src/lib/server/routes/auth/logout.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Response } from '$/types/types'; -import { adminProcedure } from '../../api'; - -export const logout = adminProcedure.GET.query(async ({ ev: { cookies } }) => { - cookies.delete('session', { - path: '/' - }); - - return { - status: true - } satisfies Response; -}); diff --git a/src/lib/server/routes/details/index.ts b/src/lib/server/routes/details/index.ts deleted file mode 100644 index 22bfef6..0000000 --- a/src/lib/server/routes/details/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { DetailTypes, ResponseWithData } from '$/types/types'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const GET = adminProcedure.GET.query(async () => { - return { - status: true, - data: await conn.selectFrom('detail_types').selectAll().execute() - } satisfies ResponseWithData; -}); - -export default [GET]; diff --git a/src/lib/server/routes/equipment/index.ts b/src/lib/server/routes/equipment/index.ts deleted file mode 100644 index f31a994..0000000 --- a/src/lib/server/routes/equipment/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { Equipment, EquipmentInfo, Response, ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { adminProcedure, procedure } from '../../api'; -import { conn } from '../../variables'; -import t from './type'; - -const add = adminProcedure.PUT.input( - z.object({ - name: z.string(), - link: z.string().url(), - type: z.number() - }) -).query(async ({ input }) => { - try { - await conn.insertInto('equipment').values(input).execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -const get = procedure.GET.query(async () => { - const data = await conn - .selectFrom('equipment') - .innerJoin('equipment_type', 'equipment.type', 'equipment_type.id') - .select(['equipment.name', 'equipment_type.name as type', 'equipment.link', 'equipment.id', 'equipment_type.id as type_id']) - .execute(); - - return { - status: true, - data - } satisfies ResponseWithData; -}); - -const remove = adminProcedure.DELETE.input(z.number()).query(async ({ input }) => { - await conn.deleteFrom('equipment').where('id', '=', input).execute(); - - return { - status: true - } satisfies Response; -}); - -const single = adminProcedure.POST.input(z.number()).query(async ({ input }) => { - const item = await conn.selectFrom('equipment').selectAll().executeTakeFirst(); - - if (!item) { - return { - status: false, - code: 404, - message: 'Not found' - } satisfies ErrorApiResponse; - } - - return { - status: true, - data: item - } satisfies ResponseWithData; -}); - -const update = adminProcedure.PATCH.input( - z.object({ - id: z.number().min(1), - name: z.string(), - link: z.string().url(), - type: z.number() - }) -).query(async ({ input }) => { - const result = await conn.updateTable('equipment').set(input).where('id', '=', input.id).executeTakeFirst(); - - if (!result) { - return { - status: false, - code: 404, - message: 'Not found' - } satisfies ErrorApiResponse; - } - - return { - status: true - } satisfies Response; -}); - -export default [ - get, - add, - remove, - single, - update, - { - type: t - } -]; diff --git a/src/lib/server/routes/equipment/type.ts b/src/lib/server/routes/equipment/type.ts deleted file mode 100644 index 1e82964..0000000 --- a/src/lib/server/routes/equipment/type.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { EquipmentType, ResponseWithData } from '$/types/types'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.GET.query(async () => { - const types = await conn.selectFrom('equipment_type').selectAll().execute(); - - return { - status: true, - data: types - } satisfies ResponseWithData; -}); - -export default endpoint; diff --git a/src/lib/server/routes/gallery/index.ts b/src/lib/server/routes/gallery/index.ts deleted file mode 100644 index b077313..0000000 --- a/src/lib/server/routes/gallery/index.ts +++ /dev/null @@ -1,210 +0,0 @@ -import type { GalleryItem, Response, ResponseWithData } from '$/types/types'; -import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; -import crypto from 'node:crypto'; -import fs from 'node:fs'; -import Path from 'node:path'; -import { z } from 'zod'; -import { adminProcedure, procedure } from '../../api'; -import { conn } from '../../variables'; -import single from './single'; - -const extensions = ['.png', '.jpg', '.jpeg', '.gif', '.avif', '.tiff', '.webp', '.webm'] as const; - -const upload = adminProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const file = input.get('file') as File | null; - if (!file) { - return { - status: false, - code: 400, - message: 'No file provided' - } satisfies ErrorApiResponse; - } - - const root = Path.join('projects', 'gallery'); - - if (!fs.existsSync(root)) { - fs.mkdirSync(root, { recursive: true }); - } - - let path = Path.join(root, file.name); - const fileName = Path.parse(file.name); - - while (fs.existsSync(path)) { - path = Path.join(root, fileName.name + crypto.randomBytes(4).toString('hex') + fileName.ext); - } - - if (!extensions.includes(fileName.ext as (typeof extensions)[number])) { - return { - status: false, - code: 400, - message: 'Nahrál jsi neplatný typ souboru' - } satisfies ErrorApiResponse; - } - - const buffer = await file.arrayBuffer(); - - fs.writeFileSync(path, Buffer.from(buffer)); - - return { - status: true, - data: file.name - } satisfies ResponseWithData; -}); - -const list = procedure.GET.query(async () => { - try { - const galleryItems = await conn.selectFrom('gallery').selectAll().orderBy('date', 'desc').execute(); - - const joinTable = await conn.selectFrom('gallery_equipment').selectAll().execute(); - - const equipments = await conn - .selectFrom('equipment') - .innerJoin('equipment_type', 'equipment.type', 'equipment_type.id') - .select(['equipment.id', 'equipment.link', 'equipment.name', 'equipment_type.name as type', 'equipment.id as type_id']) - .execute(); - - const data: GalleryItem[] = []; - - for (const item of galleryItems) { - const equipmentIds = joinTable.filter((join) => join.gallery_id === item.id).map((item) => item.equipment_id); - - data.push({ - ...item, - equipment: equipments.filter((equipment) => equipmentIds.includes(equipment.id)) - }); - } - - return { - status: true, - data - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -const add = procedure.PUT.input( - z.object({ - id: z.number().optional(), - alt: z.string(), - date: z.coerce.date(), - name: z.string(), - equipment: z.array( - z.object({ - id: z.number(), - name: z.string(), - link: z.string(), - type: z.string(), - type_id: z.number() - }) - ) - }) -).query(async ({ input }) => { - try { - const result = await conn - .insertInto('gallery') - .values({ - name: input.name, - date: input.date, - alt: input.alt - }) - .executeTakeFirst(); - - //enter equipment - if (!result.insertId) { - throw new Error('Unable to get id'); - } - - await conn - .insertInto('gallery_equipment') - .values( - input.equipment.map((item) => { - return { - gallery_id: Number(result.insertId), - equipment_id: item.id - }; - }) - ) - .execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -const remove = adminProcedure.DELETE.input(z.number()).query(async ({ input }) => { - await conn.deleteFrom('gallery').where('id', '=', input).execute(); - - return { - status: true - } satisfies Response; -}); - -const edit = adminProcedure.PATCH.input( - z.object({ - id: z.number(), - alt: z.string(), - date: z.coerce.date(), - name: z.string(), - equipment: z.array( - z.object({ - id: z.number(), - name: z.string(), - link: z.string(), - type: z.string(), - type_id: z.number() - }) - ) - }) -).query(async ({ input }) => { - try { - await conn - .updateTable('gallery') - .set({ - name: input.name, - date: input.date, - alt: input.alt - }) - .where('id', '=', input.id) - .execute(); - - //delete old equipment - await conn.deleteFrom('gallery_equipment').where('gallery_id', '=', input.id).execute(); - - //add current equipment - await conn - .insertInto('gallery_equipment') - .values( - input.equipment.map((item) => { - return { - gallery_id: input.id, - equipment_id: item.id - }; - }) - ) - .execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -export default [upload, list, add, remove, edit, { single }]; diff --git a/src/lib/server/routes/gallery/single.ts b/src/lib/server/routes/gallery/single.ts deleted file mode 100644 index 0ba6765..0000000 --- a/src/lib/server/routes/gallery/single.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { GalleryItem, NoUndefinedField, ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.POST.input(z.number()).query(async ({ input }) => { - const galleryItem = await conn - .selectFrom('gallery') - .leftJoin('gallery_equipment', 'gallery.id', 'gallery_equipment.gallery_id') - .leftJoin('equipment', 'gallery_equipment.equipment_id', 'equipment.id') - .leftJoin('equipment_type', 'equipment.type', 'equipment_type.id') - .select([ - 'gallery.id', - 'gallery.name', - 'gallery.date', - 'gallery.alt', - 'equipment.id as equipment_id', - 'equipment.name as equipment_name', - 'equipment.link', - 'equipment_type.id as type_id', - 'equipment_type.name as type_name' - ]) - .where('gallery.id', '=', input) - .execute(); - - if (galleryItem.length === 0) { - return { - status: false, - code: 404, - message: 'Not found' - } satisfies ErrorApiResponse; - } - - const first = galleryItem[0]; - - return { - status: true, - data: { - id: first.id, - alt: first.alt, - name: first.name, - date: first.date, - equipment: ( - galleryItem.filter((item) => { - return item.equipment_id && item.equipment_name && item.link && item.type_id && item.type_name; - }) as NoUndefinedField<(typeof galleryItem)[number]>[] - ).map((item) => { - return { - id: item.equipment_id, - name: item.equipment_name, - link: item.link, - type: item.type_name, - type_id: item.type_id - }; - }) - } - } satisfies ResponseWithData; -}); - -export default endpoint; diff --git a/src/lib/server/routes/project/get.ts b/src/lib/server/routes/project/get.ts deleted file mode 100644 index ff276d1..0000000 --- a/src/lib/server/routes/project/get.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { projectDataSchema, type FullProjectData, type Response, type ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import fs from 'node:fs'; -import path from 'node:path'; -import { v4 } from 'uuid'; -import { z } from 'zod'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const schema = z.object({ - uuid: z.string() -}); - -const create = adminProcedure.PUT.input(projectDataSchema).query(async ({ input }) => { - const uuid = v4(); - - const imageData = path.parse(input.filePath); - - const result = await conn - .insertInto('project') - .values({ - uuid, - name: input.name, - description: input.description, - date: new Date(input.date), - preview: imageData.base, - image_count: input.images.length - }) - .executeTakeFirst(); - - if (result.numInsertedOrUpdatedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepodařilo se vložit projekt' - } satisfies ErrorApiResponse; - } - - const root = 'projects'; - - const images = [input.filePath, ...input.images]; - - fs.mkdirSync(path.join(root, uuid)); - - for (const imageId in images) { - const image = images[imageId]; - - const imageData = path.parse(image); - - fs.copyFileSync(path.join(root, imageData.base), path.join(root, uuid, imageData.base)); - fs.unlinkSync(path.join(root, imageData.base)); - - if (parseInt(imageId) == 0) continue; - - //insert - const result = await conn - .insertInto('project_image') - .values({ - project: uuid, - name: imageData.base - }) - .executeTakeFirst(); - - if (result.numInsertedOrUpdatedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepodařilo se vložit obrázek' - } satisfies ErrorApiResponse; - } - } - - return { - status: true, - data: uuid - } satisfies ResponseWithData; -}); - -const getData = adminProcedure.POST.input(schema).query(async ({ input: { uuid } }) => { - const mainData = await conn.selectFrom('project').selectAll().where('uuid', '=', uuid).executeTakeFirst(); - - if (!mainData) { - return { - status: false, - code: 404, - message: 'Project not found' - } satisfies ErrorApiResponse; - } - try { - const tags = await conn.selectFrom('project_tags').selectAll().where('uuid', '=', uuid).execute(); - - const images = await conn.selectFrom('project_image').selectAll().where('project', '=', uuid).execute(); - - return { - status: true, - data: { - ...mainData, - tags: tags.map((tag) => { - return { - ...tag, - id: undefined - }; - }), - images: images.map((image) => { - return { - ...image, - id: undefined - }; - }) - } - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se získat data o projektu' - } satisfies ErrorApiResponse; - } -}); - -const deleteData = adminProcedure.DELETE.input(schema).query(async ({ input: { uuid } }) => { - const result = await conn.deleteFrom('project').where('uuid', '=', uuid).executeTakeFirst(); - - if (result.numDeletedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepodařilo se smazat projekt' - } satisfies ErrorApiResponse; - } - - fs.rmSync(path.join('projects', uuid), { - recursive: true - }); - - return { - status: true - } satisfies Response; -}); - -const updateSchema = projectDataSchema.extend({ - uuid: z.string(), - previewChanged: z.boolean(), - tags: z.array( - z.object({ - uuid: z.string(), - tag: z.number() - }) - ) -}); - -const updateData = adminProcedure.PATCH.input(updateSchema).query(async ({ input }) => { - const originalData = await conn.selectFrom('project').selectAll().where('uuid', '=', input.uuid).executeTakeFirst(); - - if (!originalData) { - return { - status: false, - code: 404, - message: 'Projekt nebyl nalezen' - } satisfies ErrorApiResponse; - } - - const preview = path.parse(input.filePath); - if (preview.base != originalData.preview) { - fs.copyFileSync(path.join('projects', preview.base), path.join('projects', input.uuid, preview.base)); - fs.unlinkSync(path.join('projects', input.uuid, originalData.preview)); - } - - await conn - .updateTable('project') - .set({ - name: input.name, - description: input.description, - date: new Date(input.date), - preview: preview.base - }) - .where('uuid', '=', input.uuid) - .executeTakeFirst(); - - const images = await conn.selectFrom('project_image').selectAll().where('project', '=', input.uuid).execute(); - try { - //delete old images - for (const image of images) { - if (!input.images.includes(image.name)) { - fs.unlinkSync(path.join('projects', input.uuid, image.name)); - await conn.deleteFrom('project_image').where('id', '=', image.id).executeTakeFirstOrThrow(); - } - } - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se smazat staré obrázky' - } satisfies ErrorApiResponse; - } - - //insert new images - const newImages = input.images.filter((image) => { - const p = path.parse(image); - - if (p.dir !== '') { - return true; - } - - if (p.root !== '') { - return true; - } - return false; - }); - - try { - for (const image of newImages) { - const p = path.parse(image); - - fs.copyFileSync(path.join('projects', p.base), path.join('projects', input.uuid, p.base)); - fs.unlinkSync(path.join('projects', p.base)); - - await conn - .insertInto('project_image') - .values({ - project: input.uuid, - name: p.base - }) - .executeTakeFirstOrThrow(); - } - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se vložit nové obrázky' - } satisfies ErrorApiResponse; - } - - try { - //sync tags - const currentTags = await conn.selectFrom('project_tags').selectAll().where('uuid', '=', input.uuid).execute(); - - const ids = input.tags.map((tag) => tag.tag); - - //remove unused tags - for (const tag of currentTags.filter((tag) => !ids.includes(tag.tag))) { - await conn.deleteFrom('project_tags').where('tag', '=', tag.tag).executeTakeFirstOrThrow(); - } - - const current = currentTags.map((tag) => tag.tag); - - //add new tags - for (const tag of input.tags.filter((tag) => !current.includes(tag.tag))) { - await conn - .insertInto('project_tags') - .values({ - uuid: input.uuid, - tag: tag.tag - }) - .executeTakeFirstOrThrow(); - } - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se aktualizovat tagy' - } satisfies ErrorApiResponse; - } - return { - status: true - } satisfies Response; -}); - -export const get = [create, getData, deleteData, updateData]; diff --git a/src/lib/server/routes/project/index.ts b/src/lib/server/routes/project/index.ts deleted file mode 100644 index d567b83..0000000 --- a/src/lib/server/routes/project/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { get } from './get'; -import { list } from './list'; - -export const project = { - list, - get -}; diff --git a/src/lib/server/routes/project/list.ts b/src/lib/server/routes/project/list.ts deleted file mode 100644 index f9b1b2a..0000000 --- a/src/lib/server/routes/project/list.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -export const list = adminProcedure.GET.query(async () => { - const data = await conn.selectFrom('project').selectAll().execute(); - return data; -}); diff --git a/src/lib/server/routes/projects.ts b/src/lib/server/routes/projects.ts deleted file mode 100644 index 8fecce0..0000000 --- a/src/lib/server/routes/projects.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { PublicProjectData, ResponseWithData, Tag } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { procedure } from '../api'; -import { conn } from '../variables'; - -const list = procedure.GET.query(async () => { - try { - const data = await conn.selectFrom('project').selectAll().execute(); - const tags = await conn.selectFrom('tag').selectAll().execute(); - - const returnData: PublicProjectData[] = []; - - for (const project of data) { - const result = await conn.selectFrom('project_tags').select('tag').where('uuid', '=', project.uuid).execute(); - const projectTags = result.map((tag) => tag.tag); - const images = await conn.selectFrom('project_image').select('name').where('project', '=', project.uuid).execute(); - - returnData.push({ - ...project, - tags: tags.filter((tag) => projectTags.includes(tag.id)), - images: images.map((image) => image.name) - }); - } - - return { - status: true, - data: returnData - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepovedlo se načíst projekty' - } satisfies ErrorApiResponse; - } -}); - -const getOne = procedure.POST.input( - z.object({ - uuid: z.string() - }) -).query(async ({ input: { uuid } }) => { - try { - const project = await conn.selectFrom('project').selectAll().where('uuid', '=', uuid).executeTakeFirst(); - - if (!project) { - return { - status: false, - code: 404, - message: 'Projekt nebyl nalezen' - } satisfies ErrorApiResponse; - } - - const images = await conn.selectFrom('project_image').where('project', '=', uuid).select('name').execute(); - const tags = (await conn - .selectFrom('project_tags') - .innerJoin('tag', 'project_tags.tag', 'tag.id') - .select(['id', 'name', 'color']) - .where('uuid', '=', uuid) - .execute()) as Tag[]; - - return { - status: true, - data: { - ...project, - images: images.map((image) => image.name), - tags - } - } satisfies ResponseWithData; - } catch (e) { - return { - status: false, - code: 500, - message: 'Nepovedlo se načíst projekt' - } satisfies ErrorApiResponse; - } -}); - -export const projects = [list, getOne]; diff --git a/src/lib/server/routes/stats/getToday.ts b/src/lib/server/routes/stats/getToday.ts deleted file mode 100644 index b41ff3d..0000000 --- a/src/lib/server/routes/stats/getToday.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { sql } from 'kysely'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.GET.query(async () => { - try { - const data = await conn - .selectFrom('visitors') - .select('id') - .where(sql`DATE(date)`, '=', sql`CURDATE()`) - .groupBy('ip') - .execute(); - - return { - status: true, - data: data.length - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } -}); - -export default endpoint; diff --git a/src/lib/server/routes/stats/getWeek.ts b/src/lib/server/routes/stats/getWeek.ts deleted file mode 100644 index 592d7e9..0000000 --- a/src/lib/server/routes/stats/getWeek.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { sql } from 'kysely'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.GET.query(async () => { - try { - const data = await conn - .selectFrom('visitors') - .select('id') - .where(sql`WEEK(CURDATE(), 1)`, '=', sql`WEEK(date, 1)`) - .groupBy('ip') - .execute(); - - return { - status: true, - data: data.length - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } -}); - -export default endpoint; diff --git a/src/lib/server/routes/stats/index.ts b/src/lib/server/routes/stats/index.ts deleted file mode 100644 index 3fd1596..0000000 --- a/src/lib/server/routes/stats/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import getToday from './getToday'; -import getWeek from './getWeek'; -import weekGraph from './weekGraph'; - -export default { - getToday, - getWeek, - weekGraph -}; diff --git a/src/lib/server/routes/stats/weekGraph.ts b/src/lib/server/routes/stats/weekGraph.ts deleted file mode 100644 index 68e8b82..0000000 --- a/src/lib/server/routes/stats/weekGraph.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { ResponseWithData } from '$/types/types'; -import type { AsyncReturnType, ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { sql } from 'kysely'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; -import { format } from 'date-fns'; -import { z } from 'zod'; - -const getDataForYear = async (year?: number) => { - try { - //SELECT WEEK, COUNT(WEEK) AS COUNT FROM ( - //SELECT ip, WEEK(date, 1) as WEEK FROM visitors GROUP BY WEEK(date, 1), ip - //) AS t GROUP BY WEEK - - let query = conn - .selectFrom((eb) => - eb - .selectFrom('visitors') - .select(['ip', sql`WEEK(date, 1)`.as('WEEK'), sql`YEAR(date)`.as('YEAR')]) - .groupBy([sql`WEEK(date, 1)`, 'ip']) - .as('t') - ) - .select(['YEAR', 'WEEK', sql`COUNT(WEEK)`.as('COUNT')]); - - if (year) { - query = query.where('YEAR', '=', year); - } - - return await query.groupBy(['YEAR', 'WEEK']).execute(); - } catch (_) { - return false; - } -}; - -const GET = adminProcedure.GET.query(async () => { - const result = await getDataForYear(); - - if (!result) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } - return { - status: true, - data: result - } satisfies ResponseWithData>; -}); - -const POST = adminProcedure.POST.input(z.number()).query(async ({ input }) => { - const result = await getDataForYear(input); - - if (!result) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } - return { - status: true, - data: result - } satisfies ResponseWithData>; -}); - -export default [GET, POST]; diff --git a/src/lib/server/routes/tag.ts b/src/lib/server/routes/tag.ts deleted file mode 100644 index ac7c760..0000000 --- a/src/lib/server/routes/tag.ts +++ /dev/null @@ -1,121 +0,0 @@ -import type { Response, ResponseWithData, Tag } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { adminProcedure } from '../api'; -import { conn } from '../variables'; - -export const list = adminProcedure.GET.query(async () => { - try { - const result = await conn.selectFrom('tag').selectAll().execute(); - - return { - status: true, - data: result - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se načíst tagy' - } satisfies ErrorApiResponse; - } -}); - -const create = adminProcedure.PUT.input( - z.object({ - text: z.string(), - color: z.string() - }) -).query(async ({ input }) => { - const result = await conn - .insertInto('tag') - .values({ - name: input.text, - color: input.color - }) - .executeTakeFirst(); - - if (result.numInsertedOrUpdatedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepovedlo se vytvořit tag' - } satisfies ErrorApiResponse; - } - - return { - status: true - } satisfies Response; -}); - -const load = adminProcedure.POST.input( - z.object({ - id: z.number() - }) -).query(async ({ input: { id } }) => { - const result = await conn.selectFrom('tag').selectAll().where('id', '=', id).executeTakeFirst(); - - if (!result) { - return { - status: false, - code: 404, - message: 'Tag nebyl nalezen' - } satisfies ErrorApiResponse; - } - - return { - status: true, - data: result - } satisfies ResponseWithData; -}); - -const update = adminProcedure.PATCH.input( - z.object({ - id: z.number(), - text: z.string(), - color: z.string() - }) -).query(async ({ input }) => { - try { - await conn - .updateTable('tag') - .set({ - name: input.text, - color: input.color - }) - .where('id', '=', input.id) - .execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepovedlo se upravit tag' - } satisfies ErrorApiResponse; - } -}); - -const remove = adminProcedure.DELETE.input( - z.object({ - id: z.number() - }) -).query(async ({ input: { id } }) => { - try { - await conn.deleteFrom('tag').where('id', '=', id).execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepovedlo se smazat tag' - } satisfies ErrorApiResponse; - } -}); - -export const tag = [list, create, load, update, remove]; diff --git a/src/lib/server/routes/tags.ts b/src/lib/server/routes/tags.ts deleted file mode 100644 index f158ed7..0000000 --- a/src/lib/server/routes/tags.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ResponseWithData, Tag } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { procedure } from '../api'; -import { conn } from '../variables'; - -export const tags = procedure.GET.query(async () => { - try { - const tags = await conn.selectFrom('tag').selectAll().execute(); - - return { - status: true, - data: tags - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se načíst tagy' - } satisfies ErrorApiResponse; - } -}); diff --git a/src/lib/server/routes/upload.ts b/src/lib/server/routes/upload.ts deleted file mode 100644 index d1058e5..0000000 --- a/src/lib/server/routes/upload.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Response, ResponseWithData } from '$/types/types'; -import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; -import crypto from 'node:crypto'; -import fs from 'node:fs'; -import path from 'node:path'; -import { z } from 'zod'; -import { adminProcedure } from '../api'; - -const extensions = ['.png', '.jpg', '.jpeg', '.gif', '.avif', '.tiff', '.webp', '.webm'] as const; - -const create = adminProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const file = input.get('file') as File | null; - - if (!file) { - return { - status: false, - code: 400, - message: 'Nenahrál jsi žádný soubor' - } satisfies ErrorApiResponse; - } - - const fileName = path.parse(file.name); - - if (!extensions.includes(fileName.ext as (typeof extensions)[number])) { - return { - status: false, - code: 400, - message: 'Nahrál jsi neplatný typ souboru' - } satisfies ErrorApiResponse; - } - - const buffer = await file.arrayBuffer(); - - if (!fs.existsSync('projects')) { - fs.mkdirSync('projects'); - } - - let filePath = path.join('projects', file.name); - let finalFileName = file.name; - - if (fs.existsSync(filePath)) { - const fileData = path.parse(file.name); - const bytes = crypto.randomBytes(4).toString('hex'); - - finalFileName = `${fileData.name}.${bytes}${fileData.ext}`; - - filePath = path.join('projects', finalFileName); - } - - fs.writeFileSync(filePath, Buffer.from(buffer)); - - return { - status: true, - data: path.join('/customImages', finalFileName) - } satisfies ResponseWithData; -}); - -const remove = adminProcedure.DELETE.input( - z.object({ - name: z.string() - }) -).query(async ({ input: { name } }) => { - const filePath = path.join('projects', name); - - if (!fs.existsSync(filePath)) { - return { - status: false, - code: 404, - message: 'Soubor neexistuje' - } satisfies ErrorApiResponse; - } - - try { - fs.unlinkSync(filePath); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se smazat soubor' - } satisfies ErrorApiResponse; - } -}); - -export const upload = [create, remove]; diff --git a/src/lib/server/server.ts b/src/lib/server/server.ts deleted file mode 100644 index 7ee5cd2..0000000 --- a/src/lib/server/server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { APIServer } from '@patrick115/sveltekitapi'; -import { createContext } from './context'; -import { Router } from './routes/_app'; - -export const Server = new APIServer({ - router: Router, - context: createContext, - path: '/api' -}); diff --git a/src/lib/server/variables.ts b/src/lib/server/variables.ts deleted file mode 100644 index 6d348cb..0000000 --- a/src/lib/server/variables.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { DB } from '$/types/database'; -import { DATABASE_IP, DATABASE_NAME, DATABASE_PASSWORD, DATABASE_PORT, DATABASE_USER, JWT_SECRET } from '$env/static/private'; -import { Kysely, MysqlDialect } from 'kysely'; -import { createPool } from 'mysql2'; -import { JWTCookies } from './cookies/main'; -export const jwt = new JWTCookies(JWT_SECRET); - -const dialect = new MysqlDialect({ - pool: createPool({ - host: DATABASE_IP, - port: parseInt(DATABASE_PORT), - user: DATABASE_USER, - password: DATABASE_PASSWORD, - database: DATABASE_NAME - }) -}); - -export const conn = new Kysely({ - dialect -}); diff --git a/src/routes/(adminLayout)/+layout.server.ts b/src/routes/(adminLayout)/+layout.server.ts deleted file mode 100644 index 16521c8..0000000 --- a/src/routes/(adminLayout)/+layout.server.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { jwt } from '$/lib/server/variables'; -import { npm_package_version } from '$env/static/private'; -import { redirect } from '@sveltejs/kit'; -import type { LayoutServerLoad } from './$types'; - -export const load = (async ({ cookies }) => { - const session = cookies.get('session'); - - if (!session) { - throw redirect(302, '/admin'); - } - - const data = jwt.getCookie(session); - - if (!data) { - throw redirect(302, '/admin'); - } - - return { - version: npm_package_version - }; -}) satisfies LayoutServerLoad; diff --git a/src/routes/(adminLayout)/+layout.svelte b/src/routes/(adminLayout)/+layout.svelte deleted file mode 100644 index 00cb8bd..0000000 --- a/src/routes/(adminLayout)/+layout.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -{#if $userState.logged} -
-
- {#if folded} - - {:else} - - {/if} -

{navItem?.fullName ?? ''}

- -
-
- - - {@render children()} -
-
-{/if} diff --git a/src/routes/(adminLayout)/admin/equipment/+page.server.ts b/src/routes/(adminLayout)/admin/equipment/+page.server.ts deleted file mode 100644 index 23790e6..0000000 --- a/src/routes/(adminLayout)/admin/equipment/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - equipment: await Server.ssr.equipment.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/equipment/+page.svelte b/src/routes/(adminLayout)/admin/equipment/+page.svelte deleted file mode 100644 index a0ecdbb..0000000 --- a/src/routes/(adminLayout)/admin/equipment/+page.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - -
- {#each equipments as equipment} -
- {equipment.name} - deleteEquipment(equipment.id)} class="cursor-pointer text-red-500" name="bi-trash-fill" /> -
- {/each} - -
diff --git a/src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts b/src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts deleted file mode 100644 index 2b267cc..0000000 --- a/src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - types: await Server.ssr.equipment.type(ev), - equipment: await Server.ssr.equipment.POST(ev, parseInt(ev.params.id)) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte b/src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte deleted file mode 100644 index 704ebf1..0000000 --- a/src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte +++ /dev/null @@ -1,145 +0,0 @@ - - -
- - - - - - - - - - - - - - - - -
diff --git a/src/routes/(adminLayout)/admin/equipment/new/+page.server.ts b/src/routes/(adminLayout)/admin/equipment/new/+page.server.ts deleted file mode 100644 index 4be1808..0000000 --- a/src/routes/(adminLayout)/admin/equipment/new/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - types: await Server.ssr.equipment.type(ev) - } -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/equipment/new/+page.svelte b/src/routes/(adminLayout)/admin/equipment/new/+page.svelte deleted file mode 100644 index 3853a4d..0000000 --- a/src/routes/(adminLayout)/admin/equipment/new/+page.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - -
- - - - - - - - - - - - - - - - -
diff --git a/src/routes/(adminLayout)/admin/gallery/+page.server.ts b/src/routes/(adminLayout)/admin/gallery/+page.server.ts deleted file mode 100644 index a036b11..0000000 --- a/src/routes/(adminLayout)/admin/gallery/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - items: await Server.ssr.gallery.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/gallery/+page.svelte b/src/routes/(adminLayout)/admin/gallery/+page.svelte deleted file mode 100644 index a15309a..0000000 --- a/src/routes/(adminLayout)/admin/gallery/+page.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - -
- - - {#if !items} - Načítání... - {:else if items.length == 0} - Přidej nějaký obrázky do galerie - {:else} - {#each items as item} - - {/each} - {/if} -
diff --git a/src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts b/src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts deleted file mode 100644 index 5a85463..0000000 --- a/src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - equipment: await Server.ssr.equipment.GET(ev), - data: await Server.ssr.gallery.single(ev, parseInt(ev.params.id)) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte b/src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte deleted file mode 100644 index fdaa3aa..0000000 --- a/src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/src/routes/(adminLayout)/admin/gallery/new/+page.server.ts b/src/routes/(adminLayout)/admin/gallery/new/+page.server.ts deleted file mode 100644 index 83e911d..0000000 --- a/src/routes/(adminLayout)/admin/gallery/new/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - equipment: await Server.ssr.equipment.GET(ev), - details: await Server.ssr.details.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/gallery/new/+page.svelte b/src/routes/(adminLayout)/admin/gallery/new/+page.svelte deleted file mode 100644 index 185a333..0000000 --- a/src/routes/(adminLayout)/admin/gallery/new/+page.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/src/routes/(adminLayout)/admin/main/+page.server.ts b/src/routes/(adminLayout)/admin/main/+page.server.ts deleted file mode 100644 index 0a277dc..0000000 --- a/src/routes/(adminLayout)/admin/main/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (event) => { - const request = await event.fetch('https://api.thecatapi.com/v1/images/search'); - const json = await request.json(); - - return { - //SELECT COUNT(*), page FROM visitors WHERE DATE(date) = CURDATE() GROUP BY page; - - today: await Server.ssr.stats.getToday(event), - thisWeek: await Server.ssr.stats.getWeek(event), - yearData: await Server.ssr.stats.weekGraph.GET(event), - cat: json?.[0].url - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/main/+page.svelte b/src/routes/(adminLayout)/admin/main/+page.svelte deleted file mode 100644 index 4816d6d..0000000 --- a/src/routes/(adminLayout)/admin/main/+page.svelte +++ /dev/null @@ -1,89 +0,0 @@ - - -
-
-

Statistiky

-

Unikátní návštěvníci dnes: {today}

-

Unikátní návštěvníci tento týden: {thisWeek}

-
- -
-
-
- {#if data.cat} - Random cat :) - {:else} - No cat found :( - {/if} -
-
diff --git a/src/routes/(adminLayout)/admin/projects/+page.server.ts b/src/routes/(adminLayout)/admin/projects/+page.server.ts deleted file mode 100644 index f5fabde..0000000 --- a/src/routes/(adminLayout)/admin/projects/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - projects: await Server.ssr.project.list(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/projects/+page.svelte b/src/routes/(adminLayout)/admin/projects/+page.svelte deleted file mode 100644 index dc6b64e..0000000 --- a/src/routes/(adminLayout)/admin/projects/+page.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - -
- {#if !projects} - Načítání... - {:else} - - {#if projects.length == 0} - Nebyly nalezeny žádné projekty, přidej nějaký. - {:else} - {#each projects as project} - - {/each} - {/if} - {/if} -
diff --git a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts b/src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts deleted file mode 100644 index 49f6b6a..0000000 --- a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from '$/lib/server/server'; -import { conn } from '$/lib/server/variables'; -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - const uuid = ev.params.uuid; - - const data = await conn.selectFrom('project').select('uuid').where('uuid', '=', uuid).executeTakeFirst(); - - if (!data) { - throw redirect(302, '/admin/projects'); - } - - return { - project: await Server.ssr.project.get.POST(ev, { uuid }), - tags: await Server.ssr.tag.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte b/src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte deleted file mode 100644 index 784928a..0000000 --- a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte +++ /dev/null @@ -1,358 +0,0 @@ - - -
- {#if !projectData} - Načítání... - {:else} - - project preview - - - - - - - - - - - - - - - - - - - - - {#if unassignedTags.length == 0} - Nejsou žádné jiné tagy k přiřazení - {:else} - {#each unassignedTags as tag} - {tag.name} - {/each} - {/if} - - - - - - - {#if assignedTags.length == 0} - Nejsou žádné jiné tagy k přiřazení - {:else} - {#each assignedTags as tag} - {tag.name} - {/each} - {/if} - - - - - - diff --git a/src/components/utility/Card.svelte b/src/components/utility/Card.svelte new file mode 100644 index 0000000..255945c --- /dev/null +++ b/src/components/utility/Card.svelte @@ -0,0 +1,12 @@ + + +
+ {@render children()} +
diff --git a/src/lib/functions.ts b/src/lib/functions.ts index 73fb0b6..8fe5154 100644 --- a/src/lib/functions.ts +++ b/src/lib/functions.ts @@ -22,3 +22,22 @@ export const SwalAlert = <$Type = unknown>(data: SweetAlertOptions) => { ...data }); }; + +export const formatDate = (date: Date | string) => { + if (typeof date === 'string') { + date = new Date(date); + } + + const options: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }; + + const locale = navigator.language || 'cs-CZ'; + + return new Intl.DateTimeFormat(locale, options).format(date); +}; diff --git a/src/lib/lang/_template.ts b/src/lib/lang/_template.ts index 36cd2bb..5b69426 100644 --- a/src/lib/lang/_template.ts +++ b/src/lib/lang/_template.ts @@ -92,6 +92,54 @@ export default o({ no: _ }) }) + }), + article: o({ + title: _, + create: _, + empty: _, + image: _, + articleTitle: _, + published: _, + lastEdit: _, + actions: _, + form: o({ + back: _, + editTitle: _, + createTitle: _, + details: o({ + title: _, + titleInput: _, + titlePlaceholder: _, + content: _, + editContent: _, + previewContent: _, + contentPlaceholder: _ + }), + equipment: o({ + title: _, + select: _ + }), + images: o({ + title: _, + upload: _, + descriptionPlaceholder: _, + browse: _, + button: _ + }), + exposures: o({ + title: _, + date: _, + type: _, + count: _, + seconds: _, + button: _, + time: _, + total: _ + }), + cancel: _, + save: _, + create: _ + }) }) }), errors: o({ @@ -120,5 +168,11 @@ export default o({ guidescope: _, phone: _, focuser: _ + }), + frames: o({ + light: _, + dark: _, + flat: _, + bias: _ }) }); diff --git a/src/lib/lang/czech.ts b/src/lib/lang/czech.ts index 1239b0f..7ff64df 100644 --- a/src/lib/lang/czech.ts +++ b/src/lib/lang/czech.ts @@ -130,6 +130,54 @@ export default lang.parse({ no: 'Ne' } } + }, + article: { + title: 'Články', + create: 'Vytvořit článek', + empty: 'Žádné články nebyly vytvořeny.', + articleTitle: 'Název článku', + image: 'Obrázek', + lastEdit: 'Poslední úprava', + published: 'Publikováno', + actions: 'Akce', + form: { + back: 'Zpátky k článkům', + editTitle: 'Upravit článek', + createTitle: 'Vytvořit článek', + details: { + title: 'Detaily článku', + titleInput: 'Název článku', + titlePlaceholder: 'Zadej název článku', + content: 'Obsah článku (Markdown)', + editContent: 'Úprava', + previewContent: 'Náhled', + contentPlaceholder: 'Zadej obsah článku v markdownu...' + }, + equipment: { + title: 'Vybavení', + select: 'Vyber vybavení' + }, + images: { + title: 'Obrázky', + upload: 'Nahrát obrázek', + descriptionPlaceholder: 'Zadej popis obrázku', + browse: 'Procházet', + button: 'Přidat obrázek' + }, + exposures: { + title: 'Expozice', + date: 'Datum', + type: 'Typ', + count: 'Počet', + seconds: 'Sekundy', + button: 'Přidat expozici', + time: 'Čas (s)', + total: 'Celkový čas (s)' + }, + cancel: 'Zrušit', + save: 'Uložit článek', + create: 'Vytvořit článek' + } } }, errors: { @@ -158,5 +206,11 @@ export default lang.parse({ guidescope: 'Guidescope', phone: 'Telefon', focuser: 'Zaostřovač' + }, + frames: { + light: 'Light', + dark: 'Dark', + flat: 'Flat', + bias: 'Bias' } } satisfies z.infer); diff --git a/src/lib/lang/english.ts b/src/lib/lang/english.ts index a463d73..04e2bd9 100644 --- a/src/lib/lang/english.ts +++ b/src/lib/lang/english.ts @@ -130,6 +130,54 @@ export default lang.parse({ no: 'No' } } + }, + article: { + title: 'Articles', + create: 'Create new article', + empty: 'No articles have been created yet.', + image: 'Image', + actions: 'Actions', + articleTitle: 'Article title', + published: 'Published', + lastEdit: 'Last edit', + form: { + back: 'Back to articles', + editTitle: 'Edit article', + createTitle: 'Create article', + details: { + title: 'Article details', + titleInput: 'Article title', + titlePlaceholder: 'Enter article title', + content: 'Article content (Markdown)', + editContent: 'Edit', + previewContent: 'Preview', + contentPlaceholder: 'Enter article content in markdown...' + }, + equipment: { + title: 'Equipment', + select: 'Select equipment' + }, + images: { + title: 'Images', + upload: 'Upload image', + descriptionPlaceholder: 'Enter image description', + browse: 'Browse', + button: 'Add image' + }, + exposures: { + title: 'Exposures', + date: 'Date', + type: 'Type', + count: 'Count', + seconds: 'Seconds', + button: 'Add exposure', + time: 'Time (s)', + total: 'Total (s)' + }, + cancel: 'Cancel', + save: 'Save article', + create: 'Create article' + } } }, errors: { @@ -158,5 +206,11 @@ export default lang.parse({ guidescope: 'Guidescope', phone: 'Phone', focuser: 'Focuser' + }, + frames: { + light: 'Light', + dark: 'Dark', + flat: 'Flat', + bias: 'Bias' } } satisfies z.infer); diff --git a/src/lib/server/functions.ts b/src/lib/server/functions.ts index e99544a..1ca95fa 100644 --- a/src/lib/server/functions.ts +++ b/src/lib/server/functions.ts @@ -59,14 +59,14 @@ export const isFile = async (path: string) => { }; export const uploadFile = async (file: File) => { - const arrayBuffer= await file.arrayBuffer(); + const arrayBuffer = await file.arrayBuffer(); const path = Path.parse(file.name); - const name = (await randomBytesAsync(16)).toString("hex") + path.ext; + const name = (await randomBytesAsync(16)).toString('hex') + path.ext; if (!(await isDirectory(FILE_FOLDER))) { await fs.mkdir(FILE_FOLDER, { recursive: true }); } -await fs.writeFile(Path.join(FILE_FOLDER, name), Buffer.from(arrayBuffer)); + await fs.writeFile(Path.join(FILE_FOLDER, name), Buffer.from(arrayBuffer)); return name; -} +}; diff --git a/src/routes/[[lang=lang]]/admin/article/+page.server.ts b/src/routes/[[lang=lang]]/admin/article/+page.server.ts new file mode 100644 index 0000000..d08938c --- /dev/null +++ b/src/routes/[[lang=lang]]/admin/article/+page.server.ts @@ -0,0 +1,27 @@ +import { conn } from '$/lib/server/variables'; +import type { PageServerLoad } from './$types'; + +export const load = (async () => { + const previews = await conn + .selectFrom('gallery_image') + .innerJoin( + (eb) => + eb + .selectFrom('gallery_image') + .select(['article_id', conn.fn.min('id').as('min_id')]) + .groupBy('article_id') + .as('min'), + (eb) => eb.onRef('gallery_image.article_id', '=', 'min.article_id').onRef('gallery_image.id', '=', 'min.min_id') + ) + .select(['gallery_image.article_id', 'gallery_image.name', 'gallery_image.alt_text']) + .execute(); + + const articles = await conn.selectFrom('article').selectAll().execute(); + + return { + articles: articles.map((article) => ({ + ...article, + preview: previews.find((preview) => preview.article_id === article.id) + })) + }; +}) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/admin/article/+page.svelte b/src/routes/[[lang=lang]]/admin/article/+page.svelte new file mode 100644 index 0000000..47ee6a0 --- /dev/null +++ b/src/routes/[[lang=lang]]/admin/article/+page.svelte @@ -0,0 +1,61 @@ + + +
+
+
+

{_lang.title}

+ +
+ {#if data.articles.length === 0} + {_lang.empty} + {:else} +
+ + + + + + + + + + + + + {#each data.articles as article (article.id.toString())} + + + + + + + + + {/each} + +
Id{_lang.image}{_lang.articleTitle}{_lang.published}{_lang.lastEdit}{_lang.actions}
{article.id} + {#if article.preview} + {article.preview.alt_text} + {/if} + {article.title}{formatDate(article.created_at)}{formatDate(article.updated_at)} + {}} name="bi-pencil-fill" class="cursor-pointer" /> + + {}} name="bi-trash-fill" class="cursor-pointer text-red-500" /> +
+
+ {/if} +
+
diff --git a/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts b/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts new file mode 100644 index 0000000..54c0e15 --- /dev/null +++ b/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts @@ -0,0 +1,39 @@ +import { conn } from '$/lib/server/variables'; +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load = (async ({ params }) => { + if (params.id === 'new') return; + + const article = await conn.selectFrom('article').selectAll().where('id', '=', params.id).executeTakeFirst(); + + if (!article) { + redirect(302, `/${params.lang ?? 'cs'}/admin/article`); + } + + const images = await conn.selectFrom('gallery_image').selectAll().where('article_id', '=', params.id).orderBy('id', 'asc').execute(); + + const exposures = await conn.selectFrom('exposure').selectAll().where('article_id', '=', params.id).orderBy('id', 'asc').execute(); + + const equipment = await conn + .selectFrom('article_equipment') + .innerJoin('equipment', 'article_equipment.equipment_id', 'equipment.id') + .selectAll() + .where('article_equipment.article_id', '=', params.id) + .orderBy('article_equipment.id', 'asc') + .execute(); + + return { + article: { + ...article, + images, + exposures, + equipment: equipment.map((e) => ({ + id: e.id, + name: e.name, + link: e.link, + type_id: e.type_id + })) + } + }; +}) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte b/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte new file mode 100644 index 0000000..87ba048 --- /dev/null +++ b/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte @@ -0,0 +1,45 @@ + + +{#snippet subTitle(text: string)} +

{text}

+{/snippet} + +
+
+ + + {_lang.back} + + +

{editing ? _lang.editTitle : _lang.createTitle}

+ + {@render subTitle(_lang.details.title)} + + + + + {#snippet right()} +
{showPreview ? _lang.details.previewContent : _lang.details.editContent}
+ {/snippet} + diff --git a/src/components/headers/H1.svelte b/src/components/headers/H1.svelte index 4090224..111b514 100644 --- a/src/components/headers/H1.svelte +++ b/src/components/headers/H1.svelte @@ -1,10 +1,10 @@

- {@render children?.()} + {@render children?.()}

diff --git a/src/components/headers/H2.svelte b/src/components/headers/H2.svelte index bfaee57..2a38260 100644 --- a/src/components/headers/H2.svelte +++ b/src/components/headers/H2.svelte @@ -1,10 +1,10 @@

- {@render children?.()} + {@render children?.()}

diff --git a/src/components/headers/H3.svelte b/src/components/headers/H3.svelte index f0cd7b7..32c5381 100644 --- a/src/components/headers/H3.svelte +++ b/src/components/headers/H3.svelte @@ -1,10 +1,10 @@

- {@render children?.()} + {@render children?.()}

diff --git a/src/components/table/TBody.svelte b/src/components/table/TBody.svelte index 7f9bafd..5fcba57 100644 --- a/src/components/table/TBody.svelte +++ b/src/components/table/TBody.svelte @@ -1,11 +1,11 @@ - {@render children?.()} + {@render children?.()} diff --git a/src/components/table/THead.svelte b/src/components/table/THead.svelte index c58fc96..64ad2f7 100644 --- a/src/components/table/THead.svelte +++ b/src/components/table/THead.svelte @@ -1,9 +1,9 @@ - {@render children?.()} + {@render children?.()} diff --git a/src/components/table/Table.svelte b/src/components/table/Table.svelte index 3179ca1..a5ede4c 100644 --- a/src/components/table/Table.svelte +++ b/src/components/table/Table.svelte @@ -1,13 +1,13 @@
- - {@render children?.()} -
+ + {@render children?.()} +
diff --git a/src/components/table/Td.svelte b/src/components/table/Td.svelte index 24abbe3..485e0be 100644 --- a/src/components/table/Td.svelte +++ b/src/components/table/Td.svelte @@ -1,10 +1,10 @@ - {@render children?.()} + {@render children?.()} diff --git a/src/components/table/Th.svelte b/src/components/table/Th.svelte index f7f1f5a..3010c1a 100644 --- a/src/components/table/Th.svelte +++ b/src/components/table/Th.svelte @@ -1,10 +1,10 @@ - {@render children?.()} + {@render children?.()} diff --git a/src/components/table/Tr.svelte b/src/components/table/Tr.svelte index 99fe5a3..9f83985 100644 --- a/src/components/table/Tr.svelte +++ b/src/components/table/Tr.svelte @@ -1,11 +1,11 @@ - {@render children?.()} + {@render children?.()} diff --git a/src/components/utility/Card.svelte b/src/components/utility/Card.svelte index 255945c..786ce08 100644 --- a/src/components/utility/Card.svelte +++ b/src/components/utility/Card.svelte @@ -1,12 +1,12 @@
- {@render children()} + {@render children()}
diff --git a/src/components/utility/Chart.svelte b/src/components/utility/Chart.svelte index 2d76f27..08e1bea 100644 --- a/src/components/utility/Chart.svelte +++ b/src/components/utility/Chart.svelte @@ -1,37 +1,37 @@ diff --git a/src/components/utility/Dialog.svelte b/src/components/utility/Dialog.svelte index 989c19b..eeefcb0 100644 --- a/src/components/utility/Dialog.svelte +++ b/src/components/utility/Dialog.svelte @@ -1,20 +1,20 @@ {#if opened} -
- { - opened = false; - onClose(); - }} - class="border-text bg-background/80 m-auto flex max-w-[90%] flex-col rounded-md border-2 p-4" - > - {@render children()} - -
+
+ { + opened = false; + onClose(); + }} + class="border-text bg-background/80 m-auto flex max-w-[90%] flex-col rounded-md border-2 p-4" + > + {@render children()} + +
{/if} diff --git a/src/components/utility/Dots.svelte b/src/components/utility/Dots.svelte index 89e21ba..2d8309c 100644 --- a/src/components/utility/Dots.svelte +++ b/src/components/utility/Dots.svelte @@ -1,18 +1,18 @@
- - {#each Array.from({ length: count }) as _, i (i)} - - {/each} + + {#each Array.from({ length: count }) as _, i (i)} + + {/each}
diff --git a/src/components/utility/Icon.svelte b/src/components/utility/Icon.svelte index d05c9fa..1a7b77a 100644 --- a/src/components/utility/Icon.svelte +++ b/src/components/utility/Icon.svelte @@ -1,10 +1,10 @@ - {@render children?.()} + {@render children?.()} diff --git a/src/components/utility/Image.svelte b/src/components/utility/Image.svelte index dfa620e..1d39f3f 100644 --- a/src/components/utility/Image.svelte +++ b/src/components/utility/Image.svelte @@ -1,19 +1,19 @@ - - - - - - - - + + + + + + + + diff --git a/src/components/utility/Link.svelte b/src/components/utility/Link.svelte index c61ef4f..aef11c8 100644 --- a/src/components/utility/Link.svelte +++ b/src/components/utility/Link.svelte @@ -1,21 +1,21 @@ - {@render children()} + {@render children()} diff --git a/src/components/utility/Markdown.svelte b/src/components/utility/Markdown.svelte index 1b360d0..70b3123 100644 --- a/src/components/utility/Markdown.svelte +++ b/src/components/utility/Markdown.svelte @@ -1,13 +1,13 @@
- - {@html marked(content)} + + {@html marked(content)}
diff --git a/src/components/utility/RichText.svelte b/src/components/utility/RichText.svelte index 29f9e2a..2dbdf73 100644 --- a/src/components/utility/RichText.svelte +++ b/src/components/utility/RichText.svelte @@ -1,22 +1,22 @@ {#each text as part, index (index)} - {#if typeof part === 'string'} - {#if part === '%%SPACE%%'} - - {:else} - - {@html part} - {/if} + {#if typeof part === 'string'} + {#if part === '%%SPACE%%'} + {:else} - - {part.text} - + + {@html part} {/if} + {:else} + + {part.text} + + {/if} {/each} diff --git a/src/components/utility/clickOutside.svelte b/src/components/utility/clickOutside.svelte index 1f7635a..35a8aca 100644 --- a/src/components/utility/clickOutside.svelte +++ b/src/components/utility/clickOutside.svelte @@ -1,40 +1,40 @@
{@render children()}
diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 16afc92..c27632d 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -2,26 +2,27 @@ import type { Handle } from '@sveltejs/kit'; import { conn } from './lib/server/variables'; export const handle = (async ({ event, resolve }) => { - const response = await resolve(event); - const path = event.url.pathname; + const response = await resolve(event); + const path = event.url.pathname; - const disallowedPaths = ['/api']; - const UA = event.request.headers.get('user-agent') || ''; - const isBot = /bot|crawl|spider|slurp/i.test(UA); + const disallowedPaths = ['/api']; + const UA = event.request.headers.get('user-agent') || ''; + const isBot = /bot|crawl|spider|slurp/i.test(UA); - if (!isBot && response.status === 200 && !event.locals.is404 && !disallowedPaths.some((p) => path.startsWith(p))) { - conn.insertInto('visitors') - .values({ - ip: event.getClientAddress(), - page: path, - user_agent: event.request.headers.get('user-agent') || '' - }) - .execute() - .catch((err) => { - //eslint-disable-next-line no-console - console.error('Error logging visitor:', err); - }); - } + if (!isBot && response.status === 200 && !event.locals.is404 && !disallowedPaths.some((p) => path.startsWith(p))) { + conn + .insertInto('visitors') + .values({ + ip: event.getClientAddress(), + page: path, + user_agent: event.request.headers.get('user-agent') || '' + }) + .execute() + .catch((err) => { + //eslint-disable-next-line no-console + console.error('Error logging visitor:', err); + }); + } - return response; + return response; }) satisfies Handle; diff --git a/src/lib/functions.ts b/src/lib/functions.ts index d7ebb94..fdebbd7 100644 --- a/src/lib/functions.ts +++ b/src/lib/functions.ts @@ -2,51 +2,51 @@ import { browser } from '$app/environment'; import Swal, { type SweetAlertOptions } from 'sweetalert2'; export const sleep = (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); }; export const SwalAlert = <$Type = unknown>(data: SweetAlertOptions) => { - if (!browser) { - return { - isConfirmed: false - } as const; - } - - return Swal.fire<$Type>({ - toast: true, - position: 'top-end', - timer: 2000, - timerProgressBar: true, - showCancelButton: false, - showConfirmButton: false, - ...data - }); + if (!browser) { + return { + isConfirmed: false + } as const; + } + + return Swal.fire<$Type>({ + toast: true, + position: 'top-end', + timer: 2000, + timerProgressBar: true, + showCancelButton: false, + showConfirmButton: false, + ...data + }); }; export const formatDate = (date: Date | string, time = true) => { - if (typeof date === 'string') { - date = new Date(date); - } - - const options: Intl.DateTimeFormatOptions = { - year: 'numeric', - month: '2-digit', - day: '2-digit', - ...(time && { - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }) - }; - - const locale = navigator.language || 'cs-CZ'; - - return new Intl.DateTimeFormat(locale, options).format(date); + if (typeof date === 'string') { + date = new Date(date); + } + + const options: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: '2-digit', + day: '2-digit', + ...(time && { + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }) + }; + + const locale = navigator.language || 'cs-CZ'; + + return new Intl.DateTimeFormat(locale, options).format(date); }; export const sToHHMM = (seconds: number) => { - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); - return `${hours.toString().padStart(2, '0')}h ${minutes.toString().padStart(2, '0')}m`; + return `${hours.toString().padStart(2, '0')}h ${minutes.toString().padStart(2, '0')}m`; }; diff --git a/src/lib/lang/czech.ts b/src/lib/lang/czech.ts index 2a1961b..c2a0f41 100644 --- a/src/lib/lang/czech.ts +++ b/src/lib/lang/czech.ts @@ -2,262 +2,262 @@ import type { z } from 'zod'; import lang, { _extensions } from './_template'; export default lang.parse({ - default_desc: - 'Ahoj, jsem Patrik, student a programátor, který se ve volném čase věnuje astrofotografování. Věnuji se tvorbě webových stránek a aplikací ve frameworku SvelteKit. Nebráním se tvorbě jiných aplikací, například v NodeJS, nebo jiných frameworkcích, jako jsou Vue.js, nebo React.', - yes: 'Ano', - no: 'Ne', - navigation: { - home: 'Domů', - gallery: 'Galerie', - admin: 'Administrace', - login: 'Přihlášení' - }, - adminNavigation: { - home: 'Panel', - equipment: 'Vybavení', - articles: 'Články' - }, - error: { - title: 'Chyba', - message: 'Oops!', - sub_message: 'Tuto stránku neznáme :(', - go_home: 'Zpátky domů' + default_desc: + 'Ahoj, jsem Patrik, student a programátor, který se ve volném čase věnuje astrofotografování. Věnuji se tvorbě webových stránek a aplikací ve frameworku SvelteKit. Nebráním se tvorbě jiných aplikací, například v NodeJS, nebo jiných frameworkcích, jako jsou Vue.js, nebo React.', + yes: 'Ano', + no: 'Ne', + navigation: { + home: 'Domů', + gallery: 'Galerie', + admin: 'Administrace', + login: 'Přihlášení' + }, + adminNavigation: { + home: 'Panel', + equipment: 'Vybavení', + articles: 'Články' + }, + error: { + title: 'Chyba', + message: 'Oops!', + sub_message: 'Tuto stránku neznáme :(', + go_home: 'Zpátky domů' + }, + main: { + age: 'Věk', + text: [ + '🎓 Aktuálně studuji na ', + { + text: 'Vysoké škole báňské – Technické univerzitě Ostrava', + link: 'https://www.vsb.cz/' + }, + ', obor Informatika na ', + { + text: 'Fakultě elektrotechniky a informatiky', + link: 'https://www.fei.vsb.cz/' + }, + '.', + '%%SPACE%%', + ' 💻 Ve volném čase se nejvíce věnuji vývoji webových aplikací pomocí ', + { + text: 'SvelteKit', + link: 'https://kit.svelte.dev/' + }, + ', ', + { + text: 'Tailwind CSS', + link: 'https://tailwindcss.com/' + }, + ' a ', + { + text: 'TypeScriptu', + link: 'https://www.typescriptlang.org/' + }, + '. Mám zkušenosti jak s frontendem, tak backendem a občas zabrousím i do designu.', + '%%SPACE%%', + ' 🧠 Kromě SvelteKitu zvládám i práci s jinými frameworky, jako je ', + { + text: 'Vue.js', + link: 'https://vuejs.org/' + }, + ' nebo ', + { + text: 'React', + link: 'https://react.dev/' + }, + ', takže se umím přizpůsobit různým technologiím.', + '%%SPACE%%', + ' 🛠️ V Node.js vytvářím i různé aplikace jako jsou Discord boti, Twitch boti nebo jiné skripty, které zjednodušují život.', + '%%SPACE%%', + ' 🌌 Mimo programování se věnuji také astrofotografii – moje snímky najdeš v ', + { + text: 'mé galerii', + link: '/cs/gallery', + blank: false + }, + '!' + ] + }, + admin: { + login: { + title: 'Přihlášení', + username: 'Jméno', + password: 'Heslo', + submit: 'Přihlásit se' }, main: { - age: 'Věk', - text: [ - '🎓 Aktuálně studuji na ', - { - text: 'Vysoké škole báňské – Technické univerzitě Ostrava', - link: 'https://www.vsb.cz/' - }, - ', obor Informatika na ', - { - text: 'Fakultě elektrotechniky a informatiky', - link: 'https://www.fei.vsb.cz/' - }, - '.', - '%%SPACE%%', - ' 💻 Ve volném čase se nejvíce věnuji vývoji webových aplikací pomocí ', - { - text: 'SvelteKit', - link: 'https://kit.svelte.dev/' - }, - ', ', - { - text: 'Tailwind CSS', - link: 'https://tailwindcss.com/' - }, - ' a ', - { - text: 'TypeScriptu', - link: 'https://www.typescriptlang.org/' - }, - '. Mám zkušenosti jak s frontendem, tak backendem a občas zabrousím i do designu.', - '%%SPACE%%', - ' 🧠 Kromě SvelteKitu zvládám i práci s jinými frameworky, jako je ', - { - text: 'Vue.js', - link: 'https://vuejs.org/' - }, - ' nebo ', - { - text: 'React', - link: 'https://react.dev/' - }, - ', takže se umím přizpůsobit různým technologiím.', - '%%SPACE%%', - ' 🛠️ V Node.js vytvářím i různé aplikace jako jsou Discord boti, Twitch boti nebo jiné skripty, které zjednodušují život.', - '%%SPACE%%', - ' 🌌 Mimo programování se věnuji také astrofotografii – moje snímky najdeš v ', - { - text: 'mé galerii', - link: '/cs/gallery', - blank: false - }, - '!' - ] + stats: 'Statistiky', + today: 'Dnes', + week: 'Tento týden' }, - admin: { - login: { - title: 'Přihlášení', - username: 'Jméno', - password: 'Heslo', - submit: 'Přihlásit se' + equipment: { + actions: 'Akce', + types: { + title: 'Typy vybavení', + addTitle: 'Přidání nového typu', + translateKey: 'Překlad', + priority: 'Priorita', + placeholder: 'Název typu vybavení', + button: 'Přidat', + success: 'Nový typ vybavení byl přidán!', + empty: 'Žádné typy vybavení nebyly přidány.', + editSuccess: 'Název typu vybavení byl úspěšně upraven!', + deleteSuccess: 'Typ vybavení byl úspěšně smazán!', + edit: { + title: 'Úprava typu vybavení s id %1', + button: 'Upravit' }, - main: { - stats: 'Statistiky', - today: 'Dnes', - week: 'Tento týden' - }, - equipment: { - actions: 'Akce', - types: { - title: 'Typy vybavení', - addTitle: 'Přidání nového typu', - translateKey: 'Překlad', - priority: 'Priorita', - placeholder: 'Název typu vybavení', - button: 'Přidat', - success: 'Nový typ vybavení byl přidán!', - empty: 'Žádné typy vybavení nebyly přidány.', - editSuccess: 'Název typu vybavení byl úspěšně upraven!', - deleteSuccess: 'Typ vybavení byl úspěšně smazán!', - edit: { - title: 'Úprava typu vybavení s id %1', - button: 'Upravit' - }, - delete: { - question: 'Opravdu chceš smazat tento typ?' - } - }, - equipment: { - title: 'Vybavení', - addTitle: 'Přidání nového vybavení', - type: 'Typ', - name: 'Název', - link: 'Odkaz', - button: 'Přidat', - success: 'Nové vybavení bylo přidáno!', - empty: 'Žádné vybavení nebylo přidáno.', - editSuccess: 'Vybavení bylo úspěšně upraveno!', - deleteSuccess: 'Vybavení bylo úspěšně smazáno!', - edit: { - title: 'Úprava vybavení s id %1', - button: 'Upravit' - }, - delete: { - question: 'Opravdu chceš smazat toto vybavení?' - } - } - }, - article: { - title: 'Články', - create: 'Vytvořit článek', - empty: 'Žádné články nebyly vytvořeny.', - articleTitle: 'Název článku', - description: 'Popisek', - image: 'Obrázek', - lastEdit: 'Poslední úprava', - published: 'Publikováno', - actions: 'Akce', - form: { - back: 'Zpátky k článkům', - editTitle: 'Upravit článek', - createTitle: 'Vytvořit článek', - details: { - title: 'Detaily článku', - titleInput: 'Název článku', - titlePlaceholder: 'Zadej název článku', - description: 'Popis článku', - descriptionPlaceholder: 'Zadej krátký popis článku', - content: 'Obsah článku (Markdown)', - editContent: 'Úprava', - previewContent: 'Náhled', - contentPlaceholder: 'Zadej obsah článku v markdownu...' - }, - equipment: { - title: 'Vybavení', - select: 'Vyber vybavení', - empty: 'Nevybral jsi žádné vybavení.' - }, - images: { - title: 'Obrázky', - upload: 'Nahrát obrázek', - descriptionPlaceholder: 'Zadej popis obrázku', - browse: 'Procházet', - button: 'Přidat obrázek', - alt: 'Zadej prosím popisek obrázku', - empty: 'Zatím nebyl přidán žádný obrázek.', - noImage: 'Vyber prosím obrázek', - multiple: 'Vyber prosím pouze jeden obrázek', - confirmDelete: 'Opravdu chceš smazat tento obrázek?' - }, - exposures: { - title: 'Expozice', - date: 'Datum', - type: 'Typ', - count: 'Počet', - seconds: 'Sekundy', - button: 'Přidat expozici', - time: 'Čas (s)', - total: 'Celkový čas (s)', - empty: 'Zatím jsi nepřidal žádnou expozici.', - frames: 'Snímky' - }, - cancel: 'Zrušit', - save: 'Uložit článek', - create: 'Vytvořit článek', - created: 'Článek byl úspěšně vytvořen!', - updated: 'Článek byl úspěšně upraven!' - } + delete: { + question: 'Opravdu chceš smazat tento typ?' } - }, - gallery: { - title: 'Astro-Galerie', - description: 'Zde nalezneš astrofotografie pořízené mnou, společně s popisy a detaily o expozicích.', - updated: 'Aktualizováno', - created: 'Vytvořeno', - readMore: 'Čti dále', - more: 'více', - back: 'Zpět na galerii', - totalExposure: 'Celkový čas expozice', - article: 'Článek', - details: 'Technické detaily', - equipment: 'Použité vybavení', - exposureSummary: 'Souhrn expozic', - framesCount: { - '1': 'snímek', - '2': 'snímky', - other: 'snímků' + }, + equipment: { + title: 'Vybavení', + addTitle: 'Přidání nového vybavení', + type: 'Typ', + name: 'Název', + link: 'Odkaz', + button: 'Přidat', + success: 'Nové vybavení bylo přidáno!', + empty: 'Žádné vybavení nebylo přidáno.', + editSuccess: 'Vybavení bylo úspěšně upraveno!', + deleteSuccess: 'Vybavení bylo úspěšně smazáno!', + edit: { + title: 'Úprava vybavení s id %1', + button: 'Upravit' }, - exposureDetails: 'Detaily expozic', - equipmentDetails: 'Detaily vybavení', - images: 'Obrázky' + delete: { + question: 'Opravdu chceš smazat toto vybavení?' + } + } }, - errors: { - internal: 'Něco se nepovedlo, zkus to prosím zachvíli znova.', - login: { - form: 'Vyplň prosím všechny údaje.', - username: 'Špatné uživatelské jméno.', - password: 'Špatné heslo.' - }, - types: { - form: 'Vyplň prosím název typu vybavení.', - empty: 'Překladový klíč nesmí být prázdný.', - priority: 'Priorita musí být číslo >=0.' + article: { + title: 'Články', + create: 'Vytvořit článek', + empty: 'Žádné články nebyly vytvořeny.', + articleTitle: 'Název článku', + description: 'Popisek', + image: 'Obrázek', + lastEdit: 'Poslední úprava', + published: 'Publikováno', + actions: 'Akce', + form: { + back: 'Zpátky k článkům', + editTitle: 'Upravit článek', + createTitle: 'Vytvořit článek', + details: { + title: 'Detaily článku', + titleInput: 'Název článku', + titlePlaceholder: 'Zadej název článku', + description: 'Popis článku', + descriptionPlaceholder: 'Zadej krátký popis článku', + content: 'Obsah článku (Markdown)', + editContent: 'Úprava', + previewContent: 'Náhled', + contentPlaceholder: 'Zadej obsah článku v markdownu...' }, equipment: { - form: 'Vyplň prosím všechny údaje.', - url: 'Zadej platný odkaz.' + title: 'Vybavení', + select: 'Vyber vybavení', + empty: 'Nevybral jsi žádné vybavení.' }, - upload: { - missing: 'Vyber prosím soubor k nahrání.', - error: 'Nepodařilo se nahrát soubor, zkus to prosím znovu.', - invalidFile: 'Nahrej prosím platný soubor', - extension: 'Nahrej prosím platný obrázek s příponou ' + _extensions, - notFound: 'Soubor s tímto jménem neexistuje.' + images: { + title: 'Obrázky', + upload: 'Nahrát obrázek', + descriptionPlaceholder: 'Zadej popis obrázku', + browse: 'Procházet', + button: 'Přidat obrázek', + alt: 'Zadej prosím popisek obrázku', + empty: 'Zatím nebyl přidán žádný obrázek.', + noImage: 'Vyber prosím obrázek', + multiple: 'Vyber prosím pouze jeden obrázek', + confirmDelete: 'Opravdu chceš smazat tento obrázek?' }, - article: { - notFound: 'Článek s tímto id neexistuje.', - noImages: 'Článek musí obsahovat alespoň jeden obrázek.' - } + exposures: { + title: 'Expozice', + date: 'Datum', + type: 'Typ', + count: 'Počet', + seconds: 'Sekundy', + button: 'Přidat expozici', + time: 'Čas (s)', + total: 'Celkový čas (s)', + empty: 'Zatím jsi nepřidal žádnou expozici.', + frames: 'Snímky' + }, + cancel: 'Zrušit', + save: 'Uložit článek', + create: 'Vytvořit článek', + created: 'Článek byl úspěšně vytvořen!', + updated: 'Článek byl úspěšně upraven!' + } + } + }, + gallery: { + title: 'Astro-Galerie', + description: 'Zde nalezneš astrofotografie pořízené mnou, společně s popisy a detaily o expozicích.', + updated: 'Aktualizováno', + created: 'Vytvořeno', + readMore: 'Čti dále', + more: 'více', + back: 'Zpět na galerii', + totalExposure: 'Celkový čas expozice', + article: 'Článek', + details: 'Technické detaily', + equipment: 'Použité vybavení', + exposureSummary: 'Souhrn expozic', + framesCount: { + '1': 'snímek', + '2': 'snímky', + other: 'snímků' + }, + exposureDetails: 'Detaily expozic', + equipmentDetails: 'Detaily vybavení', + images: 'Obrázky' + }, + errors: { + internal: 'Něco se nepovedlo, zkus to prosím zachvíli znova.', + login: { + form: 'Vyplň prosím všechny údaje.', + username: 'Špatné uživatelské jméno.', + password: 'Špatné heslo.' + }, + types: { + form: 'Vyplň prosím název typu vybavení.', + empty: 'Překladový klíč nesmí být prázdný.', + priority: 'Priorita musí být číslo >=0.' + }, + equipment: { + form: 'Vyplň prosím všechny údaje.', + url: 'Zadej platný odkaz.' }, - equipmentType: { - camera: 'Kamera', - mount: 'Montáž', - telescope: 'Dalekohled', - filter: 'Filtr', - barlow: 'Barlow', - reducer: 'Reduktor', - guidescope: 'Guidescope', - phone: 'Telefon', - focuser: 'Zaostřovač' + upload: { + missing: 'Vyber prosím soubor k nahrání.', + error: 'Nepodařilo se nahrát soubor, zkus to prosím znovu.', + invalidFile: 'Nahrej prosím platný soubor', + extension: 'Nahrej prosím platný obrázek s příponou ' + _extensions, + notFound: 'Soubor s tímto jménem neexistuje.' }, - frames: { - light: 'Light', - dark: 'Dark', - flat: 'Flat', - bias: 'Bias' + article: { + notFound: 'Článek s tímto id neexistuje.', + noImages: 'Článek musí obsahovat alespoň jeden obrázek.' } + }, + equipmentType: { + camera: 'Kamera', + mount: 'Montáž', + telescope: 'Dalekohled', + filter: 'Filtr', + barlow: 'Barlow', + reducer: 'Reduktor', + guidescope: 'Guidescope', + phone: 'Telefon', + focuser: 'Zaostřovač' + }, + frames: { + light: 'Light', + dark: 'Dark', + flat: 'Flat', + bias: 'Bias' + } } satisfies z.infer); diff --git a/src/lib/lang/english.ts b/src/lib/lang/english.ts index d963d9f..0c427bd 100644 --- a/src/lib/lang/english.ts +++ b/src/lib/lang/english.ts @@ -2,262 +2,262 @@ import type { z } from 'zod'; import lang, { _extensions } from './_template'; export default lang.parse({ - default_desc: - 'Hello, I am Patrik, a student and programmer who enjoys astrophotography in my free time. I create websites and applications using the SvelteKit framework. I am open to creating other applications, for example in NodeJS or other frameworks like Vue.js or React.', - yes: 'Yes', - no: 'No', - navigation: { - home: 'Home', - gallery: 'Gallery', - admin: 'Administration', - login: 'Login' - }, - adminNavigation: { - home: 'Dashboard', - equipment: 'Equipment', - articles: 'Articles' - }, - error: { - title: 'Error', - message: 'Oops!', - sub_message: "We don't know this page :(", - go_home: 'Back home' + default_desc: + 'Hello, I am Patrik, a student and programmer who enjoys astrophotography in my free time. I create websites and applications using the SvelteKit framework. I am open to creating other applications, for example in NodeJS or other frameworks like Vue.js or React.', + yes: 'Yes', + no: 'No', + navigation: { + home: 'Home', + gallery: 'Gallery', + admin: 'Administration', + login: 'Login' + }, + adminNavigation: { + home: 'Dashboard', + equipment: 'Equipment', + articles: 'Articles' + }, + error: { + title: 'Error', + message: 'Oops!', + sub_message: "We don't know this page :(", + go_home: 'Back home' + }, + main: { + age: 'Age', + text: [ + '🎓 I am currently studying at ', + { + text: 'VŠB – Technical University of Ostrava', + link: 'https://www.vsb.cz/' + }, + ', majoring in Computer Science at the ', + { + text: 'Faculty of Electrical Engineering and Computer Science', + link: 'https://www.fei.vsb.cz/' + }, + '.', + '%%SPACE%%', + ' 💻 In my free time, I mostly focus on developing web applications using ', + { + text: 'SvelteKit', + link: 'https://kit.svelte.dev/' + }, + ', ', + { + text: 'Tailwind CSS', + link: 'https://tailwindcss.com/' + }, + ', and ', + { + text: 'TypeScript', + link: 'https://www.typescriptlang.org/' + }, + '. I have experience with both frontend and backend, and I occasionally dive into design as well.', + '%%SPACE%%', + ' 🧠 Besides SvelteKit, I also work with other frameworks like ', + { + text: 'Vue.js', + link: 'https://vuejs.org/' + }, + ' and ', + { + text: 'React', + link: 'https://react.dev/' + }, + ', so I can adapt to different technologies.', + '%%SPACE%%', + ' 🛠️ In Node.js, I also develop various applications such as Discord bots, Twitch bots, and other scripts that simplify life.', + '%%SPACE%%', + ' 🌌 Outside of programming, I enjoy astrophotography — you can find my images in ', + { + text: 'my gallery', + link: '/en/gallery', + blank: false + }, + '!' + ] + }, + admin: { + login: { + title: 'Login', + username: 'Username', + password: 'Password', + submit: 'Login' }, main: { - age: 'Age', - text: [ - '🎓 I am currently studying at ', - { - text: 'VŠB – Technical University of Ostrava', - link: 'https://www.vsb.cz/' - }, - ', majoring in Computer Science at the ', - { - text: 'Faculty of Electrical Engineering and Computer Science', - link: 'https://www.fei.vsb.cz/' - }, - '.', - '%%SPACE%%', - ' 💻 In my free time, I mostly focus on developing web applications using ', - { - text: 'SvelteKit', - link: 'https://kit.svelte.dev/' - }, - ', ', - { - text: 'Tailwind CSS', - link: 'https://tailwindcss.com/' - }, - ', and ', - { - text: 'TypeScript', - link: 'https://www.typescriptlang.org/' - }, - '. I have experience with both frontend and backend, and I occasionally dive into design as well.', - '%%SPACE%%', - ' 🧠 Besides SvelteKit, I also work with other frameworks like ', - { - text: 'Vue.js', - link: 'https://vuejs.org/' - }, - ' and ', - { - text: 'React', - link: 'https://react.dev/' - }, - ', so I can adapt to different technologies.', - '%%SPACE%%', - ' 🛠️ In Node.js, I also develop various applications such as Discord bots, Twitch bots, and other scripts that simplify life.', - '%%SPACE%%', - ' 🌌 Outside of programming, I enjoy astrophotography — you can find my images in ', - { - text: 'my gallery', - link: '/en/gallery', - blank: false - }, - '!' - ] + stats: 'Statistics', + today: 'Today', + week: 'This Week' }, - admin: { - login: { - title: 'Login', - username: 'Username', - password: 'Password', - submit: 'Login' + equipment: { + actions: 'Actions', + types: { + title: 'Equipment Types', + addTitle: 'Editing equipment type', + translateKey: 'Translate', + priority: 'Priority', + placeholder: 'Type name', + button: 'Add Type', + success: 'New type was successfully added!', + empty: 'No equipment types have been added yet.', + editSuccess: 'Equpment type name was successfully edited!', + deleteSuccess: 'Equipment type was successfully deleted!', + edit: { + title: 'Editing equipment type id %1', + button: 'Edit' }, - main: { - stats: 'Statistics', - today: 'Today', - week: 'This Week' - }, - equipment: { - actions: 'Actions', - types: { - title: 'Equipment Types', - addTitle: 'Editing equipment type', - translateKey: 'Translate', - priority: 'Priority', - placeholder: 'Type name', - button: 'Add Type', - success: 'New type was successfully added!', - empty: 'No equipment types have been added yet.', - editSuccess: 'Equpment type name was successfully edited!', - deleteSuccess: 'Equipment type was successfully deleted!', - edit: { - title: 'Editing equipment type id %1', - button: 'Edit' - }, - delete: { - question: 'Are you sure you want to delete this type?' - } - }, - equipment: { - title: 'Equipment', - addTitle: 'Editing equipment', - type: 'Type', - name: 'Name', - link: 'Link', - button: 'Add Equipment', - success: 'New equipment was successfully added!', - empty: 'No equipment has been added yet.', - editSuccess: 'Equipment was successfully edited!', - deleteSuccess: 'Equipment was successfully deleted!', - edit: { - title: 'Editing equipment id %1', - button: 'Edit' - }, - delete: { - question: 'Are you sure you want to delete this equipment?' - } - } - }, - article: { - title: 'Articles', - create: 'Create new article', - empty: 'No articles have been created yet.', - image: 'Image', - actions: 'Actions', - articleTitle: 'Article title', - description: 'Description', - published: 'Published', - lastEdit: 'Last edit', - form: { - back: 'Back to articles', - editTitle: 'Edit article', - createTitle: 'Create article', - details: { - title: 'Article details', - titleInput: 'Article title', - titlePlaceholder: 'Enter article title', - description: 'Article description', - descriptionPlaceholder: 'Enter article description', - content: 'Article content (Markdown)', - editContent: 'Edit', - previewContent: 'Preview', - contentPlaceholder: 'Enter article content in markdown...' - }, - equipment: { - title: 'Equipment', - select: 'Select equipment', - empty: 'You have not selected any equipment.' - }, - images: { - title: 'Images', - upload: 'Upload image', - descriptionPlaceholder: 'Enter image description', - browse: 'Browse', - button: 'Add image', - empty: 'No images have been added yet.', - alt: 'Please enter image description', - noImage: 'Please select an image to upload', - multiple: 'Please select only one image', - confirmDelete: 'Are you sure you want to delete this image?' - }, - exposures: { - title: 'Exposures', - date: 'Date', - type: 'Type', - count: 'Count', - seconds: 'Seconds', - button: 'Add exposure', - time: 'Time (s)', - total: 'Total (s)', - empty: 'No exposures have been added yet.', - frames: 'Frames' - }, - cancel: 'Cancel', - save: 'Save article', - create: 'Create article', - created: 'Article was successfully created!', - updated: 'Article was successfully updated!' - } + delete: { + question: 'Are you sure you want to delete this type?' } - }, - gallery: { - title: 'Astro-Gallery', - description: 'Here you can find astrophotographs taken by me, along with descriptions and details about the exposures.', - updated: 'Updated', - created: 'Created', - readMore: 'Read more', - more: 'more', - back: 'Back to gallery', - totalExposure: 'Total exposure', - article: 'Article', - details: 'Technical details', - equipment: 'Equipment', - exposureSummary: 'Exposure summary', - framesCount: { - '1': 'frames', - '2': 'frames', - other: 'frames' + }, + equipment: { + title: 'Equipment', + addTitle: 'Editing equipment', + type: 'Type', + name: 'Name', + link: 'Link', + button: 'Add Equipment', + success: 'New equipment was successfully added!', + empty: 'No equipment has been added yet.', + editSuccess: 'Equipment was successfully edited!', + deleteSuccess: 'Equipment was successfully deleted!', + edit: { + title: 'Editing equipment id %1', + button: 'Edit' }, - exposureDetails: 'Exposure details', - equipmentDetails: 'Equipment details', - images: 'Images' + delete: { + question: 'Are you sure you want to delete this equipment?' + } + } }, - errors: { - internal: 'Internal Server Error, please try again later.', - login: { - form: 'Please fill in the form', - username: 'Invalid username', - password: 'Invalid password' - }, - types: { - form: 'Please fill eqipment type name.', - empty: 'Translation key cannot be empty.', - priority: 'Priority must be a number >=0.' + article: { + title: 'Articles', + create: 'Create new article', + empty: 'No articles have been created yet.', + image: 'Image', + actions: 'Actions', + articleTitle: 'Article title', + description: 'Description', + published: 'Published', + lastEdit: 'Last edit', + form: { + back: 'Back to articles', + editTitle: 'Edit article', + createTitle: 'Create article', + details: { + title: 'Article details', + titleInput: 'Article title', + titlePlaceholder: 'Enter article title', + description: 'Article description', + descriptionPlaceholder: 'Enter article description', + content: 'Article content (Markdown)', + editContent: 'Edit', + previewContent: 'Preview', + contentPlaceholder: 'Enter article content in markdown...' }, equipment: { - form: 'Please fill in the form', - url: 'Please enter a valid URL' + title: 'Equipment', + select: 'Select equipment', + empty: 'You have not selected any equipment.' }, - upload: { - missing: 'Please select a file to upload.', - error: 'Image upload failed, please try again later.', - invalidFile: 'Please enter a valid file', - extension: 'Please enter an image with valid extension: ' + _extensions, - notFound: 'File with this name does not exist.' + images: { + title: 'Images', + upload: 'Upload image', + descriptionPlaceholder: 'Enter image description', + browse: 'Browse', + button: 'Add image', + empty: 'No images have been added yet.', + alt: 'Please enter image description', + noImage: 'Please select an image to upload', + multiple: 'Please select only one image', + confirmDelete: 'Are you sure you want to delete this image?' }, - article: { - notFound: 'Article with this ID does not exist.', - noImages: 'Article must contain at least one image.' - } + exposures: { + title: 'Exposures', + date: 'Date', + type: 'Type', + count: 'Count', + seconds: 'Seconds', + button: 'Add exposure', + time: 'Time (s)', + total: 'Total (s)', + empty: 'No exposures have been added yet.', + frames: 'Frames' + }, + cancel: 'Cancel', + save: 'Save article', + create: 'Create article', + created: 'Article was successfully created!', + updated: 'Article was successfully updated!' + } + } + }, + gallery: { + title: 'Astro-Gallery', + description: 'Here you can find astrophotographs taken by me, along with descriptions and details about the exposures.', + updated: 'Updated', + created: 'Created', + readMore: 'Read more', + more: 'more', + back: 'Back to gallery', + totalExposure: 'Total exposure', + article: 'Article', + details: 'Technical details', + equipment: 'Equipment', + exposureSummary: 'Exposure summary', + framesCount: { + '1': 'frames', + '2': 'frames', + other: 'frames' + }, + exposureDetails: 'Exposure details', + equipmentDetails: 'Equipment details', + images: 'Images' + }, + errors: { + internal: 'Internal Server Error, please try again later.', + login: { + form: 'Please fill in the form', + username: 'Invalid username', + password: 'Invalid password' + }, + types: { + form: 'Please fill eqipment type name.', + empty: 'Translation key cannot be empty.', + priority: 'Priority must be a number >=0.' + }, + equipment: { + form: 'Please fill in the form', + url: 'Please enter a valid URL' }, - equipmentType: { - camera: 'Camera', - telescope: 'Telescope', - mount: 'Mount', - filter: 'Filter', - barlow: 'Barlow', - reducer: 'Reducer', - guidescope: 'Guidescope', - phone: 'Phone', - focuser: 'Focuser' + upload: { + missing: 'Please select a file to upload.', + error: 'Image upload failed, please try again later.', + invalidFile: 'Please enter a valid file', + extension: 'Please enter an image with valid extension: ' + _extensions, + notFound: 'File with this name does not exist.' }, - frames: { - light: 'Light', - dark: 'Dark', - flat: 'Flat', - bias: 'Bias' + article: { + notFound: 'Article with this ID does not exist.', + noImages: 'Article must contain at least one image.' } + }, + equipmentType: { + camera: 'Camera', + telescope: 'Telescope', + mount: 'Mount', + filter: 'Filter', + barlow: 'Barlow', + reducer: 'Reducer', + guidescope: 'Guidescope', + phone: 'Phone', + focuser: 'Focuser' + }, + frames: { + light: 'Light', + dark: 'Dark', + flat: 'Flat', + bias: 'Bias' + } } satisfies z.infer); diff --git a/src/lib/server/_routes/article.ts b/src/lib/server/_routes/article.ts index 1d36edc..2e4e380 100644 --- a/src/lib/server/_routes/article.ts +++ b/src/lib/server/_routes/article.ts @@ -11,266 +11,266 @@ import { FILE_FOLDER } from '$env/static/private'; import { articleSchema } from '$/types/schemes'; export default [ - loggedProcedure.POST.input(articleSchema).query(async ({ input }) => { - const trx = await conn.startTransaction().execute(); + loggedProcedure.POST.input(articleSchema).query(async ({ input }) => { + const trx = await conn.startTransaction().execute(); - try { - const uuid = v4(); + try { + const uuid = v4(); - await trx - .insertInto('article') - .values({ - id: uuid, - title: input.title, - description: input.description, - content_md: input.content_md - }) - .execute(); + await trx + .insertInto('article') + .values({ + id: uuid, + title: input.title, + description: input.description, + content_md: input.content_md + }) + .execute(); - //equipment - if (input.equipment.length > 0) { - await trx - .insertInto('article_equipment') - .values( - input.equipment.map((eq) => ({ - article_id: uuid, - equipment_id: eq - })) - ) - .execute(); - } + //equipment + if (input.equipment.length > 0) { + await trx + .insertInto('article_equipment') + .values( + input.equipment.map((eq) => ({ + article_id: uuid, + equipment_id: eq + })) + ) + .execute(); + } - //images - if (input.images.length === 0) { - return { - status: false, - code: 400, - message: 'article.noImages' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - await trx - .insertInto('gallery_image') - .values( - input.images.map((image) => ({ - ...image, - article_id: uuid - })) - ) - .execute(); + //images + if (input.images.length === 0) { + return { + status: false, + code: 400, + message: 'article.noImages' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + await trx + .insertInto('gallery_image') + .values( + input.images.map((image) => ({ + ...image, + article_id: uuid + })) + ) + .execute(); - //exposures - if (input.exposures.length > 0) { - await trx - .insertInto('exposure') - .values( - input.exposures.map((exposure) => ({ - ...exposure, - article_id: uuid - })) - ) - .execute(); - } + //exposures + if (input.exposures.length > 0) { + await trx + .insertInto('exposure') + .values( + input.exposures.map((exposure) => ({ + ...exposure, + article_id: uuid + })) + ) + .execute(); + } - await trx.commit().execute(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - await trx.rollback().execute(); + await trx.commit().execute(); + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + await trx.rollback().execute(); - return { - status: false, - code: 500, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - }), - loggedProcedure.PUT.input(articleSchema.required()).query(async ({ input }) => { - const originalData = await conn.selectFrom('article').selectAll().where('id', '=', input.id).executeTakeFirst(); - if (!originalData) { - return { - status: false, - code: 404, - message: 'article.notFound' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }), + loggedProcedure.PUT.input(articleSchema.required()).query(async ({ input }) => { + const originalData = await conn.selectFrom('article').selectAll().where('id', '=', input.id).executeTakeFirst(); + if (!originalData) { + return { + status: false, + code: 404, + message: 'article.notFound' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - const trx = await conn.startTransaction().execute(); + const trx = await conn.startTransaction().execute(); - try { - let someChanged = false; - //compare original data with input - if (originalData.title !== input.title || originalData.description !== input.description || originalData.content_md !== input.content_md) { - someChanged = true; - await trx - .updateTable('article') - .set({ - title: input.title, - description: input.description, - content_md: input.content_md - }) - .where('id', '=', input.id) - .execute(); - } + try { + let someChanged = false; + //compare original data with input + if (originalData.title !== input.title || originalData.description !== input.description || originalData.content_md !== input.content_md) { + someChanged = true; + await trx + .updateTable('article') + .set({ + title: input.title, + description: input.description, + content_md: input.content_md + }) + .where('id', '=', input.id) + .execute(); + } - //equipment - const originalEquipment = await conn.selectFrom('article_equipment').select(['equipment_id']).where('article_id', '=', input.id).execute(); - const ids = originalEquipment.map((eq) => eq.equipment_id); - const toAdd = input.equipment.filter((eq) => !ids.includes(eq)); - const toRemove = originalEquipment.filter((eq) => !input.equipment.includes(eq.equipment_id)); + //equipment + const originalEquipment = await conn.selectFrom('article_equipment').select(['equipment_id']).where('article_id', '=', input.id).execute(); + const ids = originalEquipment.map((eq) => eq.equipment_id); + const toAdd = input.equipment.filter((eq) => !ids.includes(eq)); + const toRemove = originalEquipment.filter((eq) => !input.equipment.includes(eq.equipment_id)); - if (toAdd.length > 0) { - someChanged = true; - await trx - .insertInto('article_equipment') - .values( - toAdd.map((eq) => ({ - article_id: input.id, - equipment_id: eq - })) - ) - .execute(); - } + if (toAdd.length > 0) { + someChanged = true; + await trx + .insertInto('article_equipment') + .values( + toAdd.map((eq) => ({ + article_id: input.id, + equipment_id: eq + })) + ) + .execute(); + } - if (toRemove.length > 0) { - someChanged = true; - await trx - .deleteFrom('article_equipment') - .where('article_id', '=', input.id) - .where( - 'equipment_id', - 'in', - toRemove.map((eq) => eq.equipment_id) - ) - .execute(); - } - //images - if (input.images.length === 0) { - return { - status: false, - code: 400, - message: 'article.noImages' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - // - const originalImages = await conn.selectFrom('gallery_image').select(['name']).where('article_id', '=', input.id).execute(); - const imageNames = originalImages.map((img) => img.name); - const toAddImages = input.images.filter((img) => !imageNames.includes(img.name)); - const toRemoveImages = originalImages.filter((img) => !input.images.some((_img) => _img.name === img.name)); + if (toRemove.length > 0) { + someChanged = true; + await trx + .deleteFrom('article_equipment') + .where('article_id', '=', input.id) + .where( + 'equipment_id', + 'in', + toRemove.map((eq) => eq.equipment_id) + ) + .execute(); + } + //images + if (input.images.length === 0) { + return { + status: false, + code: 400, + message: 'article.noImages' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + // + const originalImages = await conn.selectFrom('gallery_image').select(['name']).where('article_id', '=', input.id).execute(); + const imageNames = originalImages.map((img) => img.name); + const toAddImages = input.images.filter((img) => !imageNames.includes(img.name)); + const toRemoveImages = originalImages.filter((img) => !input.images.some((_img) => _img.name === img.name)); - if (toAddImages.length > 0) { - someChanged = true; - await trx - .insertInto('gallery_image') - .values( - toAddImages.map((image) => ({ - ...image, - article_id: input.id - })) - ) - .execute(); - } + if (toAddImages.length > 0) { + someChanged = true; + await trx + .insertInto('gallery_image') + .values( + toAddImages.map((image) => ({ + ...image, + article_id: input.id + })) + ) + .execute(); + } - if (toRemoveImages.length > 0) { - someChanged = true; - await trx - .deleteFrom('gallery_image') - .where('article_id', '=', input.id) - .where( - 'name', - 'in', - toRemoveImages.map((img) => img.name) - ) - .execute(); - } + if (toRemoveImages.length > 0) { + someChanged = true; + await trx + .deleteFrom('gallery_image') + .where('article_id', '=', input.id) + .where( + 'name', + 'in', + toRemoveImages.map((img) => img.name) + ) + .execute(); + } - //exposures - const originalExposures = await conn.selectFrom('exposure').select(['id']).where('article_id', '=', input.id).execute(); - const exposureIds = originalExposures.map((exp) => exp.id); - const toAddExposures = input.exposures.filter((exp) => exp.id === undefined || !exposureIds.includes(exp.id)); - const toRemoveExposures = originalExposures.filter((exp) => !input.exposures.some((_exp) => _exp.id === exp.id)); + //exposures + const originalExposures = await conn.selectFrom('exposure').select(['id']).where('article_id', '=', input.id).execute(); + const exposureIds = originalExposures.map((exp) => exp.id); + const toAddExposures = input.exposures.filter((exp) => exp.id === undefined || !exposureIds.includes(exp.id)); + const toRemoveExposures = originalExposures.filter((exp) => !input.exposures.some((_exp) => _exp.id === exp.id)); - if (toAddExposures.length > 0) { - await trx - .insertInto('exposure') - .values( - toAddExposures.map((exposure) => ({ - ...exposure, - article_id: input.id - })) - ) - .execute(); - } + if (toAddExposures.length > 0) { + await trx + .insertInto('exposure') + .values( + toAddExposures.map((exposure) => ({ + ...exposure, + article_id: input.id + })) + ) + .execute(); + } - if (toRemoveExposures.length > 0) { - await trx - .deleteFrom('exposure') - .where('article_id', '=', input.id) - .where( - 'id', - 'in', - toRemoveExposures.map((exp) => exp.id) - ) - .execute(); - } + if (toRemoveExposures.length > 0) { + await trx + .deleteFrom('exposure') + .where('article_id', '=', input.id) + .where( + 'id', + 'in', + toRemoveExposures.map((exp) => exp.id) + ) + .execute(); + } - if (someChanged) { - await trx - .updateTable('article') - .set({ - updated_at: new Date() - }) - .where('id', '=', input.id) - .execute(); - } + if (someChanged) { + await trx + .updateTable('article') + .set({ + updated_at: new Date() + }) + .where('id', '=', input.id) + .execute(); + } - await trx.commit().execute(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - await trx.rollback().execute(); + await trx.commit().execute(); + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + await trx.rollback().execute(); - return { - status: false, - code: 500, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - }), - loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { - const trx = await conn.startTransaction().execute(); - try { - //equipment - await trx.deleteFrom('article_equipment').where('article_id', '=', input).execute(); - //exposures - await trx.deleteFrom('exposure').where('article_id', '=', input).execute(); - //images - const images = await conn.selectFrom('gallery_image').select(['name']).where('article_id', '=', input).execute(); - await trx.deleteFrom('gallery_image').where('article_id', '=', input).execute(); - //remove images - const imgPaths = images.map((img) => Path.join(FILE_FOLDER, img.name)); - await Promise.all(imgPaths.map(async (path) => fs.unlink(path).catch(() => {}))); + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }), + loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { + const trx = await conn.startTransaction().execute(); + try { + //equipment + await trx.deleteFrom('article_equipment').where('article_id', '=', input).execute(); + //exposures + await trx.deleteFrom('exposure').where('article_id', '=', input).execute(); + //images + const images = await conn.selectFrom('gallery_image').select(['name']).where('article_id', '=', input).execute(); + await trx.deleteFrom('gallery_image').where('article_id', '=', input).execute(); + //remove images + const imgPaths = images.map((img) => Path.join(FILE_FOLDER, img.name)); + await Promise.all(imgPaths.map(async (path) => fs.unlink(path).catch(() => {}))); - await trx.deleteFrom('article').where('id', '=', input).execute(); + await trx.deleteFrom('article').where('id', '=', input).execute(); - await trx.commit().execute(); + await trx.commit().execute(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); - await trx.rollback().execute(); + await trx.rollback().execute(); - return { - status: false, - code: 500, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - }) + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }) ]; diff --git a/src/lib/server/_routes/equipment.ts b/src/lib/server/_routes/equipment.ts index 0bc28e1..64be128 100644 --- a/src/lib/server/_routes/equipment.ts +++ b/src/lib/server/_routes/equipment.ts @@ -7,119 +7,119 @@ import { conn } from '../variables'; import { z } from 'zod'; export default [ - loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const name = input.get('name') as string | null; - const type = input.get('type') as string | null; - const link = input.get('link') as string | null; + loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { + const name = input.get('name') as string | null; + const type = input.get('type') as string | null; + const link = input.get('link') as string | null; - if (!name || !type || !link || isNaN(Number(type))) { - return fail(400, { - status: false, - message: 'equipment.form' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!name || !type || !link || isNaN(Number(type))) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } - try { - const equipmentType = await conn.selectFrom('equipment_type').select('id').where('id', '=', Number(type)).executeTakeFirst(); - if (!equipmentType) { - return fail(400, { - status: false, - message: 'equipment.form' satisfies ErrorPath - } satisfies ActionsResponse); - } + try { + const equipmentType = await conn.selectFrom('equipment_type').select('id').where('id', '=', Number(type)).executeTakeFirst(); + if (!equipmentType) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } - await conn - .insertInto('equipment') - .values({ - name, - type_id: Number(type), - link - }) - .executeTakeFirst(); + await conn + .insertInto('equipment') + .values({ + name, + type_id: Number(type), + link + }) + .executeTakeFirst(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return fail(500, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } - }), - loggedProcedure.PATCH.input(FormDataInput).query(async ({ input }) => { - const id = input.get('id') as string | null; - const name = input.get('name') as string | null; - const type = input.get('type') as string | null; - const link = input.get('link') as string | null; + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.PATCH.input(FormDataInput).query(async ({ input }) => { + const id = input.get('id') as string | null; + const name = input.get('name') as string | null; + const type = input.get('type') as string | null; + const link = input.get('link') as string | null; - if (!id || isNaN(Number(id))) { - return fail(401, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!id || isNaN(Number(id))) { + return fail(401, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } - if (!name || !type || !link || isNaN(Number(type))) { - return fail(401, { - status: false, - message: 'equipment.form' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!name || !type || !link || isNaN(Number(type))) { + return fail(401, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } - try { - const equipmentType = await conn.selectFrom('equipment_type').select('id').where('id', '=', Number(type)).executeTakeFirst(); - if (!equipmentType) { - return fail(400, { - status: false, - message: 'equipment.form' satisfies ErrorPath - } satisfies ActionsResponse); - } + try { + const equipmentType = await conn.selectFrom('equipment_type').select('id').where('id', '=', Number(type)).executeTakeFirst(); + if (!equipmentType) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } - await conn - .updateTable('equipment') - .set({ - name, - type_id: Number(type), - link - }) - .where('id', '=', Number(id)) - .executeTakeFirst(); + await conn + .updateTable('equipment') + .set({ + name, + type_id: Number(type), + link + }) + .where('id', '=', Number(id)) + .executeTakeFirst(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return fail(500, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } - }), - loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { - if (!input || isNaN(Number(input))) { - return { - status: false, - code: 400, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { + if (!input || isNaN(Number(input))) { + return { + status: false, + code: 400, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - try { - await conn.deleteFrom('equipment').where('id', '=', Number(input)).executeTakeFirst(); + try { + await conn.deleteFrom('equipment').where('id', '=', Number(input)).executeTakeFirst(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return { - status: false, - code: 500, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - }) + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }) ]; diff --git a/src/lib/server/_routes/login.ts b/src/lib/server/_routes/login.ts index f5aa00d..069e555 100644 --- a/src/lib/server/_routes/login.ts +++ b/src/lib/server/_routes/login.ts @@ -8,45 +8,45 @@ import bcrypt from 'bcrypt'; import { COOKIE_EXPIRE } from '$env/static/private'; export default procedure.POST.input(FormDataInput).query(async ({ input, ev: { cookies } }) => { - const username = input.get('username') as string | null; - const password = input.get('password') as string | null; - - if (!username || !password) { - return fail(401, { - status: false, - message: 'login.form' satisfies ErrorPath - } satisfies ActionsResponse); - } - - const data = await conn.selectFrom('account').selectAll().where('username', '=', username).executeTakeFirst(); - - if (!data) { - return fail(401, { - status: false, - message: 'login.username' satisfies ErrorPath - } satisfies ActionsResponse); - } - - if (!bcrypt.compareSync(password, data.password)) { - return fail(401, { - status: false, - message: 'login.password' satisfies ErrorPath - } satisfies ActionsResponse); - } - - const userData = { - ...data, - password: undefined - }; - - const session = jwt.setCookie(userData); - - cookies.set('session', session, { - path: '/', - maxAge: parseInt(COOKIE_EXPIRE) - }); - - return { - status: true - } satisfies Response; + const username = input.get('username') as string | null; + const password = input.get('password') as string | null; + + if (!username || !password) { + return fail(401, { + status: false, + message: 'login.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const data = await conn.selectFrom('account').selectAll().where('username', '=', username).executeTakeFirst(); + + if (!data) { + return fail(401, { + status: false, + message: 'login.username' satisfies ErrorPath + } satisfies ActionsResponse); + } + + if (!bcrypt.compareSync(password, data.password)) { + return fail(401, { + status: false, + message: 'login.password' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const userData = { + ...data, + password: undefined + }; + + const session = jwt.setCookie(userData); + + cookies.set('session', session, { + path: '/', + maxAge: parseInt(COOKIE_EXPIRE) + }); + + return { + status: true + } satisfies Response; }); diff --git a/src/lib/server/_routes/types.ts b/src/lib/server/_routes/types.ts index f96a517..d77c880 100644 --- a/src/lib/server/_routes/types.ts +++ b/src/lib/server/_routes/types.ts @@ -7,114 +7,114 @@ import type { ErrorPath } from '$/lib/lang'; import { z } from 'zod'; export default [ - loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const lang_key = input.get('lang_key') as string | null; - const priority = input.get('priority') as string | null; + loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { + const lang_key = input.get('lang_key') as string | null; + const priority = input.get('priority') as string | null; - if (!lang_key) { - return fail(401, { - status: false, - message: 'types.form' satisfies ErrorPath - } satisfies ActionsResponse); - } - if (!priority || isNaN(Number(priority))) { - return fail(401, { - status: false, - message: 'types.priority' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!lang_key) { + return fail(401, { + status: false, + message: 'types.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + if (!priority || isNaN(Number(priority))) { + return fail(401, { + status: false, + message: 'types.priority' satisfies ErrorPath + } satisfies ActionsResponse); + } - try { - await conn - .insertInto('equipment_type') - .values({ - lang_key, - priority: Number(priority) - }) - .execute(); + try { + await conn + .insertInto('equipment_type') + .values({ + lang_key, + priority: Number(priority) + }) + .execute(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return fail(500, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } - }), - loggedProcedure.PATCH.input(FormDataInput).query(async ({ input }) => { - const id = input.get('id') as string | null; - const lang_key = input.get('lang_key') as string | null; - const priority = input.get('priority') as string | null; + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.PATCH.input(FormDataInput).query(async ({ input }) => { + const id = input.get('id') as string | null; + const lang_key = input.get('lang_key') as string | null; + const priority = input.get('priority') as string | null; - if (!id || isNaN(Number(id))) { - return fail(401, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!id || isNaN(Number(id))) { + return fail(401, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } - if (!lang_key || lang_key.trim().length === 0) { - return fail(401, { - status: false, - message: 'types.empty' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!lang_key || lang_key.trim().length === 0) { + return fail(401, { + status: false, + message: 'types.empty' satisfies ErrorPath + } satisfies ActionsResponse); + } - if (!priority || isNaN(Number(priority))) { - return fail(401, { - status: false, - message: 'types.priority' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!priority || isNaN(Number(priority))) { + return fail(401, { + status: false, + message: 'types.priority' satisfies ErrorPath + } satisfies ActionsResponse); + } - try { - await conn - .updateTable('equipment_type') - .set({ - lang_key, - priority: Number(priority) - }) - .where('id', '=', Number(id)) - .execute(); + try { + await conn + .updateTable('equipment_type') + .set({ + lang_key, + priority: Number(priority) + }) + .where('id', '=', Number(id)) + .execute(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return fail(500, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } - }), - loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { - const id = input; + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { + const id = input; - if (isNaN(Number(id))) { - return { - status: false, - code: 400, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + if (isNaN(Number(id))) { + return { + status: false, + code: 400, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - try { - await conn.deleteFrom('equipment_type').where('id', '=', Number(id)).execute(); + try { + await conn.deleteFrom('equipment_type').where('id', '=', Number(id)).execute(); - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return { - status: false, - code: 500, - message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - }) + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }) ]; diff --git a/src/lib/server/_routes/upload.ts b/src/lib/server/_routes/upload.ts index 1527352..aaa8e4a 100644 --- a/src/lib/server/_routes/upload.ts +++ b/src/lib/server/_routes/upload.ts @@ -9,64 +9,64 @@ import Path from 'node:path'; import { FILE_FOLDER } from '$env/static/private'; export default [ - loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const file = input.get('file'); + loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { + const file = input.get('file'); - if (file === null) { - return { - status: false, - code: 400, - message: 'upload.missing' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + if (file === null) { + return { + status: false, + code: 400, + message: 'upload.missing' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - if (!(file instanceof File)) { - return { - status: false, - code: 400, - message: 'upload.invalidFile' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + if (!(file instanceof File)) { + return { + status: false, + code: 400, + message: 'upload.invalidFile' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - try { - const imageName = await uploadFile(file); + try { + const imageName = await uploadFile(file); - if (!imageName) { - return { - status: false, - code: 400, - message: 'upload.invalidFile' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + if (!imageName) { + return { + status: false, + code: 400, + message: 'upload.invalidFile' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - return { - status: true, - data: imageName - } satisfies ResponseWithData; - } catch (e) { - console.error(e); - return { - status: false, - code: 500, - message: 'upload.error' satisfies ErrorPath - } satisfies ErrorApiResponse; - } - }), - loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { - const path = Path.join(FILE_FOLDER, input); + return { + status: true, + data: imageName + } satisfies ResponseWithData; + } catch (e) { + console.error(e); + return { + status: false, + code: 500, + message: 'upload.error' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }), + loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { + const path = Path.join(FILE_FOLDER, input); - if (!(await isFile(path))) { - return { - status: false, - code: 404, - message: 'upload.notFound' satisfies ErrorPath - } satisfies ErrorApiResponse; - } + if (!(await isFile(path))) { + return { + status: false, + code: 404, + message: 'upload.notFound' satisfies ErrorPath + } satisfies ErrorApiResponse; + } - await fs.unlink(path); + await fs.unlink(path); - return { - status: true - } satisfies Response; - }) + return { + status: true + } satisfies Response; + }) ]; diff --git a/src/lib/server/api.ts b/src/lib/server/api.ts index 1925c4c..8d70b39 100644 --- a/src/lib/server/api.ts +++ b/src/lib/server/api.ts @@ -6,13 +6,13 @@ export const api = new APICreate(); export const router = api.router; export const procedure = api.procedure; export const loggedProcedure = procedure.use(async ({ ctx, next }) => { - if (!ctx.logged) { - throw new MiddleWareError({ - status: false, - code: 401, - message: 'Unauthorized' - }); - } + if (!ctx.logged) { + throw new MiddleWareError({ + status: false, + code: 401, + message: 'Unauthorized' + }); + } - return next(ctx.data); + return next(ctx.data); }); diff --git a/src/lib/server/context.ts b/src/lib/server/context.ts index 45fec8b..eb254fc 100644 --- a/src/lib/server/context.ts +++ b/src/lib/server/context.ts @@ -2,7 +2,7 @@ import type { AsyncReturnType, CreateContext } from '@patrick115/sveltekitapi'; import { getUserState } from './functions'; export const context = (async ({ cookies }) => { - return getUserState(cookies); + return getUserState(cookies); }) satisfies CreateContext; export type Context = AsyncReturnType; diff --git a/src/lib/server/cookies/main.ts b/src/lib/server/cookies/main.ts index 99e9b53..1664a11 100644 --- a/src/lib/server/cookies/main.ts +++ b/src/lib/server/cookies/main.ts @@ -15,86 +15,86 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); interface cookie { - expires: number; - values: Type; + expires: number; + values: Type; } export class SessionCookies { - private db: JSONdb>; - - constructor(storage = './cookies.json') { - this.db = new JSONdb>(storage.startsWith('./') || storage.startsWith('../') ? path.join(__dirname, storage) : storage, { - syncOnWrite: true, - jsonSpaces: false, - asyncWrite: false - }); - } + private db: JSONdb>; + + constructor(storage = './cookies.json') { + this.db = new JSONdb>(storage.startsWith('./') || storage.startsWith('../') ? path.join(__dirname, storage) : storage, { + syncOnWrite: true, + jsonSpaces: false, + asyncWrite: false + }); + } + + checkCookies() { + const cookies = Object.entries(this.db.JSON()); + cookies.forEach(([key, value]) => { + if (value.expires < Date.now()) { + this.db.delete(key); + } + }); + } - checkCookies() { - const cookies = Object.entries(this.db.JSON()); - cookies.forEach(([key, value]) => { - if (value.expires < Date.now()) { - this.db.delete(key); - } - }); - } + getCookie(key: string) { + this.checkCookies(); + return this.db.get(key) as cookie; + } - getCookie(key: string) { - this.checkCookies(); - return this.db.get(key) as cookie; - } + updateCookie(id: string, value: T, age: number) { + this.checkCookies(); - updateCookie(id: string, value: T, age: number) { - this.checkCookies(); + this.db.set(id, { + expires: Date.now() + age, + values: value + }); - this.db.set(id, { - expires: Date.now() + age, - values: value - }); + return id; + } - return id; - } - - setCookie(value: T, age: number) { - this.checkCookies(); + setCookie(value: T, age: number) { + this.checkCookies(); - let id = uuid(); + let id = uuid(); - while (this.db.has(id)) { - id = uuid(); - } + while (this.db.has(id)) { + id = uuid(); + } - this.db.set(id, { - expires: Date.now() + age, - values: value - }); + this.db.set(id, { + expires: Date.now() + age, + values: value + }); - return id; - } + return id; + } - deleteCookie(key: string) { - this.checkCookies(); - this.db.delete(key); - } + deleteCookie(key: string) { + this.checkCookies(); + this.db.delete(key); + } } export class JWTCookies { - private key: string; - constructor(key: string) { - this.key = key; - } - - setCookie(value: object | string | Buffer) { - return jwt.sign(value, this.key); - } - - getCookie(token: string): T | null { - try { - return jwt.verify(token, this.key) as T; - } catch (error) { - //eslint-disable-next-line no-console - console.error('Invalid token:', error); - return null; - } + private key: string; + constructor(key: string) { + this.key = key; + } + + setCookie(value: object | string | Buffer) { + return jwt.sign(value, this.key); + } + + getCookie(token: string): T | null { + try { + return jwt.verify(token, this.key) as T; + } catch (error) { + //eslint-disable-next-line no-console + console.error('Invalid token:', error); + return null; } + } } diff --git a/src/lib/server/functions.ts b/src/lib/server/functions.ts index f26e8d5..c1b750c 100644 --- a/src/lib/server/functions.ts +++ b/src/lib/server/functions.ts @@ -12,66 +12,66 @@ import { FILE_FOLDER } from '$env/static/private'; const randomBytesAsync = promisify(crypto.randomBytes); export const getUserState = (cookies: Cookies): UserState => { - const session = cookies.get('session'); - if (!session) { - return { - logged: false - }; - } - - const data = jwt.getCookie(session); + const session = cookies.get('session'); + if (!session) { + return { + logged: false + }; + } - if (!data) { - return { - logged: false - }; - } + const data = jwt.getCookie(session); + if (!data) { return { - logged: true, - data + logged: false }; + } + + return { + logged: true, + data + }; }; type Params = Parameters; export const redirect = (status: Params[0], location: Params[1]) => { - const state = getState(); - _redirect(status, `/${state.selectedLang || 'cs'}${location}`); + const state = getState(); + _redirect(status, `/${state.selectedLang || 'cs'}${location}`); }; export const isDirectory = async (path: string) => { - try { - const stats = await fs.stat(path); - return stats.isDirectory(); - } catch { - return false; - } + try { + const stats = await fs.stat(path); + return stats.isDirectory(); + } catch { + return false; + } }; export const isFile = async (path: string) => { - try { - const stats = await fs.stat(path); - return stats.isFile(); - } catch { - return false; - } + try { + const stats = await fs.stat(path); + return stats.isFile(); + } catch { + return false; + } }; export const uploadFile = async (file: File) => { - const arrayBuffer = await file.arrayBuffer(); - const path = Path.parse(file.name); + const arrayBuffer = await file.arrayBuffer(); + const path = Path.parse(file.name); - if (!extensions.includes(path.ext.substring(1) as ImageExtension)) { - return undefined; - } + if (!extensions.includes(path.ext.substring(1) as ImageExtension)) { + return undefined; + } - const name = (await randomBytesAsync(16)).toString('hex') + path.ext; + const name = (await randomBytesAsync(16)).toString('hex') + path.ext; - if (!(await isDirectory(FILE_FOLDER))) { - await fs.mkdir(FILE_FOLDER, { recursive: true }); - } + if (!(await isDirectory(FILE_FOLDER))) { + await fs.mkdir(FILE_FOLDER, { recursive: true }); + } - await fs.writeFile(Path.join(FILE_FOLDER, name), Buffer.from(arrayBuffer)); - return name; + await fs.writeFile(Path.join(FILE_FOLDER, name), Buffer.from(arrayBuffer)); + return name; }; diff --git a/src/lib/server/routes.ts b/src/lib/server/routes.ts index bdc925f..a1eb923 100644 --- a/src/lib/server/routes.ts +++ b/src/lib/server/routes.ts @@ -6,11 +6,11 @@ import upload from './_routes/upload'; import article from './_routes/article'; export const r = router({ - login, - types, - equipment, - upload, - article + login, + types, + equipment, + upload, + article }); export type AppRouter = typeof r; diff --git a/src/lib/server/server.ts b/src/lib/server/server.ts index 2282610..9440314 100644 --- a/src/lib/server/server.ts +++ b/src/lib/server/server.ts @@ -3,7 +3,7 @@ import { context } from './context'; import { r } from './routes'; export const Server = new APIServer({ - router: r, - path: '/api', - context + router: r, + path: '/api', + context }); diff --git a/src/lib/server/variables.ts b/src/lib/server/variables.ts index daf2a69..b1f4a88 100644 --- a/src/lib/server/variables.ts +++ b/src/lib/server/variables.ts @@ -6,15 +6,15 @@ import { createPool } from 'mysql2'; export const jwt = new JWTCookies(JWT_SECRET); const dialect = new MysqlDialect({ - pool: createPool({ - host: DATABASE_IP, - port: parseInt(DATABASE_PORT), - user: DATABASE_USER, - password: DATABASE_PASSWORD, - database: DATABASE_NAME - }) + pool: createPool({ + host: DATABASE_IP, + port: parseInt(DATABASE_PORT), + user: DATABASE_USER, + password: DATABASE_PASSWORD, + database: DATABASE_NAME + }) }); export const conn = new Kysely({ - dialect + dialect }); diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index 7bf9b10..c4279f9 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -5,25 +5,25 @@ import type { UserState } from '$/types/types'; type Language = z.infer; type State = { - lang: Language; - selectedLang: string; - languages: Record< - string, - { - name: string; - flag: string; - } - >; - path: string; - userState: UserState; + lang: Language; + selectedLang: string; + languages: Record< + string, + { + name: string; + flag: string; + } + >; + path: string; + userState: UserState; }; const state = $state({}) as State; export const setState = (newState: Partial) => { - Object.assign(state, newState); + Object.assign(state, newState); }; export const getState = () => { - return state as State; + return state as State; }; diff --git a/src/params/lang.ts b/src/params/lang.ts index 3d8634d..eb36409 100644 --- a/src/params/lang.ts +++ b/src/params/lang.ts @@ -2,5 +2,5 @@ import { languages } from '$/lib/lang'; import type { ParamMatcher } from '@sveltejs/kit'; export const match = ((param: string): param is keyof typeof languages => { - return Object.keys(languages).includes(param); + return Object.keys(languages).includes(param); }) satisfies ParamMatcher; diff --git a/src/routes/[[lang=lang]]/+layout.server.ts b/src/routes/[[lang=lang]]/+layout.server.ts index 18fb5a3..921acd4 100644 --- a/src/routes/[[lang=lang]]/+layout.server.ts +++ b/src/routes/[[lang=lang]]/+layout.server.ts @@ -6,30 +6,30 @@ import { getUserState } from '$/lib/server/functions'; import { setState } from '$/lib/state.svelte'; export const load = (async ({ params, url, cookies }) => { - if (!params.lang) { - let path = url.pathname; - if (path.endsWith('/')) { - path = path.slice(0, -1); - } - - const queryParams = url.searchParams.toString(); - if (queryParams) { - path += `?${queryParams}`; - } + if (!params.lang) { + let path = url.pathname; + if (path.endsWith('/')) { + path = path.slice(0, -1); + } - redirect(302, `/cs${path}`); + const queryParams = url.searchParams.toString(); + if (queryParams) { + path += `?${queryParams}`; } - setState({ - selectedLang: params.lang as keyof typeof languages - }); + redirect(302, `/cs${path}`); + } + + setState({ + selectedLang: params.lang as keyof typeof languages + }); - const lang = params.lang as keyof typeof languages; - return { - api: Server.hydrateToClient(), - lang: languages[lang].t, - selectedLang: lang, - languageList: Object.fromEntries(Object.entries(languages).map(([code, lang]) => [code, { name: lang.name, flag: lang.flag }])), - userState: getUserState(cookies) - }; + const lang = params.lang as keyof typeof languages; + return { + api: Server.hydrateToClient(), + lang: languages[lang].t, + selectedLang: lang, + languageList: Object.fromEntries(Object.entries(languages).map(([code, lang]) => [code, { name: lang.name, flag: lang.flag }])), + userState: getUserState(cookies) + }; }) satisfies LayoutServerLoad; diff --git a/src/routes/[[lang=lang]]/+layout.svelte b/src/routes/[[lang=lang]]/+layout.svelte index 52cc729..b8d22fc 100644 --- a/src/routes/[[lang=lang]]/+layout.svelte +++ b/src/routes/[[lang=lang]]/+layout.svelte @@ -1,79 +1,79 @@
- + -
- {@render children()} -
+
+ {@render children()} +
-
+
diff --git a/src/routes/[[lang=lang]]/+page.svelte b/src/routes/[[lang=lang]]/+page.svelte index 43564d4..1689024 100644 --- a/src/routes/[[lang=lang]]/+page.svelte +++ b/src/routes/[[lang=lang]]/+page.svelte @@ -1,42 +1,42 @@
-
- - - - Profile IMG - -
-
-

Patrik Mintěl

-

{_state.lang.main.age}: {age}

-

-
+
+ + + + Profile IMG + +
+
+

Patrik Mintěl

+

{_state.lang.main.age}: {age}

+

+
diff --git a/src/routes/[[lang=lang]]/[...404]/+page.server.ts b/src/routes/[[lang=lang]]/[...404]/+page.server.ts index 1aaab86..3624518 100644 --- a/src/routes/[[lang=lang]]/[...404]/+page.server.ts +++ b/src/routes/[[lang=lang]]/[...404]/+page.server.ts @@ -1,5 +1,5 @@ import type { PageServerLoad } from './$types'; export const load = (async ({ locals }) => { - locals.is404 = true; + locals.is404 = true; }) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/[...404]/+page.svelte b/src/routes/[[lang=lang]]/[...404]/+page.svelte index e3692c2..e9082d2 100644 --- a/src/routes/[[lang=lang]]/[...404]/+page.svelte +++ b/src/routes/[[lang=lang]]/[...404]/+page.svelte @@ -1,21 +1,21 @@ - {title} - + {title} +
-

{_state.lang.error.message}

-

{_state.lang.error.sub_message}

- {_state.lang.error.go_home} +

{_state.lang.error.message}

+

{_state.lang.error.sub_message}

+ {_state.lang.error.go_home}
diff --git a/src/routes/[[lang=lang]]/admin/+layout.server.ts b/src/routes/[[lang=lang]]/admin/+layout.server.ts index a0f20af..716c24d 100644 --- a/src/routes/[[lang=lang]]/admin/+layout.server.ts +++ b/src/routes/[[lang=lang]]/admin/+layout.server.ts @@ -2,9 +2,9 @@ import { getUserState, redirect } from '$/lib/server/functions'; import type { LayoutServerLoad } from './$types'; export const load = (async ({ cookies, url }) => { - const userState = getUserState(cookies); + const userState = getUserState(cookies); - if (!userState.logged) { - redirect(302, `/login?next=${url.pathname}`); - } + if (!userState.logged) { + redirect(302, `/login?next=${url.pathname}`); + } }) satisfies LayoutServerLoad; diff --git a/src/routes/[[lang=lang]]/admin/+page.server.ts b/src/routes/[[lang=lang]]/admin/+page.server.ts index f9822f6..c31be11 100644 --- a/src/routes/[[lang=lang]]/admin/+page.server.ts +++ b/src/routes/[[lang=lang]]/admin/+page.server.ts @@ -3,36 +3,36 @@ import { sql } from 'kysely'; import type { PageServerLoad } from './$types'; export const load = (async () => { - const today = await conn - .selectFrom('visitors') - .select('id') - .where(sql`DATE(date)`, '=', sql`CURDATE()`) - .groupBy('ip') - .execute(); + const today = await conn + .selectFrom('visitors') + .select('id') + .where(sql`DATE(date)`, '=', sql`CURDATE()`) + .groupBy('ip') + .execute(); - const week = await conn - .selectFrom('visitors') - .select('id') - .where(sql`WEEK(date, 1)`, '=', sql`WEEK(CURDATE(), 1)`) - .where(sql`YEAR(date)`, '=', sql`YEAR(CURDATE())`) - .groupBy('ip') - .execute(); + const week = await conn + .selectFrom('visitors') + .select('id') + .where(sql`WEEK(date, 1)`, '=', sql`WEEK(CURDATE(), 1)`) + .where(sql`YEAR(date)`, '=', sql`YEAR(CURDATE())`) + .groupBy('ip') + .execute(); - const weekGraph = await conn - .selectFrom((eb) => - eb - .selectFrom('visitors') - .select(['ip', sql`WEEK(date, 1)`.as('WEEK'), sql`YEAR(date)`.as('YEAR')]) - .groupBy([sql`WEEK(date, 1)`, sql`YEAR(date)`, 'ip']) - .as('t') - ) - .select(['YEAR', 'WEEK', sql`COUNT(WEEK)`.as('COUNT')]) - .groupBy(['YEAR', 'WEEK']) - .execute(); + const weekGraph = await conn + .selectFrom((eb) => + eb + .selectFrom('visitors') + .select(['ip', sql`WEEK(date, 1)`.as('WEEK'), sql`YEAR(date)`.as('YEAR')]) + .groupBy([sql`WEEK(date, 1)`, sql`YEAR(date)`, 'ip']) + .as('t') + ) + .select(['YEAR', 'WEEK', sql`COUNT(WEEK)`.as('COUNT')]) + .groupBy(['YEAR', 'WEEK']) + .execute(); - return { - today: today.length, - week: week.length, - weekGraph - }; + return { + today: today.length, + week: week.length, + weekGraph + }; }) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/admin/+page.svelte b/src/routes/[[lang=lang]]/admin/+page.svelte index 81a9938..2c7dff7 100644 --- a/src/routes/[[lang=lang]]/admin/+page.svelte +++ b/src/routes/[[lang=lang]]/admin/+page.svelte @@ -1,42 +1,42 @@
-
-

{_state.lang.admin.main.stats}

-

{_state.lang.admin.main.today}: {data.today}

-

{_state.lang.admin.main.week}: {data.week}

-
- -
-
-
-

Cat of the day :)

- Random cat +
+

{_state.lang.admin.main.stats}

+

{_state.lang.admin.main.today}: {data.today}

+

{_state.lang.admin.main.week}: {data.week}

+
+
+
+
+

Cat of the day :)

+ Random cat +
diff --git a/src/routes/[[lang=lang]]/admin/article/+page.server.ts b/src/routes/[[lang=lang]]/admin/article/+page.server.ts index dec29b8..28d3f8f 100644 --- a/src/routes/[[lang=lang]]/admin/article/+page.server.ts +++ b/src/routes/[[lang=lang]]/admin/article/+page.server.ts @@ -2,26 +2,26 @@ import { conn } from '$/lib/server/variables'; import type { PageServerLoad } from './$types'; export const load = (async () => { - const previews = await conn - .selectFrom('gallery_image') - .innerJoin( - (eb) => - eb - .selectFrom('gallery_image') - .select(['article_id', conn.fn.min('id').as('min_id')]) - .groupBy('article_id') - .as('min'), - (eb) => eb.onRef('gallery_image.article_id', '=', 'min.article_id').onRef('gallery_image.id', '=', 'min.min_id') - ) - .select(['gallery_image.article_id', 'gallery_image.name', 'gallery_image.alt_text']) - .execute(); + const previews = await conn + .selectFrom('gallery_image') + .innerJoin( + (eb) => + eb + .selectFrom('gallery_image') + .select(['article_id', conn.fn.min('id').as('min_id')]) + .groupBy('article_id') + .as('min'), + (eb) => eb.onRef('gallery_image.article_id', '=', 'min.article_id').onRef('gallery_image.id', '=', 'min.min_id') + ) + .select(['gallery_image.article_id', 'gallery_image.name', 'gallery_image.alt_text']) + .execute(); - const articles = await conn.selectFrom('article').selectAll().orderBy('created_at', 'desc').execute(); + const articles = await conn.selectFrom('article').selectAll().orderBy('created_at', 'desc').execute(); - return { - articles: articles.map((article) => ({ - ...article, - preview: previews.find((preview) => preview.article_id === article.id) - })) - }; + return { + articles: articles.map((article) => ({ + ...article, + preview: previews.find((preview) => preview.article_id === article.id) + })) + }; }) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/admin/article/+page.svelte b/src/routes/[[lang=lang]]/admin/article/+page.svelte index cd9a517..e8f5db9 100644 --- a/src/routes/[[lang=lang]]/admin/article/+page.svelte +++ b/src/routes/[[lang=lang]]/admin/article/+page.svelte @@ -1,70 +1,70 @@
-
-
-

{_lang.title}

- -
- {#if data.articles.length === 0} - {_lang.empty} - {:else} -
- - - - - - - - - - - - - - {#each data.articles as article (article.id.toString())} - - - - - - - - - - {/each} - -
Id{_lang.image}{_lang.articleTitle}{_lang.description}{_lang.published}{_lang.lastEdit}{_lang.actions}
{article.id} - {#if article.preview} - {article.preview.alt_text} - {/if} - {article.title}{article.description}{formatDate(article.created_at)}{formatDate(article.updated_at)} - goto(`/${_state.selectedLang}/admin/article/${article.id}`)} name="bi-pencil-fill" class="cursor-pointer" /> - - {}} name="bi-trash-fill" class="cursor-pointer text-red-500" /> -
-
- {/if} +
+
+

{_lang.title}

+
+ {#if data.articles.length === 0} + {_lang.empty} + {:else} +
+ + + + + + + + + + + + + + {#each data.articles as article (article.id.toString())} + + + + + + + + + + {/each} + +
Id{_lang.image}{_lang.articleTitle}{_lang.description}{_lang.published}{_lang.lastEdit}{_lang.actions}
{article.id} + {#if article.preview} + {article.preview.alt_text} + {/if} + {article.title}{article.description}{formatDate(article.created_at)}{formatDate(article.updated_at)} + goto(`/${_state.selectedLang}/admin/article/${article.id}`)} name="bi-pencil-fill" class="cursor-pointer" /> + + {}} name="bi-trash-fill" class="cursor-pointer text-red-500" /> +
+
+ {/if} +
diff --git a/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts b/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts index dae0eb1..86425fd 100644 --- a/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts +++ b/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts @@ -5,53 +5,53 @@ import type { Insertable } from 'kysely'; import type { Article, Exposure, GalleryImage } from '$/types/database'; export const load = (async ({ params }) => { - const equipmentData = await conn.selectFrom('equipment').selectAll().execute(); + const equipmentData = await conn.selectFrom('equipment').selectAll().execute(); - const baseData = { - equipment: equipmentData - }; - - if (params.id === 'new') - return { - article: { - title: '', - description: '', - content_md: '', - images: [], - exposures: [], - equipment: [] - } as Insertable
& { - images: Insertable[]; - exposures: Insertable[]; - equipment: number[]; - }, - ...baseData - }; - - const article = await conn.selectFrom('article').selectAll().where('id', '=', params.id).executeTakeFirst(); - - if (!article) { - redirect(302, `/${params.lang ?? 'cs'}/admin/article`); - } - - const images = await conn.selectFrom('gallery_image').selectAll().where('article_id', '=', params.id).orderBy('id', 'asc').execute(); - - const exposures = await conn.selectFrom('exposure').selectAll().where('article_id', '=', params.id).orderBy('id', 'asc').execute(); - - const equipment = await conn - .selectFrom('article_equipment') - .select(['equipment_id']) - .where('article_equipment.article_id', '=', params.id) - .orderBy('article_equipment.id', 'asc') - .execute(); + const baseData = { + equipment: equipmentData + }; + if (params.id === 'new') return { - article: { - ...article, - images, - exposures, - equipment: equipment.map((e) => e.equipment_id) - }, - ...baseData + article: { + title: '', + description: '', + content_md: '', + images: [], + exposures: [], + equipment: [] + } as Insertable
& { + images: Insertable[]; + exposures: Insertable[]; + equipment: number[]; + }, + ...baseData }; + + const article = await conn.selectFrom('article').selectAll().where('id', '=', params.id).executeTakeFirst(); + + if (!article) { + redirect(302, `/${params.lang ?? 'cs'}/admin/article`); + } + + const images = await conn.selectFrom('gallery_image').selectAll().where('article_id', '=', params.id).orderBy('id', 'asc').execute(); + + const exposures = await conn.selectFrom('exposure').selectAll().where('article_id', '=', params.id).orderBy('id', 'asc').execute(); + + const equipment = await conn + .selectFrom('article_equipment') + .select(['equipment_id']) + .where('article_equipment.article_id', '=', params.id) + .orderBy('article_equipment.id', 'asc') + .execute(); + + return { + article: { + ...article, + images, + exposures, + equipment: equipment.map((e) => e.equipment_id) + }, + ...baseData + }; }) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte b/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte index e24dfa4..2516ef2 100644 --- a/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte +++ b/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte @@ -1,383 +1,375 @@ {#snippet subTitle(text: string)} -

{text}

+

{text}

{/snippet}
-
- - - {_lang.back} - - -

{editing ? _lang.editTitle : _lang.createTitle}

- - {@render subTitle(_lang.details.title)} - - - - - - - - {#snippet right()} -
{showPreview ? _lang.details.previewContent : _lang.details.editContent}
- {/snippet} - - {#if !showPreview} - +
From 2e8b7af831e816753414bc5fb33049f93e207a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Wed, 30 Jul 2025 22:03:28 +0200 Subject: [PATCH 135/143] Allow Input use outside the form --- src/components/newForm/Input.svelte | 31 ++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/newForm/Input.svelte b/src/components/newForm/Input.svelte index 27a8c5a..1922734 100644 --- a/src/components/newForm/Input.svelte +++ b/src/components/newForm/Input.svelte @@ -9,23 +9,44 @@ label: string; placeholder?: string; class?: string; + type?: 'text' | 'email' | 'password' | 'number'; + value?: string | number | undefined | null; }; - const { name, label, placeholder, class: cls = '' }: InputProps = $props(); - const context = getFormContext(); + let { + name, + label, + placeholder, + class: cls = '', + type, + value = $bindable('') + }: InputProps = $props(); + + const context = getFormContext(false); const id = `form-${name}`; + + let formValue = $state(context?.data[name] ?? value); + + $effect(() => { + if (context) { + context.data[name] = formValue; + } else { + value = formValue as typeof value; + } + }); - + From 9502a7d560cf0d3a13ebb42d66d7fd9f7430429b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Wed, 30 Jul 2025 22:04:35 +0200 Subject: [PATCH 136/143] Allow button use outside the form --- src/components/newForm/Button.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/newForm/Button.svelte b/src/components/newForm/Button.svelte index ac77ab2..3b9f3ff 100644 --- a/src/components/newForm/Button.svelte +++ b/src/components/newForm/Button.svelte @@ -6,13 +6,13 @@ type ButtonProps = HTMLButtonAttributes; - const { children, class: cls, ...rest }: ButtonProps = $props(); + const { children, class: cls, onclick = () => {}, ...rest }: ButtonProps = $props(); - const context = getFormContext(); + const context = getFormContext(false); + {/each} +
diff --git a/src/components/newForm/Select.svelte b/src/components/newForm/Select.svelte index ba6aac2..3929bcf 100644 --- a/src/components/newForm/Select.svelte +++ b/src/components/newForm/Select.svelte @@ -2,27 +2,62 @@ import clsx from 'clsx'; import type { HTMLSelectAttributes } from 'svelte/elements'; import { twMerge } from 'tailwind-merge'; - import { getFormContext } from './Form.svelte'; + import { getError, getFormContext, getValue, setValue } from './Form.svelte'; import FormItem from './FormItem.svelte'; + import TranslationAvailability from './TranslationAvailability.svelte'; type SelectProps = HTMLSelectAttributes & { name: string; label: string; }; - const { name, class: cls = '', children, label }: SelectProps = $props(); - const context = getFormContext(); + let { + name, + label, + class: cls = '', + value = $bindable(''), + children + }: SelectProps = $props(); + + const context = getFormContext(false); const id = `form-${name}`; + + let formValue = $state( + context + ? context.multiLang + ? context.data[context.lang.selectedLanguage][name] + : context.data[name] + : value + ); + + $effect(() => { + formValue = getValue(context, name); + }); + + $effect(() => { + if (context) { + setValue(context, name, formValue); + } else { + value = formValue as typeof value; + } + }); - + + {#snippet right()} + {#if context.multiLang} + + {/if} + {/snippet} + diff --git a/src/components/newForm/TranslationAvailability.svelte b/src/components/newForm/TranslationAvailability.svelte new file mode 100644 index 0000000..ffe6528 --- /dev/null +++ b/src/components/newForm/TranslationAvailability.svelte @@ -0,0 +1,34 @@ + + +
+ {#each _languages as [lang, langData] (lang)} + {@const error = errors[lang][path]} + {@const value = data[lang][path]} + {@const isDefaultValue = defaultValue[path] === '' || defaultValue[path] === null} + {@const isValid = + error === undefined && (isDefaultValue ? value !== defaultValue[path] : true)} +
+ + {langData.flag} +
+ {/each} +
diff --git a/src/components/utility/TranslationAvailability.svelte b/src/components/utility/TranslationAvailability.svelte deleted file mode 100644 index e043783..0000000 --- a/src/components/utility/TranslationAvailability.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - -
- {#each _languages as [lang, data] (lang)} - {@const translations = object[lang]} - {@const resolved = resolveObject(path, translations)} - {@const isValid = !validate(resolved) || resolved === ''} -
- - {data.flag} -
- {/each} -
diff --git a/src/lib/functions.ts b/src/lib/functions.ts index 22e7645..8339ea5 100644 --- a/src/lib/functions.ts +++ b/src/lib/functions.ts @@ -55,7 +55,7 @@ export const resolveObject = (path: string, _path: object) => { const parts = path.split('.'); for (const part of parts) { - if (_path[part as keyof typeof _path]) { + if (_path[part as keyof typeof _path] !== undefined) { //eslint-disable-next-line @typescript-eslint/no-explicit-any _path = _path[part as keyof typeof _path] as any; } else { From 4c99870420b14ac4d7df44c91f1490cbccdfeb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Sat, 23 Aug 2025 18:18:14 +0200 Subject: [PATCH 139/143] Removed unsupported files entry --- svelte.config.js | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/svelte.config.js b/svelte.config.js index 7aedf30..c73ea59 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -3,27 +3,22 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), + // Consult https://kit.svelte.dev/docs/integrations#preprocessors + // for more information about preprocessors + preprocess: vitePreprocess(), - kit: { - // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. - // If your environment is not supported or you settled on a specific environment, switch out the adapter. - // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter(), - alias: { - '$/*': 'src/*' - }, - csrf: { - checkOrigin: process.env.NODE_ENV === 'production' - }, - files: { - hooks: { - server: 'src/hooks.server.ts' - } - } + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter(), + alias: { + '$/*': 'src/*' + }, + csrf: { + checkOrigin: process.env.NODE_ENV === 'production' } + } }; export default config; From 04166db3ee20ec1e6327a6e4def46447e3b7cd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Sat, 23 Aug 2025 18:18:54 +0200 Subject: [PATCH 140/143] Updated packages --- package.json | 12 +- pnpm-lock.yaml | 312 +++++++++++++++++++++++-------------------------- 2 files changed, 153 insertions(+), 171 deletions(-) diff --git a/package.json b/package.json index eb3c985..87d3ea8 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@eslint/compat": "^1.2.9", "@eslint/js": "^9.26.0", "@sveltejs/adapter-node": "^5.2.12", - "@sveltejs/kit": "^2.20.8", + "@sveltejs/kit": "^2.31.1", "@sveltejs/vite-plugin-svelte": "^5.0.3", "@tailwindcss/typography": "^0.5.16", "@tailwindcss/vite": "^4.1.5", @@ -33,14 +33,14 @@ "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.3", "eslint-plugin-svelte": "^3.5.1", - "globals": "^16.1.0", + "globals": "^16.3.0", "husky": "^9.1.7", - "kysely-codegen": "^0.18.3", + "kysely-codegen": "^0.18.5", "lint-staged": "^16.1.2", "prettier": "^3.5.3", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", - "svelte": "^5.28.2", + "svelte": "^5.38.1", "svelte-check": "^4.1.7", "tailwindcss": "^4.1.5", "tsx": "^4.20.3", @@ -49,7 +49,7 @@ "vite": "^6.3.5" }, "dependencies": { - "@patrick115/sveltekitapi": "^1.2.16", + "@patrick115/sveltekitapi": "^1.3.1", "bcrypt": "^5.1.1", "bootstrap-icons": "^1.12.1", "chart.js": "^4.4.9", @@ -65,7 +65,7 @@ "sweetalert2": "^11.21.0", "tailwind-merge": "^3.2.0", "uuid": "^11.1.0", - "zod": "^3.24.4" + "zod": "^4.0.17" }, "lint-staged": { "src/**/*.{ts,svelte}": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11c651c..4cab5ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@patrick115/sveltekitapi': - specifier: ^1.2.16 - version: 1.2.16(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + specifier: ^1.3.1 + version: 1.3.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) bcrypt: specifier: ^5.1.1 version: 5.1.1 @@ -57,8 +57,8 @@ importers: specifier: ^11.1.0 version: 11.1.0 zod: - specifier: ^3.24.4 - version: 3.24.4 + specifier: ^4.0.17 + version: 4.0.17 devDependencies: '@eslint/compat': specifier: ^1.2.9 @@ -68,13 +68,13 @@ importers: version: 9.26.0 '@sveltejs/adapter-node': specifier: ^5.2.12 - version: 5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))) + version: 5.2.12(@sveltejs/kit@2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))) '@sveltejs/kit': - specifier: ^2.20.8 - version: 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + specifier: ^2.31.1 + version: 2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.3 - version: 5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + version: 5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) '@tailwindcss/typography': specifier: ^0.5.16 version: 0.5.16(tailwindcss@4.1.5) @@ -98,16 +98,16 @@ importers: version: 10.1.3(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-svelte: specifier: ^3.5.1 - version: 3.5.1(eslint@9.26.0(jiti@2.4.2))(svelte@5.28.2) + version: 3.5.1(eslint@9.26.0(jiti@2.4.2))(svelte@5.38.1) globals: - specifier: ^16.1.0 - version: 16.1.0 + specifier: ^16.3.0 + version: 16.3.0 husky: specifier: ^9.1.7 version: 9.1.7 kysely-codegen: - specifier: ^0.18.3 - version: 0.18.3(kysely@0.28.2)(mysql2@3.14.1)(typescript@5.8.3) + specifier: ^0.18.5 + version: 0.18.5(kysely@0.28.2)(mysql2@3.14.1)(typescript@5.8.3) lint-staged: specifier: ^16.1.2 version: 16.1.2 @@ -116,16 +116,16 @@ importers: version: 3.5.3 prettier-plugin-svelte: specifier: ^3.3.3 - version: 3.3.3(prettier@3.5.3)(svelte@5.28.2) + version: 3.3.3(prettier@3.5.3)(svelte@5.38.1) prettier-plugin-tailwindcss: specifier: ^0.6.11 - version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2))(prettier@3.5.3) + version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.38.1))(prettier@3.5.3) svelte: - specifier: ^5.28.2 - version: 5.28.2 + specifier: ^5.38.1 + version: 5.38.1 svelte-check: specifier: ^4.1.7 - version: 4.1.7(picomatch@4.0.2)(svelte@5.28.2)(typescript@5.8.3) + version: 4.1.7(picomatch@4.0.2)(svelte@5.38.1)(typescript@5.8.3) tailwindcss: specifier: ^4.1.5 version: 4.1.5 @@ -144,10 +144,6 @@ importers: packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -492,24 +488,28 @@ packages: cpu: [x64] os: [win32] - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@kurkle/color@0.3.4': resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} @@ -533,8 +533,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@patrick115/sveltekitapi@1.2.16': - resolution: {integrity: sha512-y5VxDyxsUjNyARX+VpNs8EvAsPu0h5AnQFOeiWNCpRi9MkcyJfqmhRpIXBb5UQ//xpQ9zIrt1mvJuj16LFgerg==} + '@patrick115/sveltekitapi@1.3.1': + resolution: {integrity: sha512-Tk50419up1pOWdm4LlXXgiO1tmfwWqqoDkZLOV4oyw9DdijDS71rr+ZRIuOMVSv8bQ4rvXEmMnrSa4hWLnjKPg==} '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -675,6 +675,9 @@ packages: cpu: [x64] os: [win32] + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@sveltejs/acorn-typescript@1.0.5': resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} peerDependencies: @@ -685,14 +688,18 @@ packages: peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/kit@2.20.8': - resolution: {integrity: sha512-ep9qTxL7WALhfm0kFecL3VHeuNew8IccbYGqv5TqL/KSqWRKzEgDG8blNlIu1CkLTTua/kHjI+f5T8eCmWIxKw==} + '@sveltejs/kit@2.31.1': + resolution: {integrity: sha512-Iv98PKh81amOjIWZ6Flqr6E7xYcrrYZ4mY9XwYUvaCDiQ4hYt5+jXR9CivcgLOTK+RcJ3K4guEYF4lFYQfTxaA==} engines: {node: '>=18.13'} hasBin: true peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true '@sveltejs/vite-plugin-svelte-inspector@4.0.1': resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} @@ -813,6 +820,9 @@ packages: '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -895,6 +905,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -1110,10 +1125,6 @@ packages: engines: {node: '>=4'} hasBin: true - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -1281,8 +1292,8 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@1.4.6: - resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==} + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -1348,10 +1359,6 @@ packages: picomatch: optional: true - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1375,10 +1382,6 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -1445,8 +1448,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@16.1.0: - resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==} + globals@16.3.0: + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} engines: {node: '>=18'} gopd@1.2.0: @@ -1503,9 +1506,6 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1620,20 +1620,21 @@ packages: known-css-properties@0.35.0: resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} - kysely-codegen@0.18.3: - resolution: {integrity: sha512-u2PFL1i8kaD+Jhcd5aIGPLgDqNriWvfWKtA7+kkvR2zZxr3DkdvT/B3nJWObZ/uj+GeONq0TChvf6mf6RqqWuA==} + kysely-codegen@0.18.5: + resolution: {integrity: sha512-bj6DMsXcKo0PrrXUk/fdjFgNC6Pwq+HPBCqhNGuD57gwUJZdci2s2OqhNneQeYpAIWGot7/481WdzTyXrClY2Q==} + engines: {node: '>=20.0.0'} hasBin: true peerDependencies: - '@libsql/kysely-libsql': ^0.3.0 || ^0.4.1 - '@tediousjs/connection-string': ^0.5.0 - better-sqlite3: '>=7.6.2' - kysely: ^0.27.0 - kysely-bun-sqlite: ^0.3.2 - kysely-bun-worker: ^0.5.3 - mysql2: ^2.3.3 || ^3.0.0 - pg: ^8.8.0 - tarn: ^3.0.0 - tedious: ^18.0.0 + '@libsql/kysely-libsql': '>=0.3.0 <0.5.0' + '@tediousjs/connection-string': '>=0.5.0 <0.6.0' + better-sqlite3: '>=7.6.2 <8.0.0' + kysely: '>=0.27.0 <1.0.0' + kysely-bun-sqlite: '>=0.3.2 <1.0.0' + kysely-bun-worker: '>=1.2.0 <2.0.0' + mysql2: '>=2.3.3 <4.0.0' + pg: '>=8.8.0 <9.0.0' + tarn: '>=3.0.0 <4.0.0' + tedious: '>=18.0.0 <20.0.0' peerDependenciesMeta: '@libsql/kysely-libsql': optional: true @@ -1908,11 +1909,6 @@ packages: node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -1922,10 +1918,6 @@ packages: encoding: optional: true - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - nopt@5.0.0: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} engines: {node: '>=6'} @@ -2381,8 +2373,8 @@ packages: svelte: optional: true - svelte@5.28.2: - resolution: {integrity: sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==} + svelte@5.38.1: + resolution: {integrity: sha512-fO6CLDfJYWHgfo6lQwkQU2vhCiHc2MBl6s3vEhK+sSZru17YL4R5s1v14ndRpqKAIkq8nCz6MTk1yZbESZWeyQ==} engines: {node: '>=18'} sweetalert2@11.21.0: @@ -2524,10 +2516,6 @@ packages: vite: optional: true - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -2577,15 +2565,13 @@ packages: peerDependencies: zod: ^3.24.1 - zod@3.24.4: - resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} -snapshots: + zod@4.0.17: + resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 +snapshots: '@babel/code-frame@7.27.1': dependencies: @@ -2817,23 +2803,32 @@ snapshots: '@img/sharp-win32-x64@0.34.2': optional: true - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 - '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@kurkle/color@0.3.4': {} '@mapbox/node-pre-gyp@1.0.11': @@ -2861,8 +2856,8 @@ snapshots: express-rate-limit: 7.5.0(express@5.1.0) pkce-challenge: 5.0.0 raw-body: 3.0.0 - zod: 3.24.4 - zod-to-json-schema: 3.24.5(zod@3.24.4) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -2878,12 +2873,12 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@patrick115/sveltekitapi@1.2.16(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + '@patrick115/sveltekitapi@1.3.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) - node-fetch: 3.3.2 - zod: 3.24.4 + '@sveltejs/kit': 2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + zod: 4.0.17 transitivePeerDependencies: + - '@opentelemetry/api' - '@sveltejs/vite-plugin-svelte' - svelte - vite @@ -2986,52 +2981,56 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true - '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.1)': + '@standard-schema/spec@1.0.0': {} + + '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': dependencies: - acorn: 8.14.1 + acorn: 8.15.0 - '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))': + '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))': dependencies: '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2) '@rollup/plugin-json': 6.1.0(rollup@4.40.2) '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2) - '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@sveltejs/kit': 2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) rollup: 4.40.2 - '@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + '@sveltejs/kit@2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) '@types/cookie': 0.6.0 + acorn: 8.15.0 cookie: 0.6.0 devalue: 5.1.1 esm-env: 1.2.2 - import-meta-resolve: 4.1.0 kleur: 4.1.5 magic-string: 0.30.17 mrmime: 2.0.1 sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.1 - svelte: 5.28.2 + svelte: 5.38.1 vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) debug: 4.4.0 - svelte: 5.28.2 + svelte: 5.38.1 vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.28.2)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 - svelte: 5.28.2 + svelte: 5.38.1 vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) vitefu: 1.0.6(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) transitivePeerDependencies: @@ -3118,6 +3117,8 @@ snapshots: '@types/estree@1.0.7': {} + '@types/estree@1.0.8': {} + '@types/json-schema@7.0.15': {} '@types/jsonwebtoken@9.0.9': @@ -3225,6 +3226,8 @@ snapshots: acorn@8.14.1: {} + acorn@8.15.0: {} + agent-base@6.0.2: dependencies: debug: 4.4.0 @@ -3428,8 +3431,6 @@ snapshots: cssesc@3.0.0: {} - data-uri-to-buffer@4.0.1: {} - debug@4.4.0: dependencies: ms: 2.1.3 @@ -3537,7 +3538,7 @@ snapshots: dependencies: eslint: 9.26.0(jiti@2.4.2) - eslint-plugin-svelte@3.5.1(eslint@9.26.0(jiti@2.4.2))(svelte@5.28.2): + eslint-plugin-svelte@3.5.1(eslint@9.26.0(jiti@2.4.2))(svelte@5.38.1): dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) '@jridgewell/sourcemap-codec': 1.5.0 @@ -3548,9 +3549,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.3) postcss-safe-parser: 7.0.1(postcss@8.5.3) semver: 7.7.1 - svelte-eslint-parser: 1.1.3(svelte@5.28.2) + svelte-eslint-parser: 1.1.3(svelte@5.38.1) optionalDependencies: - svelte: 5.28.2 + svelte: 5.38.1 transitivePeerDependencies: - ts-node @@ -3601,7 +3602,7 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 - zod: 3.24.4 + zod: 3.25.76 optionalDependencies: jiti: 2.4.2 transitivePeerDependencies: @@ -3619,9 +3620,9 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.4.6: + esrap@2.1.0: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 esrecurse@4.3.0: dependencies: @@ -3701,11 +3702,6 @@ snapshots: optionalDependencies: picomatch: 4.0.2 - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -3737,10 +3733,6 @@ snapshots: flatted@3.3.3: {} - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - forwarded@0.2.0: {} fresh@2.0.0: {} @@ -3823,7 +3815,7 @@ snapshots: globals@14.0.0: {} - globals@16.1.0: {} + globals@16.3.0: {} gopd@1.2.0: {} @@ -3871,8 +3863,6 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-meta-resolve@4.1.0: {} - imurmurhash@0.1.4: {} inflight@1.0.6: @@ -3922,7 +3912,7 @@ snapshots: is-reference@3.0.3: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 isexe@2.0.0: {} @@ -3974,7 +3964,7 @@ snapshots: known-css-properties@0.35.0: {} - kysely-codegen@0.18.3(kysely@0.28.2)(mysql2@3.14.1)(typescript@5.8.3): + kysely-codegen@0.18.5(kysely@0.28.2)(mysql2@3.14.1)(typescript@5.8.3): dependencies: chalk: 4.1.2 cosmiconfig: 9.0.0(typescript@5.8.3) @@ -3985,7 +3975,7 @@ snapshots: micromatch: 4.0.8 minimist: 1.2.8 pluralize: 8.0.0 - zod: 3.24.4 + zod: 3.25.76 optionalDependencies: mysql2: 3.14.1 transitivePeerDependencies: @@ -4199,18 +4189,10 @@ snapshots: node-addon-api@5.1.0: {} - node-domexception@1.0.0: {} - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - nopt@5.0.0: dependencies: abbrev: 1.1.1 @@ -4323,16 +4305,16 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2): + prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.38.1): dependencies: prettier: 3.5.3 - svelte: 5.28.2 + svelte: 5.38.1 - prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2))(prettier@3.5.3): + prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.38.1))(prettier@3.5.3): dependencies: prettier: 3.5.3 optionalDependencies: - prettier-plugin-svelte: 3.3.3(prettier@3.5.3)(svelte@5.28.2) + prettier-plugin-svelte: 3.3.3(prettier@3.5.3)(svelte@5.38.1) prettier@3.5.3: {} @@ -4622,19 +4604,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.1.7(picomatch@4.0.2)(svelte@5.28.2)(typescript@5.8.3): + svelte-check@4.1.7(picomatch@4.0.2)(svelte@5.38.1)(typescript@5.8.3): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.3 fdir: 6.4.4(picomatch@4.0.2) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.28.2 + svelte: 5.38.1 typescript: 5.8.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.1.3(svelte@5.28.2): + svelte-eslint-parser@1.1.3(svelte@5.38.1): dependencies: eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -4643,20 +4625,20 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.3) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.28.2 + svelte: 5.38.1 - svelte@5.28.2: + svelte@5.38.1: dependencies: - '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.5.0 - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1) - '@types/estree': 1.0.7 - acorn: 8.14.1 + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 aria-query: 5.3.2 axobject-query: 4.1.0 clsx: 2.1.1 esm-env: 1.2.2 - esrap: 1.4.6 + esrap: 2.1.0 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.17 @@ -4764,8 +4746,6 @@ snapshots: optionalDependencies: vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) - web-streams-polyfill@3.3.3: {} - webidl-conversions@3.0.1: {} whatwg-url@5.0.0: @@ -4801,8 +4781,10 @@ snapshots: zimmerframe@1.1.2: {} - zod-to-json-schema@3.24.5(zod@3.24.4): + zod-to-json-schema@3.24.5(zod@3.25.76): dependencies: - zod: 3.24.4 + zod: 3.25.76 + + zod@3.25.76: {} - zod@3.24.4: {} + zod@4.0.17: {} From 601ad4cee5589a6adc3c08dc89e46b5882db6073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Sat, 23 Aug 2025 18:43:16 +0200 Subject: [PATCH 141/143] Finished components --- src/components/form/Select.svelte | 11 +- src/components/form/index.ts | 6 + src/components/headers/index.ts | 3 + src/components/newForm/Button.svelte | 14 +- src/components/newForm/DataProvider.svelte | 28 ++++ src/components/newForm/DatePicker.svelte | 41 ++--- src/components/newForm/Form.svelte | 155 +++++++++++------- src/components/newForm/Input.svelte | 40 ++--- src/components/newForm/Markdown.svelte | 36 ++++ src/components/newForm/Select.svelte | 43 +++-- src/components/newForm/TextArea.svelte | 48 +++--- .../newForm/TranslationAvailability.svelte | 5 +- src/components/newForm/index.ts | 11 ++ src/components/table/index.ts | 6 + src/components/utility/index.ts | 10 ++ src/types/types.ts | 7 +- 16 files changed, 298 insertions(+), 166 deletions(-) create mode 100644 src/components/form/index.ts create mode 100644 src/components/headers/index.ts create mode 100644 src/components/newForm/DataProvider.svelte create mode 100644 src/components/newForm/Markdown.svelte create mode 100644 src/components/newForm/index.ts create mode 100644 src/components/table/index.ts create mode 100644 src/components/utility/index.ts diff --git a/src/components/form/Select.svelte b/src/components/form/Select.svelte index b43a0da..9ceda8b 100644 --- a/src/components/form/Select.svelte +++ b/src/components/form/Select.svelte @@ -3,15 +3,22 @@ import type { HTMLSelectAttributes } from 'svelte/elements'; import { twMerge } from 'tailwind-merge'; - type SelectProps = HTMLSelectAttributes; + type SelectProps = HTMLSelectAttributes & { error?: string }; - let { children, class: cls, value = $bindable(), ...props }: SelectProps = $props(); + let { + children, + class: cls, + value = $bindable(), + error, + ...props + }: SelectProps = $props();
diff --git a/src/components/newForm/Form.svelte b/src/components/newForm/Form.svelte index f1bd72b..81514cf 100644 --- a/src/components/newForm/Form.svelte +++ b/src/components/newForm/Form.svelte @@ -6,26 +6,25 @@ type Errors = Record; type BaseFormContext = { - defaultData: Record; onSubmitHandler: () => void; + multiLangKeys: string[]; + data: Record; + defaultData: Record; + errors: () => Errors; }; export type MultiLangFormContext = BaseFormContext & { - data: Record>; multiLang: true; lang: { selectedLanguage: Language; }; - errors: () => Record; }; export type NonMultiLangFormContext = BaseFormContext & { - data: Record; multiLang: false; lang: { selectedLanguage: Language; }; - errors: () => Errors; }; type FormContext = MultiLangFormContext | NonMultiLangFormContext; @@ -42,26 +41,35 @@ export const getError = (context: FormContext, name: string) => { if (context.multiLang) { - return context.errors()[context.lang.selectedLanguage][name]; - } else { - return context.errors()[name]; + //eslint-disable-next-line @typescript-eslint/no-explicit-any + const errors = context.errors() as any; + if (context.multiLangKeys.includes(name)) { + return (errors[context.lang.selectedLanguage] as Errors)[name]; + } } + return context.errors()[name]; }; export const setValue = (context: FormContext, name: string, value: unknown) => { if (context.multiLang) { - context.data[context.lang.selectedLanguage][name] = value; - } else { - context.data[name] = value; + if (context.multiLangKeys.includes(name)) { + (context.data[context.lang.selectedLanguage] as Record)[name] = + value; + return; + } } + context.data[name] = value; }; export const getValue = (context: FormContext, name: string) => { if (context.multiLang) { - return context.data[context.lang.selectedLanguage][name]; - } else { - return context.data[name]; + if (context.multiLangKeys.includes(name)) { + return (context.data[context.lang.selectedLanguage] as Record)[ + name + ]; + } } + return context.data[name]; }; type ObjectToErrorObject< @@ -77,10 +85,11 @@ [$Key in keyof $Object]?: string | undefined; }; - export type FormFunction< + export type FormAction< //eslint-disable-next-line @typescript-eslint/no-explicit-any $Type extends SubmitFunction, - $ZodSchema extends ZodSchema, + //eslint-disable-next-line @typescript-eslint/no-explicit-any + $ZodSchema extends z.ZodType, $MultiLang extends boolean > = $Type extends SubmitFunction @@ -103,7 +112,7 @@ - + {#snippet right()} + {#if _right} + {@render _right()} + {/if} {#if context.multiLang} {/if} {/snippet} - diff --git a/src/components/newForm/Markdown.svelte b/src/components/newForm/Markdown.svelte new file mode 100644 index 0000000..f5a8cfe --- /dev/null +++ b/src/components/newForm/Markdown.svelte @@ -0,0 +1,36 @@ + + + + {#snippet right()} + {#if _right} + {@render _right()} + {/if} + {/snippet} + + + diff --git a/src/components/newForm/Select.svelte b/src/components/newForm/Select.svelte index 3929bcf..4b2ef09 100644 --- a/src/components/newForm/Select.svelte +++ b/src/components/newForm/Select.svelte @@ -1,66 +1,61 @@ - + {#snippet right()} + {#if _right} + {@render _right()} + {/if} {#if context.multiLang} {/if} {/snippet} - + diff --git a/src/components/newForm/TextArea.svelte b/src/components/newForm/TextArea.svelte index b9a74e4..8ad2b0c 100644 --- a/src/components/newForm/TextArea.svelte +++ b/src/components/newForm/TextArea.svelte @@ -2,15 +2,17 @@ import clsx from 'clsx'; import { untrack } from 'svelte'; import type { SvelteHTMLElements } from 'svelte/elements'; - import { twMerge } from 'tailwind-merge'; + import BaseTextArea from '../form/TextArea.svelte'; import { getError, getFormContext } from './Form.svelte'; import FormItem from './FormItem.svelte'; import TranslationAvailability from './TranslationAvailability.svelte'; + import type { Snippet } from 'svelte'; type TextAreaProps = SvelteHTMLElements['textarea'] & { name: string; label: string; - value?: string | undefined | null; + variant?: 'small' | 'normal'; + right?: Snippet; }; let { @@ -18,30 +20,26 @@ label, class: cls = '', placeholder, - value = $bindable('') + variant, + right: _right }: TextAreaProps = $props(); - const context = getFormContext(false); + const context = getFormContext(); const id = `form-${name}`; - let formValue = $state( - context - ? context.multiLang - ? context.data[context.lang.selectedLanguage][name] - : context.data[name] - : value + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let formValue = $state( + context.multiLang + ? context.data[context.lang.selectedLanguage][name] + : context.data[name] ); $effect(() => { - if (context) { - if (context.multiLang) { - context.data[untrack(() => context.lang.selectedLanguage)][name] = formValue; - } else { - context.data[name] = formValue; - } + if (context.multiLang) { + context.data[untrack(() => context.lang.selectedLanguage)][name] = formValue; } else { - value = formValue as typeof value; + context.data[name] = formValue; } }); @@ -57,22 +55,22 @@ }); - + {#snippet right()} + {#if _right} + {@render _right()} + {/if} {#if context.multiLang} {/if} {/snippet} - + error={getError(context, name)} + class={clsx(cls)} + /> diff --git a/src/components/newForm/TranslationAvailability.svelte b/src/components/newForm/TranslationAvailability.svelte index ffe6528..d744226 100644 --- a/src/components/newForm/TranslationAvailability.svelte +++ b/src/components/newForm/TranslationAvailability.svelte @@ -20,9 +20,10 @@ {#each _languages as [lang, langData] (lang)} {@const error = errors[lang][path]} {@const value = data[lang][path]} - {@const isDefaultValue = defaultValue[path] === '' || defaultValue[path] === null} + {@const isDefaultValue = + defaultValue[lang][path] === '' || defaultValue[lang][path] === null} {@const isValid = - error === undefined && (isDefaultValue ? value !== defaultValue[path] : true)} + error === undefined && (isDefaultValue ? value !== defaultValue[lang][path] : true)}
= { class?: ClassValue; @@ -60,3 +60,6 @@ export type Path< >; }[keyof T] : P; + +//eslint-disable-next-line @typescript-eslint/no-explicit-any +export const NROT = <$Type extends readonly any[]>(array: $Type) => array as [...$Type]; From 0e3fe7be7d4dad072ac160f0b3a80169ee19862b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Mint=C4=9Bl?= Date: Sat, 23 Aug 2025 19:16:07 +0200 Subject: [PATCH 142/143] Cleanup --- src/components/newForm/Button.svelte | 4 +- src/components/newForm/DataProvider.svelte | 14 +- src/components/newForm/DatePicker.svelte | 8 +- src/components/newForm/Form.svelte | 150 ++++++++---------- src/components/newForm/FormItem.svelte | 13 +- src/components/newForm/Input.svelte | 8 +- .../newForm/LanguageSelector.svelte | 6 +- src/components/newForm/Markdown.svelte | 4 +- src/components/newForm/Select.svelte | 12 +- src/components/newForm/TextArea.svelte | 21 ++- .../newForm/TranslationAvailability.svelte | 18 ++- 11 files changed, 114 insertions(+), 144 deletions(-) diff --git a/src/components/newForm/Button.svelte b/src/components/newForm/Button.svelte index c7658ea..e9924b4 100644 --- a/src/components/newForm/Button.svelte +++ b/src/components/newForm/Button.svelte @@ -1,9 +1,9 @@
diff --git a/src/components/newForm/Input.svelte b/src/components/newForm/Input.svelte index 16b25b2..00b33bc 100644 --- a/src/components/newForm/Input.svelte +++ b/src/components/newForm/Input.svelte @@ -11,7 +11,7 @@ label: string; placeholder?: string; class?: string; - type?: 'text' | 'email' | 'password' | 'number'; + type?: 'text' | 'email' | 'password'; variant?: 'small' | 'normal'; right?: Snippet; }; @@ -27,11 +27,9 @@ }: InputProps = $props(); const context = getFormContext(); - const id = `form-${name}`; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let formValue = $state(getValue(context, name)); + let formValue = $state(getValue(context, name)); $effect(() => { formValue = getValue(context, name); @@ -56,7 +54,7 @@ {id} {name} {type} - bind:value={formValue} + bind:value={formValue as string} {placeholder} error={getError(context, name)} class={clsx(cls)} diff --git a/src/components/newForm/LanguageSelector.svelte b/src/components/newForm/LanguageSelector.svelte index bbd4519..bb44443 100644 --- a/src/components/newForm/LanguageSelector.svelte +++ b/src/components/newForm/LanguageSelector.svelte @@ -6,12 +6,14 @@ import Button from './Button.svelte'; import { getFormContext } from './Form.svelte'; - const _state = getState(); const context = getFormContext();
-

{_state.lang.language}:

+

+ + {getState().lang.language}: +

{#each Object.entries(languages) as [lang, data] (lang)} + {/snippet} + +
+ {#each images as img, idx (img.name)} +
+ Preview +
+ {#each Object.entries(languages) as [langKey, langInfo] (langKey)} + {@const inputId = `${name}-${idx}-${langKey}`} +
+ + + setAltText(idx, langKey, e.currentTarget.value)} + placeholder="Description / Alt Text" + /> +
+ {/each} +
+ +
+ {/each} + + {#if images.length === 0} +
No images selected
+ {/if} +
+ diff --git a/src/components/newForm/index.ts b/src/components/newForm/index.ts index f5c9e03..32c4779 100644 --- a/src/components/newForm/index.ts +++ b/src/components/newForm/index.ts @@ -3,6 +3,7 @@ export { default as DataProvider } from './DataProvider.svelte'; export { default as DatePicker } from './DatePicker.svelte'; export { default as Form } from './Form.svelte'; export { default as FormItem } from './FormItem.svelte'; +export { default as ImageManager } from './ImageManager.svelte'; export { default as Input } from './Input.svelte'; export { default as LanguageSelector } from './LanguageSelector.svelte'; export { default as Markdown } from './Markdown.svelte'; diff --git a/src/fonts.css b/src/fonts.css index e3a1819..021248d 100644 --- a/src/fonts.css +++ b/src/fonts.css @@ -1,2322 +1,2843 @@ /* vietnamese */ @font-face { - font-family: 'Ephesis'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xvR38yH.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Ephesis'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xvR38yH.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Ephesis'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xrR38yH.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ephesis'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xrR38yH.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ephesis'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xTR3w.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ephesis'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xTR3w.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tDMPKzSQ.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tDMPKzSQ.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tMMPKzSQ.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tMMPKzSQ.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tCMPI.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tCMPI.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 200; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 200; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 200; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLucXtAKPY.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLucXtAKPY.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLufntAKPY.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLufntAKPY.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLucHtA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLucHtA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVFteOcEg.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVGdeOcEg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVGdeOcEg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: italic; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVF9eO.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVF9eO.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTucXtAKPY.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTucXtAKPY.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTucHtA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTucHtA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 200; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 200; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 200; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJbecmNE.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJbecmNE.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJnecmNE.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJfecg.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* devanagari */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09; + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; } /* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z1JlFc-K.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z1JlFc-K.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z1xlFQ.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z1xlFQ.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: italic; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* math */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) format('woff2'); - unicode-range: - U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, - U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, - U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, - U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, - U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; } /* symbols */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) format('woff2'); - unicode-range: - U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, - U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, - U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, - U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, - U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, - U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, - U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, - U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, - U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, - U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.woff2) + format('woff2'); + unicode-range: + U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, + U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 900; - font-stretch: 100%; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyCN4Ffgg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyCN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyLN4Ffgg.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyLN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyDN4Ffgg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyDN4Ffgg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyMN4Ffgg.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyMN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyBN4Ffgg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyBN4Ffgg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyPN4E.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyPN4E.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej75l0mwFg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej75l0mwFg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej7wl0mwFg.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej7wl0mwFg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej74l0mwFg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej74l0mwFg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej73l0mwFg.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej73l0mwFg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej76l0mwFg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej76l0mwFg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej70l0k.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej70l0k.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyCN4Ffgg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyCN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyLN4Ffgg.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyLN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyDN4Ffgg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyDN4Ffgg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyMN4Ffgg.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyMN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyBN4Ffgg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyBN4Ffgg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyPN4E.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyPN4E.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyCN4Ffgg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyCN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyLN4Ffgg.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyLN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyDN4Ffgg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyDN4Ffgg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyMN4Ffgg.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyMN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyBN4Ffgg.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyBN4Ffgg.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyPN4E.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyPN4E.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvWyNL4U.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjtGyNL4U.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvGyNL4U.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1Czjs2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1Czjs2yNL4U.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvmyNL4U.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvmyNL4U.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjsGyN.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjsGyN.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcg72j00.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcg72j00.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKew72j00.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKew72j00.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcw72j00.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcw72j00.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfA72j00.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfA72j00.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcQ72j00.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcQ72j00.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfw72.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfw72.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvWyNL4U.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jtGyNL4U.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvGyNL4U.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3js2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3js2yNL4U.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvmyNL4U.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvmyNL4U.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jsGyN.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jsGyN.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvWyNL4U.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjtGyNL4U.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvGyNL4U.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjs2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjs2yNL4U.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* latin-ext */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvmyNL4U.woff2) format('woff2'); - unicode-range: - U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvmyNL4U.woff2) + format('woff2'); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, + U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, + U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjsGyN.woff2) format('woff2'); - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjsGyN.woff2) + format('woff2'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, + U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } diff --git a/src/hooks.server.ts b/src/hooks.server.ts index c27632d..b51551c 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -9,7 +9,12 @@ export const handle = (async ({ event, resolve }) => { const UA = event.request.headers.get('user-agent') || ''; const isBot = /bot|crawl|spider|slurp/i.test(UA); - if (!isBot && response.status === 200 && !event.locals.is404 && !disallowedPaths.some((p) => path.startsWith(p))) { + if ( + !isBot && + response.status === 200 && + !event.locals.is404 && + !disallowedPaths.some((p) => path.startsWith(p)) + ) { conn .insertInto('visitors') .values({ diff --git a/src/lib/lang/_template.ts b/src/lib/lang/_template.ts index 8740730..8affa26 100644 --- a/src/lib/lang/_template.ts +++ b/src/lib/lang/_template.ts @@ -46,7 +46,8 @@ export default o({ title: _, message: _, sub_message: _, - go_home: _ + go_home: _, + invalid_type_number: _ }), main: o({ age: _, @@ -227,7 +228,11 @@ export default o({ }), article: o({ notFound: _, - noImages: _ + noImages: _, + noTitle: _, + noDescrption: _, + noContent: _, + noAltText: _ }) }), equipmentType: o({ diff --git a/src/lib/lang/czech.ts b/src/lib/lang/czech.ts index 36c4909..ad84cdc 100644 --- a/src/lib/lang/czech.ts +++ b/src/lib/lang/czech.ts @@ -24,7 +24,8 @@ export default lang.parse({ title: 'Chyba', message: 'Oops!', sub_message: 'Tuto stránku neznáme :(', - go_home: 'Zpátky domů' + go_home: 'Zpátky domů', + invalid_type_number: 'Zadejte platné číslo.' }, main: { age: 'Věk', @@ -265,7 +266,11 @@ export default lang.parse({ }, article: { notFound: 'Článek s tímto id neexistuje.', - noImages: 'Článek musí obsahovat alespoň jeden obrázek.' + noImages: 'Článek musí obsahovat alespoň jeden obrázek.', + noTitle: 'Musíš zadat název článku.', + noDescrption: 'Musíš zadat popisek článku.', + noContent: 'Musíš zadat obsah článku.', + noAltText: 'Musíš zadat popisek obrázku.' } }, equipmentType: { diff --git a/src/lib/lang/english.ts b/src/lib/lang/english.ts index 2eb0c1f..f26488b 100644 --- a/src/lib/lang/english.ts +++ b/src/lib/lang/english.ts @@ -24,7 +24,8 @@ export default lang.parse({ title: 'Error', message: 'Oops!', sub_message: "We don't know this page :(", - go_home: 'Back home' + go_home: 'Back home', + invalid_type_number: 'Please enter a valid number.' }, main: { age: 'Age', @@ -265,7 +266,11 @@ export default lang.parse({ }, article: { notFound: 'Article with this ID does not exist.', - noImages: 'Article must contain at least one image.' + noImages: 'Article must contain at least one image.', + noTitle: 'You need to enter article title.', + noDescrption: 'You need to enter article description.', + noContent: 'You need to enter article content.', + noAltText: 'You need to enter image description.' } }, equipmentType: { diff --git a/src/lib/lang/index.ts b/src/lib/lang/index.ts index ab1d69c..5473693 100644 --- a/src/lib/lang/index.ts +++ b/src/lib/lang/index.ts @@ -1,10 +1,10 @@ import type { Path } from '$/types/types'; +import type { z } from 'zod'; import { resolveObject } from '../functions'; import type { GatheredTranslationsAll } from '../server/functions'; import template, { languagable } from './_template'; import czech from './czech'; import english from './english'; -import type { z } from 'zod'; export const languages = { cs: { @@ -37,7 +37,7 @@ export const compareErrors = (a: string, b: ErrorPath) => { }; export const resolveError = (error: string, lang: z.infer) => { - return resolveObject(error, lang.error); + return resolveObject(error, lang.errors); }; export const resolveTranslation = ( @@ -82,17 +82,44 @@ export const resolveLanguagable = ( return translate.other; }; +type TransformIntoLanguagableReturn<$Object, $Keys extends keyof $Object> = Record< + keyof typeof languages, + Pick<$Object, $Keys> +> & + Omit<$Object, $Keys>; //Resolve arrays and sub objects -export const transformIntoLanguagable = <$Object extends object>( +export const transformIntoLanguagable = < + $Object extends object, + $Keys extends keyof $Object +>( object: $Object, - gathered: GatheredTranslationsAll -): Record => { - const result: Record = {}; + gathered: GatheredTranslationsAll, + languageKeys: $Keys[] +): TransformIntoLanguagableReturn<$Object, $Keys> => { + //eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = {} as any; + + //Fill the non language keys + for (const key in object) { + //@ts-expect-error Here we put string into .includes, which is fine + if (languageKeys.includes(key)) continue; + + result[key] = object[key]; + } for (const _lang in languages) { const lang = _lang as keyof typeof languages; const baseObject = structuredClone(object); + + //delete non-language keys from base object + for (const key in baseObject) { + //@ts-expect-error Here we put string into .includes, which is fine + if (!languageKeys.includes(key)) { + delete baseObject[key]; + } + } + const currentLang = gathered[lang]; const translateObject = (obj: Record) => { @@ -119,7 +146,9 @@ export const transformIntoLanguagable = <$Object extends object>( const translated = currentLang[value]; if (!translated) { - obj[key] = ''; + // If translation is not found, keep the original value + // This allows non-translatable fields (like UUIDs for images) to persist + obj[key] = value; continue; } obj[key] = translated; diff --git a/src/lib/server/_routes/article.ts b/src/lib/server/_routes/article.ts index 2e4e380..8ef3b2e 100644 --- a/src/lib/server/_routes/article.ts +++ b/src/lib/server/_routes/article.ts @@ -1,29 +1,42 @@ +import { languages, type ErrorPath } from '$/lib/lang'; +import { articleSchema as _articleSchema } from '$/types/schemes'; +import type { ActionsResponse, Response } from '$/types/types'; +import { FILE_FOLDER } from '$env/static/private'; +import { AnyFormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; +import { fail } from '@sveltejs/kit'; import fs from 'node:fs/promises'; import Path from 'node:path'; +import { v4 } from 'uuid'; import { z } from 'zod'; import { loggedProcedure } from '../api'; +import { insertTranslations, parseFormData } from '../functions'; import { conn } from '../variables'; -import type { Response } from '$/types/types'; -import { v4 } from 'uuid'; -import type { ErrorPath } from '$/lib/lang'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { FILE_FOLDER } from '$env/static/private'; -import { articleSchema } from '$/types/schemes'; + +const articleSchema = _articleSchema('cs'); export default [ - loggedProcedure.POST.input(articleSchema).query(async ({ input }) => { + loggedProcedure.POST.input(AnyFormDataInput).query(async ({ input: _input }) => { + const parsed = parseFormData(_input, articleSchema); + const input = parsed.cs; + const trx = await conn.startTransaction().execute(); try { + const translations = await insertTranslations(trx, parsed, [ + 'title', + 'description', + 'content_md' + ]); + const uuid = v4(); await trx .insertInto('article') .values({ id: uuid, - title: input.title, - description: input.description, - content_md: input.content_md + title: translations.title, + description: translations.description, + content_md: translations.content_md }) .execute(); @@ -42,21 +55,38 @@ export default [ //images if (input.images.length === 0) { - return { + return fail(400, { status: false, - code: 400, message: 'article.noImages' satisfies ErrorPath - } satisfies ErrorApiResponse; + } satisfies ActionsResponse); + } + + const languagesKeys = Object.keys(languages); + const imagesToInsert = []; + + for (let i = 0; i < input.images.length; i++) { + const image = input.images[i]; + + const transObject: Record = {}; + for (const lang of languagesKeys) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const langData = parsed[lang] as any; + transObject[lang] = { alt_text: langData?.images?.[i]?.alt_text ?? '' }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const transResult = await insertTranslations(trx, transObject as any, [ + 'alt_text' + ]); + + imagesToInsert.push({ + article_id: uuid, + name: image.name, + alt_text: transResult.alt_text + }); } - await trx - .insertInto('gallery_image') - .values( - input.images.map((image) => ({ - ...image, - article_id: uuid - })) - ) - .execute(); + + await trx.insertInto('gallery_image').values(imagesToInsert).execute(); //exposures if (input.exposures.length > 0) { @@ -79,46 +109,70 @@ export default [ console.error(err); await trx.rollback().execute(); - return { + return fail(500, { status: false, - code: 500, message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; + } satisfies ActionsResponse); } }), - loggedProcedure.PUT.input(articleSchema.required()).query(async ({ input }) => { - const originalData = await conn.selectFrom('article').selectAll().where('id', '=', input.id).executeTakeFirst(); + loggedProcedure.PUT.input(AnyFormDataInput).query(async ({ input: _input }) => { + const parsed = parseFormData(_input, articleSchema); + const input = parsed.cs; + + if (!input.id) { + return fail(400, { + status: false, + message: 'article.notFound' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const originalData = await conn + .selectFrom('article') + .selectAll() + .where('id', '=', input.id) + .executeTakeFirst(); if (!originalData) { - return { + return fail(404, { status: false, - code: 404, message: 'article.notFound' satisfies ErrorPath - } satisfies ErrorApiResponse; + } satisfies ActionsResponse); } const trx = await conn.startTransaction().execute(); try { let someChanged = false; - //compare original data with input - if (originalData.title !== input.title || originalData.description !== input.description || originalData.content_md !== input.content_md) { - someChanged = true; - await trx - .updateTable('article') - .set({ - title: input.title, - description: input.description, - content_md: input.content_md - }) - .where('id', '=', input.id) - .execute(); + const languagesKeys = Object.keys(languages); + + // Update Translations (Title, Description, Content) + const fields = ['title', 'description', 'content_md'] as const; + for (const field of fields) { + const uuid = originalData[field]; + for (const lang of languagesKeys) { + const newText = parsed[lang]?.[field]; + if (newText) { + await trx + .updateTable('translations') + .set({ text: newText }) + .where('key', '=', uuid) + .where('lang', '=', lang) + .execute(); + someChanged = true; + } + } } //equipment - const originalEquipment = await conn.selectFrom('article_equipment').select(['equipment_id']).where('article_id', '=', input.id).execute(); + const originalEquipment = await conn + .selectFrom('article_equipment') + .select(['equipment_id']) + .where('article_id', '=', input.id) + .execute(); const ids = originalEquipment.map((eq) => eq.equipment_id); const toAdd = input.equipment.filter((eq) => !ids.includes(eq)); - const toRemove = originalEquipment.filter((eq) => !input.equipment.includes(eq.equipment_id)); + const toRemove = originalEquipment.filter( + (eq) => !input.equipment.includes(eq.equipment_id) + ); if (toAdd.length > 0) { someChanged = true; @@ -126,7 +180,7 @@ export default [ .insertInto('article_equipment') .values( toAdd.map((eq) => ({ - article_id: input.id, + article_id: input.id!, equipment_id: eq })) ) @@ -137,7 +191,7 @@ export default [ someChanged = true; await trx .deleteFrom('article_equipment') - .where('article_id', '=', input.id) + .where('article_id', '=', input.id!) .where( 'equipment_id', 'in', @@ -145,51 +199,114 @@ export default [ ) .execute(); } + //images if (input.images.length === 0) { - return { + return fail(400, { status: false, - code: 400, message: 'article.noImages' satisfies ErrorPath - } satisfies ErrorApiResponse; + } satisfies ActionsResponse); } - // - const originalImages = await conn.selectFrom('gallery_image').select(['name']).where('article_id', '=', input.id).execute(); - const imageNames = originalImages.map((img) => img.name); - const toAddImages = input.images.filter((img) => !imageNames.includes(img.name)); - const toRemoveImages = originalImages.filter((img) => !input.images.some((_img) => _img.name === img.name)); - if (toAddImages.length > 0) { + const originalImages = await conn + .selectFrom('gallery_image') + .selectAll() + .where('article_id', '=', input.id) + .execute(); + + const inputImageNames = input.images.map((img) => img.name); + + // Removed Images + const imagesToRemove = originalImages.filter( + (img) => !inputImageNames.includes(img.name) + ); + if (imagesToRemove.length > 0) { someChanged = true; + // Delete images await trx - .insertInto('gallery_image') - .values( - toAddImages.map((image) => ({ - ...image, - article_id: input.id - })) + .deleteFrom('gallery_image') + .where( + 'id', + 'in', + imagesToRemove.map((img) => img.id) ) .execute(); - } - if (toRemoveImages.length > 0) { - someChanged = true; + // Cleanup translations await trx - .deleteFrom('gallery_image') - .where('article_id', '=', input.id) + .deleteFrom('translations') .where( - 'name', + 'key', 'in', - toRemoveImages.map((img) => img.name) + imagesToRemove.map((img) => img.alt_text) ) .execute(); } + // Process Input Images + for (let i = 0; i < input.images.length; i++) { + const img = input.images[i]; + const existing = originalImages.find((orig) => orig.name === img.name); + + if (existing) { + // Update Translations + const uuid = existing.alt_text; + for (const lang of languagesKeys) { + const newText = parsed[lang]?.images?.[i]?.alt_text ?? ''; + + const result = await trx + .updateTable('translations') + .set({ text: newText }) + .where('key', '=', uuid) + .where('lang', '=', lang) + .executeTakeFirst(); + + if (Number(result.numUpdatedRows) === 0) { + await trx + .insertInto('translations') + .values({ key: uuid, lang: lang, text: newText }) + .execute(); + } + } + } else { + someChanged = true; + // New Image -> Insert + const transObject: Record = {}; + for (const lang of languagesKeys) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const langData = parsed[lang] as any; + transObject[lang] = { alt_text: langData?.images?.[i]?.alt_text ?? '' }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const transResult = await insertTranslations(trx, transObject as any, [ + 'alt_text' + ]); + + await trx + .insertInto('gallery_image') + .values({ + article_id: input.id!, + name: img.name, + alt_text: transResult.alt_text + }) + .execute(); + } + } + //exposures - const originalExposures = await conn.selectFrom('exposure').select(['id']).where('article_id', '=', input.id).execute(); + const originalExposures = await conn + .selectFrom('exposure') + .select(['id']) + .where('article_id', '=', input.id) + .execute(); const exposureIds = originalExposures.map((exp) => exp.id); - const toAddExposures = input.exposures.filter((exp) => exp.id === undefined || !exposureIds.includes(exp.id)); - const toRemoveExposures = originalExposures.filter((exp) => !input.exposures.some((_exp) => _exp.id === exp.id)); + const toAddExposures = input.exposures.filter( + (exp) => exp.id === undefined || !exposureIds.includes(exp.id) + ); + const toRemoveExposures = originalExposures.filter( + (exp) => !input.exposures.some((_exp) => _exp.id === exp.id) + ); if (toAddExposures.length > 0) { await trx @@ -197,7 +314,7 @@ export default [ .values( toAddExposures.map((exposure) => ({ ...exposure, - article_id: input.id + article_id: input.id! })) ) .execute(); @@ -206,7 +323,7 @@ export default [ if (toRemoveExposures.length > 0) { await trx .deleteFrom('exposure') - .where('article_id', '=', input.id) + .where('article_id', '=', input.id!) .where( 'id', 'in', @@ -221,7 +338,7 @@ export default [ .set({ updated_at: new Date() }) - .where('id', '=', input.id) + .where('id', '=', input.id!) .execute(); } @@ -233,11 +350,10 @@ export default [ console.error(err); await trx.rollback().execute(); - return { + return fail(500, { status: false, - code: 500, message: 'internal' satisfies ErrorPath - } satisfies ErrorApiResponse; + } satisfies ActionsResponse); } }), loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { @@ -248,7 +364,11 @@ export default [ //exposures await trx.deleteFrom('exposure').where('article_id', '=', input).execute(); //images - const images = await conn.selectFrom('gallery_image').select(['name']).where('article_id', '=', input).execute(); + const images = await conn + .selectFrom('gallery_image') + .select(['name']) + .where('article_id', '=', input) + .execute(); await trx.deleteFrom('gallery_image').where('article_id', '=', input).execute(); //remove images const imgPaths = images.map((img) => Path.join(FILE_FOLDER, img.name)); diff --git a/src/lib/server/_routes/equipment.ts b/src/lib/server/_routes/equipment.ts index 64be128..4513e2a 100644 --- a/src/lib/server/_routes/equipment.ts +++ b/src/lib/server/_routes/equipment.ts @@ -1,103 +1,121 @@ -import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { loggedProcedure } from '../api'; -import { fail } from '@sveltejs/kit'; -import type { ActionsResponse, Response } from '$/types/types'; import type { ErrorPath } from '$/lib/lang'; -import { conn } from '../variables'; +import type { ActionsResponse, Response } from '$/types/types'; +import { createFormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; +import { fail } from '@sveltejs/kit'; import { z } from 'zod'; +import { loggedProcedure } from '../api'; +import { conn } from '../variables'; -export default [ - loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const name = input.get('name') as string | null; - const type = input.get('type') as string | null; - const link = input.get('link') as string | null; +const equipmentSchema = z.object({ + name: z.string().min(1, 'equipment.form.name'), + type: z.string().min(1, 'equipment.form.type'), + link: z.url('equipment.form.link') +}); - if (!name || !type || !link || isNaN(Number(type))) { - return fail(400, { - status: false, - message: 'equipment.form' satisfies ErrorPath - } satisfies ActionsResponse); - } +export default [ + loggedProcedure.POST.input(createFormDataInput(equipmentSchema)).query( + async ({ input }) => { + const name = input.get('name') as string; + const type = input.get('type') as string; + const link = input.get('link') as string; - try { - const equipmentType = await conn.selectFrom('equipment_type').select('id').where('id', '=', Number(type)).executeTakeFirst(); - if (!equipmentType) { + if (!name || !type || !link || isNaN(Number(type))) { return fail(400, { status: false, message: 'equipment.form' satisfies ErrorPath } satisfies ActionsResponse); } - await conn - .insertInto('equipment') - .values({ - name, - type_id: Number(type), - link - }) - .executeTakeFirst(); + try { + const equipmentType = await conn + .selectFrom('equipment_type') + .select('id') + .where('id', '=', Number(type)) + .executeTakeFirst(); + if (!equipmentType) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return fail(500, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); - } - }), - loggedProcedure.PATCH.input(FormDataInput).query(async ({ input }) => { - const id = input.get('id') as string | null; - const name = input.get('name') as string | null; - const type = input.get('type') as string | null; - const link = input.get('link') as string | null; + await conn + .insertInto('equipment') + .values({ + name, + type_id: Number(type), + link + }) + .executeTakeFirst(); - if (!id || isNaN(Number(id))) { - return fail(401, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } } + ), + loggedProcedure.PATCH.input(createFormDataInput(equipmentSchema.partial())).query( + async ({ input }) => { + const id = input.get('id') as string | null; + const name = input.get('name') as string | null; + const type = input.get('type') as string | null; + const link = input.get('link') as string | null; - if (!name || !type || !link || isNaN(Number(type))) { - return fail(401, { - status: false, - message: 'equipment.form' satisfies ErrorPath - } satisfies ActionsResponse); - } + if (!id || isNaN(Number(id))) { + return fail(401, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } - try { - const equipmentType = await conn.selectFrom('equipment_type').select('id').where('id', '=', Number(type)).executeTakeFirst(); - if (!equipmentType) { - return fail(400, { + if (!name || !type || !link || isNaN(Number(type))) { + return fail(401, { status: false, message: 'equipment.form' satisfies ErrorPath } satisfies ActionsResponse); } - await conn - .updateTable('equipment') - .set({ - name, - type_id: Number(type), - link - }) - .where('id', '=', Number(id)) - .executeTakeFirst(); + try { + const equipmentType = await conn + .selectFrom('equipment_type') + .select('id') + .where('id', '=', Number(type)) + .executeTakeFirst(); + if (!equipmentType) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } - return { - status: true - } satisfies Response; - } catch (err) { - console.error(err); - return fail(500, { - status: false, - message: 'internal' satisfies ErrorPath - } satisfies ActionsResponse); + await conn + .updateTable('equipment') + .set({ + name, + type_id: Number(type), + link + }) + .where('id', '=', Number(id)) + .executeTakeFirst(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } } - }), + ), loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { if (!input || isNaN(Number(input))) { return { @@ -108,7 +126,10 @@ export default [ } try { - await conn.deleteFrom('equipment').where('id', '=', Number(input)).executeTakeFirst(); + await conn + .deleteFrom('equipment') + .where('id', '=', Number(input)) + .executeTakeFirst(); return { status: true diff --git a/src/lib/server/_routes/login.ts b/src/lib/server/_routes/login.ts index 069e555..214ed41 100644 --- a/src/lib/server/_routes/login.ts +++ b/src/lib/server/_routes/login.ts @@ -7,46 +7,52 @@ import { conn, jwt } from '../variables'; import bcrypt from 'bcrypt'; import { COOKIE_EXPIRE } from '$env/static/private'; -export default procedure.POST.input(FormDataInput).query(async ({ input, ev: { cookies } }) => { - const username = input.get('username') as string | null; - const password = input.get('password') as string | null; - - if (!username || !password) { - return fail(401, { - status: false, - message: 'login.form' satisfies ErrorPath - } satisfies ActionsResponse); +export default procedure.POST.input(FormDataInput).query( + async ({ input, ev: { cookies } }) => { + const username = input.get('username') as string | null; + const password = input.get('password') as string | null; + + if (!username || !password) { + return fail(401, { + status: false, + message: 'login.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const data = await conn + .selectFrom('account') + .selectAll() + .where('username', '=', username) + .executeTakeFirst(); + + if (!data) { + return fail(401, { + status: false, + message: 'login.username' satisfies ErrorPath + } satisfies ActionsResponse); + } + + if (!bcrypt.compareSync(password, data.password)) { + return fail(401, { + status: false, + message: 'login.password' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const userData = { + ...data, + password: undefined + }; + + const session = jwt.setCookie(userData); + + cookies.set('session', session, { + path: '/', + maxAge: parseInt(COOKIE_EXPIRE) + }); + + return { + status: true + } satisfies Response; } - - const data = await conn.selectFrom('account').selectAll().where('username', '=', username).executeTakeFirst(); - - if (!data) { - return fail(401, { - status: false, - message: 'login.username' satisfies ErrorPath - } satisfies ActionsResponse); - } - - if (!bcrypt.compareSync(password, data.password)) { - return fail(401, { - status: false, - message: 'login.password' satisfies ErrorPath - } satisfies ActionsResponse); - } - - const userData = { - ...data, - password: undefined - }; - - const session = jwt.setCookie(userData); - - cookies.set('session', session, { - path: '/', - maxAge: parseInt(COOKIE_EXPIRE) - }); - - return { - status: true - } satisfies Response; -}); +); diff --git a/src/lib/server/cookies/main.ts b/src/lib/server/cookies/main.ts index 1664a11..5a887f0 100644 --- a/src/lib/server/cookies/main.ts +++ b/src/lib/server/cookies/main.ts @@ -23,11 +23,16 @@ export class SessionCookies { private db: JSONdb>; constructor(storage = './cookies.json') { - this.db = new JSONdb>(storage.startsWith('./') || storage.startsWith('../') ? path.join(__dirname, storage) : storage, { - syncOnWrite: true, - jsonSpaces: false, - asyncWrite: false - }); + this.db = new JSONdb>( + storage.startsWith('./') || storage.startsWith('../') + ? path.join(__dirname, storage) + : storage, + { + syncOnWrite: true, + jsonSpaces: false, + asyncWrite: false + } + ); } checkCookies() { diff --git a/src/lib/server/functions.ts b/src/lib/server/functions.ts index ad537dc..cf25c9f 100644 --- a/src/lib/server/functions.ts +++ b/src/lib/server/functions.ts @@ -1,3 +1,4 @@ +import type { DB, Translations } from '$/types/database'; import { extensions, type DePromise, @@ -5,16 +6,19 @@ import { type UserData, type UserState } from '$/types/types'; +import { FILE_FOLDER } from '$env/static/private'; import type { Cookies } from '@sveltejs/kit'; -import { conn, jwt } from './variables'; import { redirect as _redirect } from '@sveltejs/kit'; -import { getState } from '../state.svelte'; +import type { ControlledTransaction, Insertable } from 'kysely'; +import crypto from 'node:crypto'; import fs from 'node:fs/promises'; import Path from 'node:path'; -import crypto from 'node:crypto'; import { promisify } from 'node:util'; -import { FILE_FOLDER } from '$env/static/private'; +import { v4 } from 'uuid'; +import type z from 'zod'; import { languages } from '../lang'; +import { getState } from '../state.svelte'; +import { conn, jwt } from './variables'; const randomBytesAsync = promisify(crypto.randomBytes); @@ -118,3 +122,84 @@ export const constructEmptyTranslations = () => { }; export type GatheredTranslationsAll = DePromise>; + +export const parseFormData = <$DataType>( + formData: FormData, + schema: z.ZodType<$DataType> +) => { + const object = Object.fromEntries(formData.entries()); + //parse objects/arrays + for (const key in object) { + const value = object[key]; + if ( + typeof value === 'string' && + (value.startsWith('{') || value.startsWith('[') || value.startsWith('"')) + ) { + try { + object[key] = JSON.parse(value); + } catch { + // If parsing fails, keep the original string + } + } + } + + //resolve languagable + //language:key = value + + const groupped = {} as Record>; + const languagesKeys = Object.keys(languages); + + for (const lang of languagesKeys) { + groupped[lang] = {}; + } + + for (const key in object) { + const [lang, subKey] = key.split(':'); + if (lang && subKey && (languagesKeys as string[]).includes(lang)) { + groupped[lang as keyof typeof languages][subKey] = object[key]; + } else { + for (const lang of languagesKeys) { + groupped[lang][key] = object[key]; + } + } + } + + //parse search sub-object + for (const lang in groupped) { + const subObject = groupped[lang]; + const parsed = schema.parse(subObject); + groupped[lang] = parsed as Record; + } + + return groupped as Record; +}; + +export const insertTranslations = async < + $Record extends Record, + $Fields extends keyof $Record +>( + trx: ControlledTransaction, + obj: Record, + fields: $Fields[] +) => { + const result = {} as Record<$Fields, string>; + + const toInsert = [] as Insertable[]; + + const langs = Object.keys(obj); + for (const field of fields) { + const uuid = v4(); + for (const lang of langs) { + toInsert.push({ + key: uuid, + lang: lang, + text: obj[lang][field] as string + }); + result[field] = uuid; + } + } + + await trx.insertInto('translations').values(toInsert).execute(); + + return result; +}; diff --git a/src/lib/server/variables.ts b/src/lib/server/variables.ts index b1f4a88..9822a9f 100644 --- a/src/lib/server/variables.ts +++ b/src/lib/server/variables.ts @@ -1,5 +1,12 @@ import type { DB } from '$/types/database'; -import { JWT_SECRET, DATABASE_NAME, DATABASE_IP, DATABASE_PASSWORD, DATABASE_PORT, DATABASE_USER } from '$env/static/private'; +import { + JWT_SECRET, + DATABASE_NAME, + DATABASE_IP, + DATABASE_PASSWORD, + DATABASE_PORT, + DATABASE_USER +} from '$env/static/private'; import { JWTCookies } from './cookies/main'; import { Kysely, MysqlDialect } from 'kysely'; import { createPool } from 'mysql2'; diff --git a/src/routes/[[lang=lang]]/+layout.server.ts b/src/routes/[[lang=lang]]/+layout.server.ts index 921acd4..f886931 100644 --- a/src/routes/[[lang=lang]]/+layout.server.ts +++ b/src/routes/[[lang=lang]]/+layout.server.ts @@ -29,7 +29,12 @@ export const load = (async ({ params, url, cookies }) => { api: Server.hydrateToClient(), lang: languages[lang].t, selectedLang: lang, - languageList: Object.fromEntries(Object.entries(languages).map(([code, lang]) => [code, { name: lang.name, flag: lang.flag }])), + languageList: Object.fromEntries( + Object.entries(languages).map(([code, lang]) => [ + code, + { name: lang.name, flag: lang.flag } + ]) + ), userState: getUserState(cookies) }; }) satisfies LayoutServerLoad; diff --git a/src/routes/[[lang=lang]]/+layout.svelte b/src/routes/[[lang=lang]]/+layout.svelte index b8d22fc..473d37c 100644 --- a/src/routes/[[lang=lang]]/+layout.svelte +++ b/src/routes/[[lang=lang]]/+layout.svelte @@ -66,9 +66,12 @@ }); - + -
+
diff --git a/src/routes/[[lang=lang]]/+page.svelte b/src/routes/[[lang=lang]]/+page.svelte index 1689024..1379403 100644 --- a/src/routes/[[lang=lang]]/+page.svelte +++ b/src/routes/[[lang=lang]]/+page.svelte @@ -26,17 +26,34 @@ }); -
+
- - - Profile IMG + + + Profile IMG

Patrik Mintěl

{_state.lang.main.age}: {age}

-

+

+ +

diff --git a/src/routes/[[lang=lang]]/[...404]/+page.svelte b/src/routes/[[lang=lang]]/[...404]/+page.svelte index e9082d2..d7c2ec5 100644 --- a/src/routes/[[lang=lang]]/[...404]/+page.svelte +++ b/src/routes/[[lang=lang]]/[...404]/+page.svelte @@ -15,7 +15,12 @@
-

{_state.lang.error.message}

+

+ + {_state.lang.error.message} +

{_state.lang.error.sub_message}

- {_state.lang.error.go_home} + {_state.lang.error.go_home}
diff --git a/src/routes/[[lang=lang]]/admin/+page.server.ts b/src/routes/[[lang=lang]]/admin/+page.server.ts index c31be11..6cbb2d3 100644 --- a/src/routes/[[lang=lang]]/admin/+page.server.ts +++ b/src/routes/[[lang=lang]]/admin/+page.server.ts @@ -22,7 +22,11 @@ export const load = (async () => { .selectFrom((eb) => eb .selectFrom('visitors') - .select(['ip', sql`WEEK(date, 1)`.as('WEEK'), sql`YEAR(date)`.as('YEAR')]) + .select([ + 'ip', + sql`WEEK(date, 1)`.as('WEEK'), + sql`YEAR(date)`.as('YEAR') + ]) .groupBy([sql`WEEK(date, 1)`, sql`YEAR(date)`, 'ip']) .as('t') ) diff --git a/src/routes/[[lang=lang]]/admin/+page.svelte b/src/routes/[[lang=lang]]/admin/+page.svelte index 2c7dff7..51a438a 100644 --- a/src/routes/[[lang=lang]]/admin/+page.svelte +++ b/src/routes/[[lang=lang]]/admin/+page.svelte @@ -16,12 +16,16 @@ }; //get all unique years - const years = new Set(data.weekGraph.filter((item) => item.YEAR).map((item) => item.YEAR)); + const years = new Set( + data.weekGraph.filter((item) => item.YEAR).map((item) => item.YEAR) + ); for (const year of years) { chartData.datasets.push({ label: year.toString(), - data: data.weekGraph.filter((item) => item.YEAR === year).map((item) => ({ x: item.WEEK.toString(), y: item.COUNT })) + data: data.weekGraph + .filter((item) => item.YEAR === year) + .map((item) => ({ x: item.WEEK.toString(), y: item.COUNT })) }); } diff --git a/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts b/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts index a19fa8d..2755ec3 100644 --- a/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts +++ b/src/routes/[[lang=lang]]/admin/article/[id]/+page.server.ts @@ -1,12 +1,13 @@ -import { conn } from '$/lib/server/variables'; -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; -import type { Insertable } from 'kysely'; -import type { Article, Exposure, GalleryImage } from '$/types/database'; import { constructEmptyTranslations, gatherTranslationsAll } from '$/lib/server/functions'; +import { Server } from '$/lib/server/server'; +import { conn } from '$/lib/server/variables'; +import type { Article, Exposure, GalleryImage } from '$/types/database'; +import { redirect, type Actions } from '@sveltejs/kit'; +import type { Insertable } from 'kysely'; +import type { PageServerLoad } from './$types'; export const load = (async ({ params }) => { const equipmentData = await conn.selectFrom('equipment').selectAll().execute(); @@ -80,3 +81,8 @@ export const load = (async ({ params }) => { ]) }; }) satisfies PageServerLoad; + +export const actions = { + create: Server.actions.article.POST, + edit: Server.actions.article.PUT +} satisfies Actions; diff --git a/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte b/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte index 8fb798a..74f74bb 100644 --- a/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte +++ b/src/routes/[[lang=lang]]/admin/article/[id]/+page.svelte @@ -1,30 +1,35 @@ {#snippet subTitle(text: string)} @@ -205,7 +140,15 @@ {/snippet}
-
+
{_lang.back} @@ -213,79 +156,69 @@

{editing ? _lang.editTitle : _lang.createTitle}

-
-

{_state.lang.language}:

- {#each Object.entries(languages) as [lang, data] (lang)} - - {/each} -
+ +
{@render subTitle(_lang.details.title)} - - - {#snippet right()} - - {/snippet} - - - - - - {#snippet right()} -
- {showPreview ? _lang.details.previewContent : _lang.details.editContent} - -
- {/snippet} - - {#if !showPreview} - + {:else} + + {#snippet right()} + (showPreview = false)} + /> + {/snippet} + + {/if}
{@render subTitle(_lang.equipment.title)} - {#if article.equipment.length === 0} + + {#if equipment.length === 0}

{_lang.equipment.empty}

{:else}
- {#each data.equipment.filter( (item) => article.equipment.includes(item.id) ) as item (item.id)} + {#each data.equipment.filter( (item) => equipment.includes(item.id) ) as item (item.id)}
{item.name} - (article.equipment = article.equipment.filter( - (_item) => _item !== item.id - ))} + (equipment = equipment.filter((_item) => _item !== item.id))} name="bi-trash-fill" class="cursor-pointer text-red-500" /> @@ -296,62 +229,7 @@ {@render subTitle(_lang.images.title)} - -
- - - -
- - {#if article.images.length === 0} -

{_lang.images.empty}

- {:else} -
- {#each article.images as image (image.name)} -
- {image.alt_text} - {image.alt_text} -
- deleteImage(image.name)} - name="bi-trash-fill" - class="cursor-pointer rounded-md border-2 border-red-600 bg-red-500 px-2 py-1" - /> -
-
- {/each} -
- {/if} -
+
{@render subTitle(_lang.exposures.title)} @@ -373,7 +251,7 @@ label={_lang.exposures.count} variant="small" > - + - +
- +
+ @@ -408,12 +287,12 @@ - {#if article.exposures.length === 0} + {#if exposures.length === 0} {:else} - {#each article.exposures as exposure, idx (`${exposure.date.toISOString()}-${exposure.type}-${exposure.count}-${exposure.exposure_time_s}`)} + {#each exposures as exposure, idx (`${exposure.date.toISOString()}-${exposure.type}-${exposure.count}-${exposure.exposure_time_s}`)} @@ -423,9 +302,7 @@ {/each} @@ -285,7 +343,9 @@
{@render title(_lang.equipment.title)} - +
{#if data.equipment.length === 0} {@render info(_lang.equipment.empty)} @@ -306,7 +366,12 @@
- + {/each} diff --git a/src/routes/[[lang=lang]]/contact/+page.svelte b/src/routes/[[lang=lang]]/contact/+page.svelte index 13a50e5..8914f94 100644 --- a/src/routes/[[lang=lang]]/contact/+page.svelte +++ b/src/routes/[[lang=lang]]/contact/+page.svelte @@ -86,12 +86,19 @@ target="_blank" class="border-text bg-background group flex w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-md border-2 p-4 transition-all duration-300 hover:scale-105 hover:bg-gray-900 sm:w-1/3 md:w-1/4" > - +

{platform.name}

-
+
{platform.tag}
-

{resolveTranslation(platform.descriptionKey, _state.lang)}

+

+ {resolveTranslation(platform.descriptionKey, _state.lang)} +

{/each}
diff --git a/src/routes/[[lang=lang]]/gallery/+page.server.ts b/src/routes/[[lang=lang]]/gallery/+page.server.ts index 101b464..14deab8 100644 --- a/src/routes/[[lang=lang]]/gallery/+page.server.ts +++ b/src/routes/[[lang=lang]]/gallery/+page.server.ts @@ -5,7 +5,11 @@ import type { PageServerLoad } from './$types'; export const load = (async ({ parent }) => { const parentData = await parent(); - const posts = await conn.selectFrom('article').select(['id', 'title', 'description', 'created_at', 'updated_at']).orderBy('created_at', 'desc').execute(); + const posts = await conn + .selectFrom('article') + .select(['id', 'title', 'description', 'created_at', 'updated_at']) + .orderBy('created_at', 'desc') + .execute(); const postsEquipment = await conn .selectFrom('article_equipment') @@ -21,12 +25,17 @@ export const load = (async ({ parent }) => { return { posts: posts.map((post) => ({ ...post, - equipment: postsEquipment.filter((eq) => eq.article_id === post.id).sort((a, b) => b.priority - a.priority), + equipment: postsEquipment + .filter((eq) => eq.article_id === post.id) + .sort((a, b) => b.priority - a.priority), images: images.filter((image) => image.article_id === post.id), exposures: exposures.filter((exposure) => exposure.article_id === post.id) })), dynamicTranslations: await gatherTranslations( - [...posts.map((post) => [post.title, post.description]).flat(), ...images.map((image) => image.alt_text)], + [ + ...posts.map((post) => [post.title, post.description]).flat(), + ...images.map((image) => image.alt_text) + ], parentData.selectedLang ) }; diff --git a/src/routes/[[lang=lang]]/gallery/+page.svelte b/src/routes/[[lang=lang]]/gallery/+page.svelte index 8de36f7..6ab316f 100644 --- a/src/routes/[[lang=lang]]/gallery/+page.svelte +++ b/src/routes/[[lang=lang]]/gallery/+page.svelte @@ -14,14 +14,21 @@ {#snippet badge(text: string)} -
{text}
+
+ {text} +
{/snippet}

{_lang.title}

@@ -56,7 +67,9 @@ {formatDate(post.updated_at, false)} {/if} - + diff --git a/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts b/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts index ea2eaf2..ec2e0b1 100644 --- a/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts +++ b/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts @@ -5,13 +5,27 @@ import type { PageServerLoad } from './$types'; export const load = (async ({ params, parent }) => { const parentData = await parent(); - const post = await conn.selectFrom('article').selectAll().where('id', '=', params.id).executeTakeFirst(); + const post = await conn + .selectFrom('article') + .selectAll() + .where('id', '=', params.id) + .executeTakeFirst(); if (!post) { return redirect(302, '/gallery'); } - const images = await conn.selectFrom('gallery_image').selectAll().where('article_id', '=', post.id).orderBy('id', 'asc').execute(); - const exposures = await conn.selectFrom('exposure').selectAll().where('article_id', '=', post.id).orderBy('date', 'asc').execute(); + const images = await conn + .selectFrom('gallery_image') + .selectAll() + .where('article_id', '=', post.id) + .orderBy('id', 'asc') + .execute(); + const exposures = await conn + .selectFrom('exposure') + .selectAll() + .where('article_id', '=', post.id) + .orderBy('date', 'asc') + .execute(); const equipment = await conn .selectFrom('article_equipment') .innerJoin('equipment', 'equipment_id', 'equipment.id') @@ -27,6 +41,14 @@ export const load = (async ({ params, parent }) => { exposures, equipment }, - dynamicTranslations: await gatherTranslations([post.title, post.description, post.content_md, ...images.map((image) => image.alt_text)], parentData.selectedLang) + dynamicTranslations: await gatherTranslations( + [ + post.title, + post.description, + post.content_md, + ...images.map((image) => image.alt_text) + ], + parentData.selectedLang + ) }; }) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte b/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte index 4ffebae..ab4516f 100644 --- a/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte +++ b/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte @@ -30,31 +30,57 @@
- {_lang.back} + {_lang.back}

{_langDynamic[data.post.title]}

-
{_lang.created} {formatDate(data.post.created_at, false)}
-
{_lang.updated} {formatDate(data.post.updated_at, false)}
+
+ + {_lang.created} + {formatDate(data.post.created_at, false)} +
+
+ + {_lang.updated} + {formatDate(data.post.updated_at, false)} +
- {_lang.totalExposure}: {sToHHMM(data.post.exposures.filter((ex) => ex.type === 'light').reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0))} + {_lang.totalExposure}: {sToHHMM( + data.post.exposures + .filter((ex) => ex.type === 'light') + .reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0) + )}
-
+
- + {#if data.post.images.length > 1} -
+
{/if}
- {_langDynamic[data.post.images[selectedImage].alt_text]} + {_langDynamic[data.post.images[selectedImage].alt_text]} {#if data.post.images.length > 1} @@ -89,10 +117,20 @@ {#each ['light', 'dark', 'bias', 'flat'] as const as type (type)} {@const filtered = data.post.exposures.filter((ex) => ex.type === type)} {@const count = filtered.reduce((acc, ex) => acc + ex.count, 0)} -
- {_state.lang.frames[type]} {_frames.frames} -

{sToHHMM(filtered.reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0))}

- {count} {resolveLanguagable(_lang.framesCount, count)} +
+ {_state.lang.frames[type]} {_frames.frames} +

{sToHHMM( + filtered.reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0) + )}

+ {count} {resolveLanguagable(_lang.framesCount, count)}
{/each}
@@ -102,25 +140,42 @@
{#if section === 'article'} - +

{_lang.images}

{#each data.post.images as image (image.id)} - - + + {_langDynamic[image.alt_text]} @@ -148,7 +203,9 @@
- + {/each} @@ -159,10 +216,18 @@

{_lang.equipmentDetails}

{#each data.post.equipment as equipment (equipment.name)} -
+

{equipment.name}

- {resolveTranslation(equipment.lang_key, _state.lang)} -
+ {resolveTranslation(equipment.lang_key, _state.lang)} +
{/each}
diff --git a/src/routes/[[lang=lang]]/login/+page.svelte b/src/routes/[[lang=lang]]/login/+page.svelte index e99a578..75028cc 100644 --- a/src/routes/[[lang=lang]]/login/+page.svelte +++ b/src/routes/[[lang=lang]]/login/+page.svelte @@ -1,46 +1,52 @@ -
+

{_state.lang.admin.login.title}

- - - - - - + + + - + diff --git a/src/routes/image/[name]/+server.ts b/src/routes/image/[name]/+server.ts index 0406e16..cf9237f 100644 --- a/src/routes/image/[name]/+server.ts +++ b/src/routes/image/[name]/+server.ts @@ -41,8 +41,13 @@ class MemoryCache { } set(key: string, value: Buffer) { - if (this.getCacheSize() + value.length > this.maxSize || this.cache.size >= this.maxEntries) { - const oldest = [...this.cache.entries()].sort((a, b) => a[1].timestamp - b[1].timestamp)[0]; + if ( + this.getCacheSize() + value.length > this.maxSize || + this.cache.size >= this.maxEntries + ) { + const oldest = [...this.cache.entries()].sort( + (a, b) => a[1].timestamp - b[1].timestamp + )[0]; if (oldest) { this.cache.delete(oldest[0]); } @@ -160,7 +165,10 @@ export const GET = (async ({ params, url, request }) => { const originalContent = await fs.readFile(filePath); let image = sharp(originalContent); - const imageOptions: sharp.JpegOptions & sharp.PngOptions & sharp.WebpOptions & sharp.TiffOptions = { + const imageOptions: sharp.JpegOptions & + sharp.PngOptions & + sharp.WebpOptions & + sharp.TiffOptions = { quality: DEFAULT_IMAGE_QUALITY }; diff --git a/src/types/schemes.ts b/src/types/schemes.ts index 50143f9..d6a92be 100644 --- a/src/types/schemes.ts +++ b/src/types/schemes.ts @@ -1,7 +1,8 @@ +import { languages } from '$/lib/lang'; import { z } from 'zod'; const articleImage = z.object({ - alt_text: z.string(), + alt_text: z.string().min(1, 'article.noAltText'), article_id: z.string(), id: z.number().optional(), name: z.string() @@ -15,14 +16,15 @@ const articleExposure = z.object({ type: z.string() }); -export const articleSchema = z.object({ - id: z.string().optional(), - title: z.string(), - description: z.string(), - content_md: z.string(), - created_at: z.coerce.date().optional(), - updated_at: z.coerce.date().optional(), - images: z.array(articleImage), - exposures: z.array(articleExposure), - equipment: z.array(z.number()) -}); +export const articleSchema = (lang: keyof typeof languages) => + z.object({ + id: z.string().optional(), + title: z.string().min(1, languages[lang].t.errors.article.noTitle), + description: z.string().min(1, languages[lang].t.errors.article.noDescrption), + content_md: z.string().min(1, languages[lang].t.errors.article.noContent), + created_at: z.coerce.date().optional(), + updated_at: z.coerce.date().optional(), + images: z.array(articleImage), + exposures: z.array(articleExposure), + equipment: z.array(z.number()) + });
{_lang.exposures.empty}
{formatDate(exposure.date, false)} {exposure.type} - (article.exposures = article.exposures.filter( - (_, _idx) => _idx !== idx - ))} + (exposures = exposures.filter((_, _idx) => _idx !== idx))} name="bi-trash-fill" class="cursor-pointer text-2xl text-red-500" /> @@ -444,7 +321,7 @@ >{_state.lang.frames[type]} {_lang.exposures.frames} - {article.exposures + {exposures .filter((ex) => ex.type === type) .reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0)}s @@ -453,12 +330,12 @@
- - + goto(`/${_state.selectedLang}/admin/article`)}> + {_lang.cancel} + +
- + diff --git a/src/routes/[[lang=lang]]/admin/equipment/+page.svelte b/src/routes/[[lang=lang]]/admin/equipment/+page.svelte index 3cf315e..b3c45d3 100644 --- a/src/routes/[[lang=lang]]/admin/equipment/+page.svelte +++ b/src/routes/[[lang=lang]]/admin/equipment/+page.svelte @@ -25,7 +25,10 @@ const { data }: PageProps = $props(); - const createSubmitFunction = (successMessage: string, resetFn = () => {}): SubmitFunction => { + const createSubmitFunction = ( + successMessage: string, + resetFn = () => {} + ): SubmitFunction => { return (() => { return async ({ update, result }) => { if (result.type === 'success') { @@ -131,7 +134,9 @@ {/snippet} {#snippet info(text: string)} - {text} + {text} {/snippet} {#snippet _switch(text: string, name: typeof section)} @@ -147,39 +152,84 @@ (typeEditing = null)}> {@render title(replacePlaceholders(_lang.types.edit.title, typeEditing!.id.toString()))} -
(typeEditing = null))}> + (typeEditing = null) + )} + > - + - +
(equipmentEditing = null)}> - {@render title(replacePlaceholders(_lang.equipment.edit.title, equipmentEditing!.id.toString()))} + {@render title( + replacePlaceholders(_lang.equipment.edit.title, equipmentEditing!.id.toString()) + )}
(equipmentEditing = null))} + use:enhance={createSubmitFunction( + _lang.equipment.editSuccess, + () => (equipmentEditing = null) + )} > - + - +
@@ -209,7 +259,10 @@ class="border-text flex flex-col items-center justify-center rounded-md border-2 p-2" method="POST" action="?/equipmentAdd" - use:enhance={createSubmitFunction(_lang.equipment.success, () => (openEquipmentAdd = false))} + use:enhance={createSubmitFunction( + _lang.equipment.success, + () => (openEquipmentAdd = false) + )} > @@ -218,7 +271,8 @@ @@ -272,7 +326,11 @@ class="cursor-pointer" /> - typeDelete(type.id)} name="bi-trash-fill" class="cursor-pointer text-red-500" /> + typeDelete(type.id)} + name="bi-trash-fill" + class="cursor-pointer text-red-500" + />
{equipment.id} {equipment.name}{resolveTranslation(data.types.find((type) => type.id === equipment.type_id)!.lang_key, _state.lang)}{resolveTranslation( + data.types.find((type) => type.id === equipment.type_id)!.lang_key, + _state.lang + )} {equipment.link} - equipmentDelete(equipment.id)} name="bi-trash-fill" class="cursor-pointer text-red-500" /> + equipmentDelete(equipment.id)} + name="bi-trash-fill" + class="cursor-pointer text-red-500" + />
{_state.lang.frames[exposure.type as Frame]} {exposure.count} {exposure.exposure_time_s}{exposure.count * exposure.exposure_time_s}{exposure.count * exposure.exposure_time_s}