diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0a2f026 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,40 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# All files +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# TypeScript, JavaScript, CSS, HTML, JSON, Markdown +[*.{ts,js,css,html,md}] +indent_style = space +indent_size = 4 +max_line_length = 150 + +# Markdown files +[*.md] +trim_trailing_whitespace = false +max_line_length = off + +# CSS files specific +[*.{css,postcss}] +indent_style = space +indent_size = 2 +block_comment_start = /* +block_comment = * +block_comment_end = */ + +# Package files +[*.json] +indent_style = space +indent_size = 2 + +# Config files +[*.{yml,yaml,conf}] +indent_style = space +indent_size = 2 diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index f504fb7..0000000 --- a/.eslintrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "airbnb-base/legacy", - "rules": { - "indent": ["error", 4], // Use 4 spaces for indentation - "func-names": ["error", "never"], // Allow anonymous functions - "no-param-reassign": ["error", { "props": false }], // Allow parameter reassignment for object properties - "quotes": "off", // Disable quotes enforcement - "object-curly-spacing": ["off"], // Disable spacing inside curly braces - "block-spacing": ["off"], // Disable spacing enforcement inside blocks - "semi": ["off"], // Allow missing semicolons - "no-plusplus": ["off"], // Allow ++ and -- operators - "prefer-arrow-callback": ["off"], // Allow traditional function expressions - "vars-on-top": ["off"], // Allow variable declarations anywhere - "consistent-return": ["off"], // Allow inconsistent returns in functions - "no-loop-func": ["off"], // Allow functions inside loops - "object-shorthand": ["off"], // Allow verbose object property declarations - "no-prototype-builtins": ["off"], // Allow direct use of Object.prototype methods - "prefer-template": ["off"] // Allow string concatenation with `+` - }, - "parserOptions": { - "ecmaVersion": 6 - } -} diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000..1eee51c --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload docs/dist directory + path: 'docs/dist' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index c8229fc..e12a58e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ node_modules -bower_components -yarn-error.log - -# IntelliJ project files -.idea - +dist_tests +yarn.lock +package-lock.json +pnpm-lock.yaml diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..bb8c76c --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v22.11.0 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ec70dac --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ + +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "EditorConfig.EditorConfig" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e3d9194 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,29 @@ +{ + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "never" + }, + "editor.defaultFormatter": null, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "editor.tabSize": 2, + "editor.insertSpaces": true, + "files.eol": "\n", + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "editor.rulers": [150], + "editor.wordWrapColumn": 150 +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c264d10..d3a6db5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,124 @@ -### Contributing -- Give it a [star](https://github.com/VincentLoy/simplyCountdown.js/stargazers) ! -- [Report a bug](https://github.com/VincentLoy/simplyCountdown.js/issues) -- Tweet about it :) - -#### Pull Requests -- **Solve a problem** -- For code enhancement, use [ESLint](https://eslint.org/) as a code quality tool. -- Small is better than Big. +# Contributing to SimplyCountdown.js + +πŸŽ‰ Thank you for considering contributing to SimplyCountdown.js! I appreciate your support and I'm excited to collaborate with you. Below are the guidelines to help you get started. + +## How to Contribute + +### ⭐ 0. Give a Star to the Repository + +This step is totally optional... but seriously, who doesn't love some stars? πŸ˜‰ +It's like giving a virtual high-five to the project! πŸ™Œ ⭐ πŸ™Œ + +### πŸ”± 1. Fork the Repository + +Start by forking the repository to your GitHub account. + +### πŸ“₯ 2. Clone Your Fork + +Clone your forked repository to your local machine: + +```bash +git clone git@github.com:your-username/simplyCountdown.js.git +cd simplyCountdown.js +``` + +### πŸ”§ 3. Node.js Version + +Make sure you're using the correct Node.js version. You can check the required version in `.nvmrc`. If you use [nvm](https://github.com/nvm-sh/nvm), simply run: + +```bash +nvm use +``` + +This will automatically switch to the correct Node.js version for the project. + +### πŸ› οΈ 4. Set Up the Development Environment + +Install the necessary dependencies: +We recommend using [bun](https://bun.sh/) as it offers superior performance and seamless dependency management. + +```bash +bun install +``` + +**NOTE:** _For now, `package.json` is running bun commands only, maybe we will have to update this to be compatible with any package management system in the future_ + +### 🌿 5. Create a Branch + +Create a new branch for your feature or bugfix: + +```bash +git checkout -b feature/your-feature-name +``` + +### πŸ’» 6. Make Your Changes + +Make your changes to the codebase. Ensure your code follows the project's code style and formatting guidelines. +While developping, you should run this command: + +```bash +bun run dev +``` + +**NOTE:** _This will run a dev server with Vite, update the sources from `src/core` and tests your new features in `docs/src` files like `docs/src/index.html` and `docs/src/assets/js/main.js`. Each changes inside those files will refresh the dev page_ + +### βœ… 7. Test Your Changes + +Build, then, run the tests to make sure your changes don't break anything: + +```bash +bun run build +``` + +then: + +```bash +bun run dist:test:serve +``` + +**NOTE:** _This will generate various HTML files inside a `dist_test` directory and run a temporary node.js server where you can check different implementations and use-cases of the distribution files. Every HTML pages should run a working countdown._ + +### πŸ’Ύ 8. Commit Your Changes + +Commit your changes with a clear and concise commit message: + +```bash +git add . +git commit -m "Add feature: your feature description" +``` + +### πŸš€ 9. Push to Your Fork + +Push your changes to your forked repository: + +```bash +git push origin feature/your-feature-name +``` + +### πŸ”„ 10. Open a Pull Request + +Open a pull request from your forked repository to the main repository. Provide a detailed description of your changes and any relevant information. + +## Pull Request Process + +- Provide a detailed description of your changes in the pull request. +- Link any relevant issues in the pull request description. +- Wait for a maintainer to review your pull request. + +## Creating Issues + +If you find a bug or have a feature request, please create an issue in the repository. Provide as much detail as possible, including steps to reproduce the issue or a clear description of the feature. + +## Code Review Process + +All pull requests will be reviewed by a maintainer. The review process includes: + +- Checking for code quality and adherence to the style guide. +- Ensuring tests are passing. +- Providing feedback and requesting changes if necessary. + +## Documentation + +For more information about the project, please refer to the [README.md](README.md) and the [documentation website](https://vincentloy.github.io/simplyCountdown.js/). + +Thank you for contributing! πŸš€ diff --git a/LICENSE b/LICENSE index 80536d6..f4350ce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Vincent Loy +Copyright (c) 2015-present Vincent Loy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 6e01b55..0c0ae19 100644 --- a/README.md +++ b/README.md @@ -1,222 +1,355 @@ -# simplyCountdown.js +![SimplyCountdown.js](docs/src/assets/images/simplyCountdown_banner.webp) -## Why another countdown ? +# SimplyCountdown.js -I developed this little library in a boring day. I regularly use this kind of Javascript's libraries to display countdowns on websites and this one exactly meets the needs that I have. It is perfect to make 'under construction' pages, etc. +A simple yet powerful countdown plugin, with no dependencies. Create beautiful countdowns with ease! -This is a very (very) basic Javascript CountDown. +## Features -## Getting Started +- Zero dependencies +- TypeScript support +- Multiple module formats (ES, UMD, CommonJS) +- Multiple themes included +- Lightweight and performant +- Count up or down functionality +- UTC support +- Highly customizable +- Control API (stop, resume, update) +- Comprehensive event callbacks -install via npm or bower +## Installation +```bash +npm install simplycountdown +# or +yarn add simplycountdown +# or +bun install simplycountdown ``` -$ yarn add simplycountdown.js -// or +## Basic Usage -$ npm install simplycountdown.js +### ES Module + +```javascript +import simplyCountdown from "simplycountdown.js"; + +simplyCountdown("#mycountdown", { + year: 2025, + month: 12, + day: 25, +}); +``` + +## Accessing Source Files + +If you want to import and compile the TypeScript source files directly, you can include the source files in your project: + +```typescript +import simplyCountdown from "simplycountdown.js/src/core/simplyCountdown"; +``` + +### CommonJS + +```javascript +const simplyCountdown = require("simplycountdown"); + +simplyCountdown("#mycountdown", { + year: 2025, + month: 12, + day: 25, +}); ``` -### Insert simplyCountdown to your HTML +### Browser (UMD) ```html - - + + ``` + +## Configuration Options + ```javascript - // This is an example with default parameters - - simplyCountdown('[CSS-SELECTOR]', { - year: 2015, // Target year (required) - month: 6, // Target month [1-12] (required) - day: 28, // Target day [1-31] (required) - hours: 0, // Target hour [0-23], default: 0 - minutes: 0, // Target minute [0-59], default: 0 - seconds: 0, // Target second [0-59], default: 0 - words: { // Custom labels, with lambda for plurals - days: { root: 'day', lambda: (root, n) => n > 1 ? root + 's' : root }, - hours: { root: 'hour', lambda: (root, n) => n > 1 ? root + 's' : root }, - minutes: { root: 'minute', lambda: (root, n) => n > 1 ? root + 's' : root }, - seconds: { root: 'second', lambda: (root, n) => n > 1 ? root + 's' : root } +simplyCountdown("#mycountdown", { + // Target date (Required) + year: 2025, // Target year [YYYY] + month: 12, // Target month [1-12] + day: 25, // Target day [1-31] + hours: 0, // Target hours [0-23] + minutes: 0, // Target minutes [0-59] + seconds: 0, // Target seconds [0-59] + + // Words customization + words: { + days: { + // Function to handle pluralization + lambda: (root, count) => (count > 1 ? root + "s" : root), + root: "day", // Base word for days }, - plural: true, // Use plurals for labels - inline: false, // Inline format: e.g., "24 days, 4 hours, 2 minutes" - inlineSeparator: ', ', // Separator for inline format, default: ", " - inlineClass: 'simply-countdown-inline', // CSS class for inline countdown - enableUtc: false, // Use UTC time if true - onEnd: function () {}, // Callback when countdown ends - refresh: 1000, // Refresh interval in ms, default: 1000 - sectionClass: 'simply-section', // CSS class for each countdown section - amountClass: 'simply-amount', // CSS class for numeric values - wordClass: 'simply-word', // CSS class for unit labels - zeroPad: false, // Pad numbers with leading zero - removeZeroUnits: false, // Remove units with zero value - countUp: false // Count up after reaching zero - }); + hours: { + lambda: (root, count) => (count > 1 ? root + "s" : root), + root: "hour", + }, + minutes: { + lambda: (root, count) => (count > 1 ? root + "s" : root), + root: "minute", + }, + seconds: { + lambda: (root, count) => (count > 1 ? root + "s" : root), + root: "second", + }, + }, + + // Display options + plural: true, // Enable/disable pluralization + inline: false, // Display inline (true) or in blocks (false) + inlineSeparator: ", ", // Separator for inline display + enableUtc: false, // Use UTC time instead of local time + + // Styling classes + inlineClass: "simply-countdown-inline", // Class for inline display + sectionClass: "simply-section", // Class for each time unit section + amountClass: "simply-amount", // Class for number display + wordClass: "simply-word", // Class for word display + + // Formatting options + zeroPad: false, // Add leading zeros to numbers (e.g., 05 instead of 5) + countUp: false, // Count up from target date instead of down to it + removeZeroUnits: false, // Hide time units when they reach zero + refresh: 1000, // Update interval in milliseconds (1 second = 1000) + + // Event handlers + onEnd: () => { + // Callback function when countdown ends + console.log("Countdown finished!"); + }, + onStop: () => {}, // Callback when countdown is stopped + onResume: () => {}, // Callback when countdown is resumed + onUpdate: (params) => {}, // Callback when countdown is updated +}); +``` + +## HTML Structure + +The plugin generates the following HTML structure: - // Also, you can init with already existing Javascript Object. - let myElement = document.querySelector('.my-countdown'); - simplyCountdown(myElement, { /* options */ }); +### Block Display (default) - let multipleElements = document.querySelectorAll('.my-countdown'); - simplyCountdown(multipleElements, { /* options */ }); +```html +
+
+
+ 24 + days +
+
+ +
``` -### You can use it with jQuery too (not required) +### Inline Display + +```html +
24 days, 3 hours, 45 minutes, 12 seconds
+``` + +## Themes + +The library comes with several built-in themes: + +- `default.css` - Classic theme with clean, modern design +- `dark.css` - Dark mode theme +- `circle.css` - Circular display with modern aesthetics +- `cyber.css` - Cyberpunk-inspired design +- `losange.css` - Diamond-shaped display + +To use a theme, include its CSS file: + +```html + +``` + +## Examples + +### New Year Countdown with UTC ```javascript -// jQuery Example -$('[CSS-SELECTOR]').simplyCountdown({ - year: 2019, // required - month: 6, // required - day: 28, // required - ... +simplyCountdown("#newyear", { + year: 2025, + month: 1, + day: 1, + enableUtc: true, + zeroPad: true, + onEnd: () => { + alert("Happy New Year!"); + }, }); ``` -## Parameters -| Parameter | Type | Description | Default | -|--------------------|-----------------|---------------------------------------------------------------------------------------------|-----------------------------| -| `year` | Number (required) | The target year for the countdown. | - | -| `month` | Number (required) | The target month [1-12] for the countdown. | - | -| `day` | Number (required) | The target day [1-31] for the countdown. | - | -| `hours` | Number | The target hour [0-23]. | 0 | -| `minutes` | Number | The target minute [0-59]. | 0 | -| `seconds` | Number | The target second [0-59]. | 0 | -| `words` | Object | Custom labels for the units (days, hours, minutes, seconds) with optional lambda for pluralization. | `{ days: { root: 'day', lambda: (root, n) => n > 1 ? root + 's' : root }, ... }` | -| `plural` | Boolean | Whether to use plural forms for the unit labels. | `true` | -| `inline` | Boolean | Set to `true` for a simple inline countdown (e.g., "24 days, 4 hours, 2 minutes"). | `false` | -| `inlineSeparator` | String | Separator used in the inline countdown format. | `, ` | -| `inlineClass` | String | CSS class applied to the inline countdown container. | `"simply-countdown-inline"` | -| `enableUtc` | Boolean | Set to `true` to use UTC time for the countdown calculations. | `false` | -| `onEnd` | Function | Callback function executed when the countdown ends. | `() => {}` | -| `refresh` | Number | The countdown refresh interval in milliseconds. | `1000` | -| `sectionClass` | String | CSS class applied to each countdown section (days, hours, minutes, seconds). | `"simply-section"` | -| `amountClass` | String | CSS class applied to the numeric value of each countdown section. | `"simply-amount"` | -| `wordClass` | String | CSS class applied to the unit label of each countdown section. | `"simply-word"` | -| `zeroPad` | Boolean | Whether to pad the numeric values with leading zeros (e.g., "05"). | `false` | -| `removeZeroUnits` | Boolean | Remove units with zero value (e.g., remove "0 days" if days are zero). | `false` | -| `countUp` | Boolean | Count up after reaching zero if set to `true`. | `false` | - -## Easy to customize - -You can easly customize the countdown using the css theme starter file or create your own like so : - -/!\ The following theme template works with default class in parameters. - - ```css - /* - * Project : simply-countdown - * File : simplyCountdown.theme.custom - * Author : Your Name - */ - - .simply-countdown { - /* The countdown */ - } - .simply-countdown > .simply-section { - /* coutndown blocks */ - } - - .simply-countdown > .simply-section > div { - /* countdown block inner div */ - } - - .simply-countdown > .simply-section .simply-amount, - .simply-countdown > .simply-section .simply-word { - /* amounts and words */ - } - - .simply-countdown > .simply-section .simply-amount { - /* amounts */ - } - - .simply-countdown > .simply-section .simply-word { - /* words */ - } -``` +### Event Timer (Count Up) with Zero Padding -### Contributing -- Give it a [star](https://github.com/VincentLoy/simplyCountdown.js/stargazers) ! -- [Report a bug](https://github.com/VincentLoy/simplyCountdown.js/issues) -- Tweet about it :) - -#### Pull Requests -- **Solve a problem** -- For code enhancement, use [ESLint](https://eslint.org/) as a code quality tool. -- Small is better than Big. - -### Changelog - -#### 2.0.1 -Update all 2015 npm dependencies and rebuild the plugin with new deps. - -#### 2.0.0 -This version may have breaking changes, if your website is working well with 1.7.0, you may want to stay on the previous version. -- fix plurals in a generic way [#52](https://github.com/VincentLoy/simplyCountdown.js/pull/52) - - Thumbs up to [mira01](https://github.com/mira01) that fixed [#51](https://github.com/VincentLoy/simplyCountdown.js/issues/51), [#23](https://github.com/VincentLoy/simplyCountdown.js/issues/23) & [#42](https://github.com/VincentLoy/simplyCountdown.js/issues/42) ! -- Added the `inlineSeparator` parameter. Previously, the inline mode only supported a comma-separated countdown. Now, you can customize the separator, using options like |, /, -, or any character that suits your needs. -- Fix potential UTC-related issues (fingers crossed!). - - -#### 1.7.0 -- Countdowns can be initialized directly with HTML elements with variables like - - document.getElementById - - document.querySelector - - document.querySelectorAll - - etc... - -##### 1.6.0 -- Compatibility with languages like german for plurals ([PR #15](https://github.com/VincentLoy/simplyCountdown.js/pull/15)), thanks to [q30t](https://github.com/q30t) - -##### 1.5.0 -- Resolve #10 - Add countup support -- Upgrade yarn dev dependencies -- Some minor code reformatting - -##### 1.4.0 -- Remove bower support -- migrate from LESS to SASS (scss) / for demo and themes -- migrate lib from ES5 to a really basic ES6 - - Remove JSLint support - - Add ESLint support based on customized [airbnb rules](https://www.npmjs.com/package/eslint-config-airbnb-base) - -##### 1.3.2 -- add zeroPad parameter ```javascript -zeroPad: false //default +simplyCountdown("#timer", { + year: 2024, + month: 1, + day: 1, + countUp: true, + zeroPad: true, + removeZeroUnits: true, +}); ``` -- fixed Flash of Unstyled Content -##### 1.3.1 -- clean some code -- add refresh parameter +### Custom Words with Inline Display + ```javascript -refresh: 1000 //default +simplyCountdown("#inline", { + year: 2025, + month: 12, + day: 25, + inline: true, + inlineSeparator: " | ", + words: { + days: { + lambda: (root, count) => (count === 1 ? "day" : "days"), + root: "day", + }, + hours: { + lambda: (root, count) => (count === 1 ? "hour" : "hours"), + root: "hour", + }, + minutes: { + lambda: (root, count) => (count === 1 ? "minute" : "minutes"), + root: "minute", + }, + seconds: { + lambda: (root, count) => (count === 1 ? "second" : "seconds"), + root: "second", + }, + }, +}); ``` -##### 1.3.0 -- Add onEnd callback +## Selector Support + +The plugin accepts various selector types: + ```javascript -onEnd: function () { - // some code -} +// CSS selector string +simplyCountdown("#countdown", parameters); + +// Single DOM element +simplyCountdown(document.getElementById("countdown"), parameters); + +// Multiple elements +simplyCountdown(document.querySelectorAll(".countdown"), parameters); ``` -##### 1.2.0 -- Resolve #4 - Add UTC support adding enableUtc parameter +## Control Features + +The countdown instance returns a controller object that allows you to manipulate the countdown: + ```javascript -enableUtc: true //true is default +// Initialize countdown +const countdown = simplyCountdown("#mycountdown", parameters); + +// Stop the countdown +countdown.pause(); + +// Resume countdown +countdown.resume(); + +// Update countdown parameters +countdown.update({ + year: 2026, + month: 1, + day: 1, +}); + +// Chain control methods +countdown.pause(); +countdown.resume(); +countdown.update({ year: 2026, hours: 12, minutes: 51 }); ``` -##### 1.1.1 -- Resolve #3 - Remove ID Only compatibility -##### 1.1.0 -- Add hours, minutes, seconds in available settings to set the target Date +## Development Commands + +### Main Commands + +- `npm run dev`: Start development server for the documentation site & Core library (port 3000) +- `npm run build`: Build everything (library, themes, and documentation) +- `npm run preview`: Preview the documentation site + +### Build Commands + +- `npm run build:lib`: Build the library (ES and UMD formats) +- `npm run build:themes`: Build CSS themes +- `npm run build:docs`: Build the documentation site + +### Test Commands + +- `npm run test`: Run tests +- `npm run test:watch`: Run tests in watch mode +- `npm run dist:test`: Generate test files for different module formats +- `npm run dist:test:serve`: Generate and serve test files locally + +## Module Format Testing + +Test different usecases using: + +```bash +npm run dist:test:serve +``` + +Available test files: + +1. ES Module (`index.es.html`) +2. UMD Global (`index.umd-global.html`) +3. UMD AMD/RequireJS (`index.umd-amd.html`) +4. UMD CommonJS (`index.umd-commonjs.html`) +5. UMD Dynamic Loading (`index.umd-dynamic.html`) + +## Browser Support + +The library supports all modern browsers (Chrome, Firefox, Safari, Edge) and IE11+. + +## Key Changes from v1.x/v2.x to v3.x πŸš€ + +### πŸ”Œ Removal of jQuery Support + +jQuery is no longer supported in simplyCountdown to reduce dependencies and improve performance. This change ensures a more lightweight and modern library. + +**Note:** You can still use simplyCountdown in your jQuery projects by using the vanilla JavaScript syntax instead of the jQuery-specific one: + +- Old jQuery-compatible syntax: `$('.some-countdowns').simplyCountdown(options)` +- New vanilla JavaScript syntax: `simplyCountdown('.some-countdowns', options)` + +### ⚑ Transition from Gulp to Vite + +The build process has been migrated from Gulp to Vite. Vite offers faster builds, better development server capabilities, and improved support for modern JavaScript features. + +### πŸ“ Migration from ES6 JavaScript to TypeScript + +The source code has been rewritten in TypeScript to enhance code quality, provide better type checking, and improve developer experience. + +### πŸ“š New Documentation Website + +A new documentation website has been launched to provide comprehensive guides, examples, and API references. This site aims to make it easier for developers to integrate and use simplyCountdown. + +### πŸ“¦ Updated Distribution Files + +The distribution files have been reorganized to include multiple module formats (ES, UMD, CommonJS) and themes. This change ensures compatibility with various development environments and build tools. + +### πŸƒβ€β™‚οΈ Transition from npm to Bun + +The package manager has been switched from npm to Bun for faster installs and improved performance. Bun offers a more efficient and modern package management experience. -##### 1.0.1 -- Fix console error when not using jQuery +## License -##### 1.0.0 -- initial release +MIT Β© 2015-present - Vincent Loy-Serre diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..64747cc Binary files /dev/null and b/bun.lockb differ diff --git a/css/demo-only/prism.css b/css/demo-only/prism.css deleted file mode 100644 index 4412600..0000000 --- a/css/demo-only/prism.css +++ /dev/null @@ -1,135 +0,0 @@ -/** - * prism.js default theme for JavaScript, CSS and HTML - * Based on dabblet (http://dabblet.com) - * @author Lea Verou - */ - -code[class*="language-"], -pre[class*="language-"] { - color: black; - text-shadow: 0 1px white; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - line-height: 1.5; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, -code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { - text-shadow: none; - background: #b3d4fc; -} - -pre[class*="language-"]::selection, pre[class*="language-"] ::selection, -code[class*="language-"]::selection, code[class*="language-"] ::selection { - text-shadow: none; - background: #b3d4fc; -} - -@media print { - code[class*="language-"], - pre[class*="language-"] { - text-shadow: none; - } -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #f5f2f0; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #999; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag, -.token.boolean, -.token.number, -.token.constant, -.token.symbol, -.token.deleted { - color: #905; -} - -.token.selector, -.token.attr-name, -.token.string, -.token.char, -.token.builtin, -.token.inserted { - color: #690; -} - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string { - color: #a67f59; - background: hsla(0, 0%, 100%, .5); -} - -.token.atrule, -.token.attr-value, -.token.keyword { - color: #07a; -} - -.token.function { - color: #DD4A68; -} - -.token.regex, -.token.important, -.token.variable { - color: #e90; -} - -.token.important, -.token.bold { - font-weight: bold; -} -.token.italic { - font-style: italic; -} - -.token.entity { - cursor: help; -} diff --git a/css/demo.css b/css/demo.css deleted file mode 100644 index f2518c3..0000000 --- a/css/demo.css +++ /dev/null @@ -1 +0,0 @@ -a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box}body,html{font-family:"Wix Madefor Text",serif;font-optical-sizing:auto;font-style:normal;overflow-x:hidden;font-size:16px}p{font-size:1.2rem;line-height:1.6;font-weight:400;margin:20px 0 45px}h1,h2,h3{font-family:"Montserrat",sans-serif}header{display:flex;align-items:center;justify-content:center;background:#112d4e;border-bottom:5px solid #3f72af;padding:20px;height:450px}@media screen and (min-width:970px){header{height:650px}}header .header-content{text-align:center}header .header-content h1{font-style:italic;font-size:2rem;font-weight:600;color:#f9f7f7}@media screen and (min-width:970px){header .header-content h1{font-size:4rem}}header .header-content h2{font-size:1.33rem;color:#f9f7f7;margin:20px 0;font-weight:500}@media screen and (min-width:970px){header .header-content h2{font-size:1.75rem;margin:40px 0 60px 0}}header .header-content .repo-buttons{display:flex;align-items:center;justify-content:center}.button{color:#f9f7f7;border:2px solid #f9f7f7;border-radius:2px;text-decoration:none;padding:10px;transition:.1s ease-in-out all;display:block;margin:0 20px 20px 20px}.button span{display:none}@media screen and (min-width:970px){.button{margin-bottom:0}.button span{display:inline}}@media screen and (min-width:970px){.button>i{margin-right:10px}}.button:hover{background:#f9f7f7;color:#112d4e}.container{width:100%;padding:0 20px;margin:auto}@media screen and (min-width:970px){.container{width:960px}}.note{background:#3f72af;color:#fff;font-style:italic;border-radius:2px;padding:6.6666666667px 20px;border-radius:5px}.note p{margin-bottom:20px}main>section:not(:last-of-type){border-bottom:1px solid #112d4e;padding-bottom:20px}main h2{font-size:2rem;color:#3f72af;font-weight:700;margin:20px 0}@media screen and (min-width:970px){main h2{font-size:3rem;margin:40px 0}}main h3{font-size:20px;color:#3f72af;font-weight:700;margin:20px 0}@media screen and (min-width:970px){main h3{font-size:25px;margin:40px 0}}main h3 a{color:#6995ca;text-decoration:none}main h3 a:hover{text-decoration:underline}main a{font-weight:700;text-decoration:none;color:#6995ca;transition:.2s ease all}main a:hover{color:#112d4e;text-decoration:underline}.simply-countdown-inline{margin-top:20px;font-weight:700;font-size:1.3rem}.sc-inline-header{color:#f9f7f7;font-weight:100;font-size:1.4rem;font-style:italic}.alert{position:fixed;bottom:-60px;left:0;right:0;margin:auto;padding:20px;display:table;background:#112d4e;color:#fff;border-radius:5px;font-size:1.3rem;opacity:0;transition:.3s ease all}.alert.show-after-header-cd.active{opacity:1;bottom:60px}.alert.show-after-header-cd a{font-weight:700;color:#f9f7f7}footer{background:#112d4e;color:#fff;text-align:center;padding:20px;margin-top:40px;font-weight:600}footer a{color:#c7d8eb;text-decoration:none;font-weight:700}footer a:hover{text-decoration:underline}.table-wrapper{width:100%;overflow-x:auto}.table-wrapper table{width:100%;margin-bottom:1rem;color:#212529;border-collapse:collapse;min-width:600px}.table-wrapper table th,.table-wrapper table td{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table-wrapper table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6;background-color:#f8f9fa;font-weight:700}.table-wrapper table tbody tr:nth-child(even){background-color:#f2f2f2}.table-wrapper table.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-wrapper table.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-wrapper table.table-bordered{border:1px solid #dee2e6}.table-wrapper table.table-bordered th,.table-wrapper table.table-bordered td{border:1px solid #dee2e6}.table-wrapper table.table-sm th,.table-wrapper table.table-sm td{padding:.3rem}.text-center{text-align:center}.text-right{text-align:right}.bold{font-weight:700}.ff-code{font-family:monospace;font-size:.9rem}.hide{display:none} \ No newline at end of file diff --git a/css/scss/demo.scss b/css/scss/demo.scss deleted file mode 100644 index 84906f4..0000000 --- a/css/scss/demo.scss +++ /dev/null @@ -1,343 +0,0 @@ -/** -* Project : simply-countdown -* File : demo -* Date : 27/06/2015 -* Author : Vincent Loy -*/ -@use "sass:color"; - -$color-main: #3F72AF; -$color-main-darken: #112D4E; -$color-main-lighten: #6995ca; -$color-secondary: #F9F7F7; -$color-secondary-lighten: #F9F7F7; -$color-white: #fff; - -$ff-base: "Wix Madefor Text", serif; -$ff-title: "Montserrat", sans-serif; - -$spacing: 20px; - -$tablet: "screen and (min-width: 970px)"; - -a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}table{border-collapse:collapse;border-spacing:0} - -* { - box-sizing: border-box; -} - -body, html { - font-family: $ff-base; - font-optical-sizing: auto; - font-style: normal; - overflow-x: hidden; - font-size: 16px; -} - -p { - font-size: 1.2rem; - line-height: 1.6; - font-weight: 400; - margin: $spacing 0 45px; -} - -h1, h2, h3 { - font-family: $ff-title; -} - - -header { - display: flex; - align-items: center; - justify-content: center; - background: $color-main-darken; - border-bottom: 5px solid $color-main; - padding: $spacing; - height: 450px; - - @media #{$tablet} { - height: 650px; - } - - .header-content { - text-align: center; - - h1 { - font-style: italic; - font-size: 2rem; - font-weight: 600; - color: $color-secondary-lighten; - - @media #{$tablet} { - font-size: 4rem; - } - } - - h2 { - font-size: 1.33rem; - color: $color-secondary-lighten; - margin: $spacing 0; - font-weight: 500; - - @media #{$tablet} { - font-size: 1.75rem; - margin: calc($spacing * 2) 0 calc($spacing * 3) 0; - } - } - - .repo-buttons { - display: flex; - align-items: center; - justify-content: center; - } - } -} - -.button { - color: $color-secondary-lighten; - border:2px solid $color-secondary-lighten; - border-radius: 2px; - text-decoration: none; - padding: calc($spacing / 2); - transition: 0.1s ease-in-out all; - display: block; - margin: 0 $spacing $spacing $spacing; - - span { - display: none; - } - - @media #{$tablet} { - margin-bottom: 0; - - span { - display: inline; - } - } - - > i { - @media #{$tablet} { - margin-right: calc($spacing / 2); - } - } - - &:hover { - background: $color-secondary-lighten; - color: $color-main-darken; - } -} - -.container { - width: 100%; - padding: 0 $spacing; - margin: auto; - - @media #{$tablet} { - width: 960px; - } -} - -.note { - background: $color-main; - color: $color-white; - font-style: italic; - border-radius: 2px; - padding: calc($spacing / 3) $spacing; - border-radius: 5px; - - p { - margin-bottom: $spacing; - } -} - -main { - > section:not(:last-of-type) { - border-bottom: 1px solid $color-main-darken; - padding-bottom: $spacing; - } - - h2 { - font-size: 2rem; - color: $color-main; - font-weight: 700; - margin: $spacing 0; - - @media #{$tablet} { - font-size: 3rem; - margin: $spacing * 2 0; - } - } - - h3 { - font-size: 20px; - color: $color-main; - font-weight: 700; - margin: $spacing 0; - - @media #{$tablet} { - font-size: 25px; - margin: calc($spacing * 2) 0; - } - - a { - color: $color-main-lighten; - text-decoration: none; - &:hover { - text-decoration: underline; - } - } - } - - a { - font-weight: bold; - text-decoration: none; - color: $color-main-lighten; - transition: 0.2s ease all; - - &:hover { - color: $color-main-darken; - text-decoration: underline; - } - } -} - -.simply-countdown-inline { - margin-top: $spacing; - font-weight: bold; - font-size: 1.3rem; -} - -.sc-inline-header { - color: $color-secondary-lighten; - font-weight: 100; - font-size: 1.4rem; - font-style: italic; -} - -.alert { - position: fixed; - bottom: -60px; - left: 0; - right: 0; - margin: auto; - padding: 20px; - display: table; - background: $color-main-darken; - color: white; - border-radius: 5px; - font-size: 1.3rem; - opacity: 0; - transition: 0.3s ease all; - - &.show-after-header-cd { - - &.active { - opacity: 1; - bottom: 60px; - } - - a { - font-weight: bold; - color: $color-secondary-lighten; - } - } -} - -footer { - background: $color-main-darken; - color: $color-white; - text-align: center; - padding: $spacing; - margin-top: $spacing * 2; - font-weight: 600; - - a { - color: color.adjust($color-main-lighten, $lightness: 25%); - text-decoration: none; - font-weight: bold; - - &:hover{ - text-decoration: underline; - } - } -} - -.table-wrapper { - width: 100%; - overflow-x: auto; - - table { - width: 100%; - margin-bottom: 1rem; - color: #212529; - border-collapse: collapse; - min-width: 600px; // Ensures the table maintains its width with a scrollbar on smaller screens - - th, - td { - padding: 0.75rem; - vertical-align: top; - border-top: 1px solid #dee2e6; - } - - thead { - th { - vertical-align: bottom; - border-bottom: 2px solid #dee2e6; - background-color: #f8f9fa; - font-weight: bold; - } - } - - tbody { - tr { - &:nth-child(even) { - background-color: #f2f2f2; - } - } - } - - &.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(0, 0, 0, 0.05); - } - - &.table-hover tbody tr:hover { - background-color: rgba(0, 0, 0, 0.075); - } - - &.table-bordered { - border: 1px solid #dee2e6; - - th, - td { - border: 1px solid #dee2e6; - } - } - - &.table-sm th, - &.table-sm td { - padding: 0.3rem; - } - } -} - -.text-center { - text-align: center; -} - -.text-right { - text-align: right; -} - -.bold { - font-weight: bold; -} - -.ff-code { - font-family: monospace; - font-size: 0.9rem; -} - -.hide { - display: none; -} \ No newline at end of file diff --git a/css/scss/simplyCountdown.theme.default.scss b/css/scss/simplyCountdown.theme.default.scss deleted file mode 100644 index 00b1dc3..0000000 --- a/css/scss/simplyCountdown.theme.default.scss +++ /dev/null @@ -1,31 +0,0 @@ -/** -* Project : simply-countdown -* File : simplyCountdown.theme.default -* Date : 27/06/2015 -* Author : Vincent Loy -*/ - -.simply-countdown { - overflow: hidden; - display: table; - font-family: 'Arial', sans-serif; - - & > .simply-section { - width: 50px; - height: 50px; - padding: 50px; - display: flex; - align-items: center; - justify-content: center; - float: left; - margin: 15px; - background: #fff; - box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); - - .simply-amount, - .simply-word { - display: block; - text-align: center; - } - } -} diff --git a/css/scss/simplyCountdown.theme.losange.scss b/css/scss/simplyCountdown.theme.losange.scss deleted file mode 100644 index 051f28c..0000000 --- a/css/scss/simplyCountdown.theme.losange.scss +++ /dev/null @@ -1,35 +0,0 @@ -/** -* Project : simply-countdown -* File : simplyCountdown.theme.losange -* Date : 27/06/2015 -* Author : Vincent Loy -*/ - -.simply-countdown-losange { - overflow: visible; - display: table; - font-family: 'Arial', sans-serif; - - > .simply-section { - width: 100px; - height: 100px; - display: flex; - justify-content: center; - align-items: center; - transform: rotate(-45deg); - float: left; - margin: 30px; - background: #024772; - - > div { - transform: rotate(45deg); - .simply-amount, - .simply-word { - display: block; - color: #fff; - font-weight: bold; - text-align: center; - } - } - } -} diff --git a/css/simplyCountdown.theme.custom.css b/css/simplyCountdown.theme.custom.css deleted file mode 100644 index 38452a9..0000000 --- a/css/simplyCountdown.theme.custom.css +++ /dev/null @@ -1,29 +0,0 @@ -/** -* Project : simply-countdown -* File : simplyCountdown.theme.custom -* Author : Your Name -*/ - -.simply-countdown { - /* The countdown */ -} -.simply-countdown > .simply-section { - /* coutndown blocks */ -} - -.simply-countdown > .simply-section > div { - /* countdown block inner div */ -} - -.simply-countdown > .simply-section .simply-amount, -.simply-countdown > .simply-section .simply-word { - /* amounts and words */ -} - -.simply-countdown > .simply-section .simply-amount { - /* amounts */ -} - -.simply-countdown > .simply-section .simply-word { - /* words */ -} diff --git a/css/simplyCountdown.theme.default.css b/css/simplyCountdown.theme.default.css deleted file mode 100644 index 1efede8..0000000 --- a/css/simplyCountdown.theme.default.css +++ /dev/null @@ -1 +0,0 @@ -.simply-countdown{overflow:hidden;display:table;font-family:"Arial",sans-serif}.simply-countdown>.simply-section{width:50px;height:50px;padding:50px;display:flex;align-items:center;justify-content:center;float:left;margin:15px;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.24)}.simply-countdown>.simply-section .simply-amount,.simply-countdown>.simply-section .simply-word{display:block;text-align:center} \ No newline at end of file diff --git a/css/simplyCountdown.theme.losange.css b/css/simplyCountdown.theme.losange.css deleted file mode 100644 index ac5262a..0000000 --- a/css/simplyCountdown.theme.losange.css +++ /dev/null @@ -1 +0,0 @@ -.simply-countdown-losange{overflow:visible;display:table;font-family:"Arial",sans-serif}.simply-countdown-losange>.simply-section{width:100px;height:100px;display:flex;justify-content:center;align-items:center;transform:rotate(-45deg);float:left;margin:30px;background:#024772}.simply-countdown-losange>.simply-section>div{transform:rotate(45deg)}.simply-countdown-losange>.simply-section>div .simply-amount,.simply-countdown-losange>.simply-section>div .simply-word{display:block;color:#fff;font-weight:700;text-align:center} \ No newline at end of file diff --git a/dev/simplyCountdown.js b/dev/simplyCountdown.js deleted file mode 100644 index 8a775b7..0000000 --- a/dev/simplyCountdown.js +++ /dev/null @@ -1,325 +0,0 @@ -/* global Symbol */ - -/*! -* Project : simply-countdown -* Date : 06/12/2024 -* License : MIT -* Version : 2.0.1 -* Author : Vincent Loy -* Contributors : -* - Justin Beasley -* - Nathan Smith -*/ -(function (exports) { - 'use strict'; - - /** - * Function that merge user parameters with defaults one. - * @param output - * @returns {*|{}} - */ - let extend = function (output) { - let obj; - let out = output || {}; - - for (let i = 1; i < arguments.length; i += 1) { - obj = arguments[i]; - const keys = Object.keys(obj); - - if (keys.length) { - for (let i2 = 0; i2 < keys.length; i2 += 1) { - let key = keys[i2]; - - if (Object.prototype.hasOwnProperty.call(obj, key)) { - if (typeof obj[key] === 'object') { - extend(out[key], obj[key]); - } else { - out[key] = obj[key]; - } - } - } - } - } - - return out; - }; - - let isIterableElement = (val) => { - return val !== null && Symbol.iterator in Object(val); - }; - - /** - * Function that create a countdown section - * @param countdown - * @param parameters - * @param typeClass - * @returns {{full: (*|Element), amount: (*|Element), word: (*|Element)}} - */ - let createCountdownElt = (countdown, parameters, typeClass) => { - let sectionTag = document.createElement('div'); - let amountTag = document.createElement('span'); - let wordTag = document.createElement('span'); - let innerSectionTag = document.createElement('div'); - - innerSectionTag.appendChild(amountTag); - innerSectionTag.appendChild(wordTag); - sectionTag.appendChild(innerSectionTag); - - sectionTag.classList.add(parameters.sectionClass); - sectionTag.classList.add(typeClass); - amountTag.classList.add(parameters.amountClass); - wordTag.classList.add(parameters.wordClass); - - countdown.appendChild(sectionTag); - - return { - full: sectionTag, - amount: amountTag, - word: wordTag - }; - }; - - /** - * Function that create full countdown DOM elements calling createCountdownElt - * @param parameters - * @param countdown - * @returns {{days:(*|Element), hours:(*|Element), minutes:(*|Element), seconds:(*|Element)}} - */ - let createElements = (parameters, countdown) => { - let spanTag; - - if (!parameters.inline) { - return { - days: createCountdownElt(countdown, parameters, 'simply-days-section'), - hours: createCountdownElt(countdown, parameters, 'simply-hours-section'), - minutes: createCountdownElt(countdown, parameters, 'simply-minutes-section'), - seconds: createCountdownElt(countdown, parameters, 'simply-seconds-section') - }; - } - - spanTag = document.createElement('span'); - spanTag.classList.add(parameters.inlineClass); - return spanTag; - }; - - /** - * simplyCountdown, create and display the coundtown. - * @param elt - * @param args (parameters) - */ - exports.simplyCountdown = (elt, args) => { - const eltProto = Object.getPrototypeOf(elt); - let parameters = extend({ - year: 2015, - month: 6, - day: 28, - hours: 0, - minutes: 0, - seconds: 0, - words: { - days: {lambda: (root, n) => {return n > 1 ? root + "s" : root }, root: 'day'}, - hours: {lambda: (root, n) => {return n > 1 ? root + "s" : root }, root: 'hour'}, - minutes: {lambda: (root, n) => {return n > 1 ? root + "s" : root }, root: 'minute'}, - seconds: {lambda: (root, n) => {return n > 1 ? root + "s" : root }, root: 'second'} - }, - plural: true, - inline: false, - inlineSeparator: ', ', - enableUtc: false, - onEnd: () => { - }, - refresh: 1000, - inlineClass: 'simply-countdown-inline', - sectionClass: 'simply-section', - amountClass: 'simply-amount', - wordClass: 'simply-word', - zeroPad: false, - removeZeroUnits: false, - countUp: false - }, args); - let interval; - let targetDate; - let now; - let secondsLeft; - let days; - let hours; - let minutes; - let seconds; - let cd; - - // console.log(typeof elt); - // - if (eltProto === String.prototype) { - cd = document.querySelectorAll(elt); - } else { - cd = elt; - } - - if (parameters.enableUtc) { - // Use UTC for target date - targetDate = new Date(Date.UTC( - parameters.year, - parameters.month - 1, - parameters.day, - parameters.hours, - parameters.minutes, - parameters.seconds - )); - } else { - // Use local time for target date - targetDate = new Date( - parameters.year, - parameters.month - 1, - parameters.day, - parameters.hours, - parameters.minutes, - parameters.seconds - ); - } - - let runCountdown = (theCountdown) => { - let countdown = theCountdown; - let fullCountDown = createElements(parameters, countdown); - let refresh; - - refresh = function () { - let dayWord; - let hourWord; - let minuteWord; - let secondWord; - - let updateDisplayDate = () => { - days = parseInt(secondsLeft / 86400, 10); - secondsLeft %= 86400; - - hours = parseInt(secondsLeft / 3600, 10); - secondsLeft %= 3600; - - minutes = parseInt(secondsLeft / 60, 10); - seconds = parseInt(secondsLeft % 60, 10); - }; - - if (parameters.enableUtc) { - // Calculate "now" in UTC - now = new Date(); - now = new Date(Date.UTC( - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate(), - now.getUTCHours(), - now.getUTCMinutes(), - now.getUTCSeconds() - )); - } else { - // Calculate "now" in local time - now = new Date(); - } - - secondsLeft = Math.floor((targetDate - now.getTime()) / 1000); - - if (secondsLeft > 0) { - updateDisplayDate(); - } else if (parameters.countUp) { - secondsLeft = (now.getTime() - targetDate) / 1000; - updateDisplayDate(); - } else { - days = 0; - hours = 0; - minutes = 0; - seconds = 0; - window.clearInterval(interval); - parameters.onEnd(); - } - - let getWord = (obj, n) => { - return obj.hasOwnProperty('lambda') - ? obj.lambda(obj.root, n) - : obj.root - }; - let words = parameters.words; - dayWord = getWord(words.days, days); - hourWord = getWord(words.hours, hours); - minuteWord = getWord(words.minutes, minutes); - secondWord = getWord(words.seconds, seconds); - - /* display an inline countdown into a span tag */ - if (parameters.inline) { - let displayStr = ''; - - if (!(parameters.removeZeroUnits && days === 0)) { - displayStr += `${days} ${dayWord}${parameters.inlineSeparator}`; - } - - if (!(parameters.removeZeroUnits && days === 0 && hours === 0)) { - displayStr += `${hours} ${hourWord}${parameters.inlineSeparator}`; - } - - if (!(parameters.removeZeroUnits && days === 0 && hours === 0 && minutes === 0)) { - displayStr += `${minutes} ${minuteWord}${parameters.inlineSeparator}`; - } - - // Seconds should always be displayed - displayStr += `${seconds} ${secondWord}`; - - countdown.innerHTML = displayStr.replace(/, $/, ''); // Remove trailing comma if any - } else { - if (!(parameters.removeZeroUnits && days === 0)) { - fullCountDown.days.amount.textContent = (parameters.zeroPad && days.toString().length < 2 ? '0' : '') + days; - fullCountDown.days.word.textContent = dayWord; - fullCountDown.days.full.style.display = ''; - } else { - fullCountDown.days.full.style.display = 'none'; - } - - if (!(parameters.removeZeroUnits && days === 0 && hours === 0)) { - fullCountDown.hours.amount.textContent = (parameters.zeroPad && hours.toString().length < 2 ? '0' : '') + hours; - fullCountDown.hours.word.textContent = hourWord; - fullCountDown.hours.full.style.display = ''; - } else { - fullCountDown.hours.full.style.display = 'none'; - } - - if (!(parameters.removeZeroUnits && days === 0 && hours === 0 && minutes === 0)) { - fullCountDown.minutes.amount.textContent = (parameters.zeroPad && minutes.toString().length < 2 ? '0' : '') + minutes; - fullCountDown.minutes.word.textContent = minuteWord; - fullCountDown.minutes.full.style.display = ''; - } else { - fullCountDown.minutes.full.style.display = 'none'; - } - - // Seconds should always be displayed - fullCountDown.seconds.amount.textContent = (parameters.zeroPad && seconds.toString().length < 2 ? '0' : '') + seconds; - fullCountDown.seconds.word.textContent = secondWord; - fullCountDown.seconds.full.style.display = ''; - } - }; - - // Refresh immediately to prevent a Flash of Unstyled Content - refresh(); - interval = window.setInterval(refresh, parameters.refresh); - }; - - if (!isIterableElement(cd)) { - runCountdown(cd); - } else { - Array.prototype.forEach.call(cd, (cdElt) => { - runCountdown(cdElt); - }); - } - }; -}(window)); - -/* global jQuery, simplyCountdown */ -if (window.jQuery) { - (function ($, simplyCountdown) { - 'use strict'; - - function simplyCountdownify(el, options) { - simplyCountdown(el, options); - } - - $.fn.simplyCountdown = function (options) { - return simplyCountdownify(this.selector, options); - }; - }(jQuery, simplyCountdown)); -} diff --git a/dist/simplyCountdown.js b/dist/simplyCountdown.js new file mode 100644 index 0000000..0837e68 --- /dev/null +++ b/dist/simplyCountdown.js @@ -0,0 +1,190 @@ +const w = (n, e, o, t, l, d) => { + const u = document.createElement("div"); + u.className = `${n} ${d.sectionClass}`; + const c = document.createElement("div"), a = document.createElement("span"), r = document.createElement("span"); + return a.className = `${e} ${d.amountClass}`, r.className = `${o} ${d.wordClass}`, a.textContent = String(t), r.textContent = l, c.appendChild(a), c.appendChild(r), u.appendChild(c), u; +}, b = (n, e, o) => { + const t = n.querySelector(".simply-amount"), l = n.querySelector(".simply-word"); + t && (t.textContent = String(e)), l && (l.textContent = o); +}, E = (n, e) => { + const o = "simply-amount", t = "simply-word", l = w("simply-section simply-days-section", o, t, 0, "day", e), d = w("simply-section simply-hours-section", o, t, 0, "hour", e), u = w("simply-section simply-minutes-section", o, t, 0, "minute", e), c = w("simply-section simply-seconds-section", o, t, 0, "second", e); + return n.appendChild(l), n.appendChild(d), n.appendChild(u), n.appendChild(c), { + days: l, + hours: d, + minutes: u, + seconds: c + }; +}; +/*! + * Project : simplyCountdown.js + * Date : 2024-12-27 + * License : MIT + * Version : 3.0.0 + * Author : Vincent Loy-Serre + * Contributors : + * - Justin Beasley + * - Nathan Smith + * - Mehdi Rezaei + * - mira01 + */ +const m = { + year: 2024, + month: 12, + day: 25, + hours: 0, + minutes: 0, + seconds: 0, + words: { + days: { lambda: (n, e) => e > 1 ? n + "s" : n, root: "day" }, + hours: { lambda: (n, e) => e > 1 ? n + "s" : n, root: "hour" }, + minutes: { lambda: (n, e) => e > 1 ? n + "s" : n, root: "minute" }, + seconds: { lambda: (n, e) => e > 1 ? n + "s" : n, root: "second" } + }, + plural: !0, + inline: !1, + inlineSeparator: ", ", + enableUtc: !1, + onEnd: () => { + }, + refresh: 1e3, + inlineClass: "simply-countdown-inline", + sectionClass: "simply-section", + amountClass: "simply-amount", + wordClass: "simply-word", + zeroPad: !1, + countUp: !1, + removeZeroUnits: !1, + onStop: () => { + }, + onResume: () => { + }, + onUpdate: () => { + } +}, N = (n) => n instanceof NodeList; +function I(n, e) { + return `${e.zeroPad ? String(n.value).padStart(2, "0") : n.value} ${e.words[n.word].lambda(e.words[n.word].root, n.value)}`; +} +function S(n, e, o) { + return o.removeZeroUnits ? n.value !== 0 || e.some((t) => t.value !== 0) : !0; +} +function M(n, e, o) { + const t = n.filter((l, d) => S(l, n.slice(0, d), e)).map((l) => I(l, e)).join(e.inlineSeparator); + o.innerHTML = t; +} +function $(n, e, o) { + n.forEach((t, l) => { + t.word === "seconds" || S(t, n.slice(0, l), e) ? (b( + o[t.word], + e.zeroPad ? String(t.value).padStart(2, "0") : t.value, + e.words[t.word].lambda(e.words[t.word].root, t.value) + ), o[t.word].style.display = "") : o[t.word].style.display = "none"; + }); +} +const y = (n, e) => { + let o = { + isPaused: !1, + interval: null, + targetDate: /* @__PURE__ */ new Date() + }; + const t = (s) => s.enableUtc ? new Date(Date.UTC(s.year, s.month - 1, s.day, s.hours, s.minutes, s.seconds)) : new Date(s.year, s.month - 1, s.day, s.hours, s.minutes, s.seconds); + o.targetDate = t(e); + let l = null; + e.inline && (l = document.createElement("span"), l.className = e.inlineClass, n.appendChild(l)); + const d = e.inline ? null : E(n, { + sectionClass: e.sectionClass, + amountClass: e.amountClass, + wordClass: e.wordClass + }), u = () => { + const s = e.enableUtc ? new Date( + Date.UTC( + (/* @__PURE__ */ new Date()).getUTCFullYear(), + (/* @__PURE__ */ new Date()).getUTCMonth(), + (/* @__PURE__ */ new Date()).getUTCDate(), + (/* @__PURE__ */ new Date()).getUTCHours(), + (/* @__PURE__ */ new Date()).getUTCMinutes(), + (/* @__PURE__ */ new Date()).getUTCSeconds() + ) + ) : /* @__PURE__ */ new Date(); + let i = e.countUp ? s.getTime() - o.targetDate.getTime() : o.targetDate.getTime() - s.getTime(); + i <= 0 && !e.countUp && (i = 0, o.interval !== null && clearInterval(o.interval), e.onEnd && e.onEnd()); + const f = Math.floor(i / (1e3 * 60 * 60 * 24)); + i -= f * 1e3 * 60 * 60 * 24; + const C = Math.floor(i / (1e3 * 60 * 60)); + i -= C * 1e3 * 60 * 60; + const v = Math.floor(i / (1e3 * 60)); + i -= v * 1e3 * 60; + const g = Math.floor(i / 1e3); + e.inline && l ? M([ + { value: f, word: "days" }, + { + value: C, + word: "hours" + }, + { + value: v, + word: "minutes" + }, + { + value: g, + word: "seconds" + } + ], e, l) : d && $([ + { value: f, word: "days" }, + { + value: C, + word: "hours" + }, + { + value: v, + word: "minutes" + }, + { + value: g, + word: "seconds" + } + ], e, d); + }, c = () => { + o.interval = setInterval(u, e.refresh), u(); + }, a = () => { + var s; + o.interval !== null && (clearInterval(o.interval), o.interval = null), o.isPaused = !0, (s = e.onStop) == null || s.call(e); + }, r = () => { + var s; + o.isPaused && (c(), o.isPaused = !1, (s = e.onResume) == null || s.call(e)); + }, U = (s) => { + var i; + Object.assign(e, s), (s.year !== void 0 || s.month !== void 0 || s.day !== void 0 || s.hours !== void 0 || s.minutes !== void 0 || s.seconds !== void 0) && (o.targetDate = t(e)), (i = e.onUpdate) == null || i.call(e, s), o.isPaused || (o.interval && clearInterval(o.interval), c()); + }, p = () => ({ ...o }); + c(); + const h = new MutationObserver((s) => { + s.forEach((i) => { + i.removedNodes.forEach((f) => { + f === n && (o.interval !== null && clearInterval(o.interval), h.disconnect()); + }); + }); + }); + return n.parentNode && h.observe(n.parentNode, { childList: !0 }), { + stopCountdown: a, + resumeCountdown: r, + updateCountdown: U, + getState: p + }; +}, D = (n) => { + const e = n; + return e.stopCountdown = () => n.forEach((o) => o.stopCountdown()), e.resumeCountdown = () => n.forEach((o) => o.resumeCountdown()), e.updateCountdown = (o) => n.forEach((t) => t.updateCountdown(o)), e.getState = () => n.map((o) => o.getState()), e; +}, x = (n, e = m) => { + const o = { ...m, ...e }; + if (typeof n == "string") { + const t = document.querySelectorAll(n), l = Array.from(t).map((d) => y(d, o)); + return l.length === 1 ? l[0] : D(l); + } + if (N(n)) { + const t = Array.from(n).map((l) => y(l, o)); + return t.length === 1 ? t[0] : D(t); + } + return y(n, o); +}; +export { + x as default +}; +//# sourceMappingURL=simplyCountdown.js.map diff --git a/dist/simplyCountdown.js.map b/dist/simplyCountdown.js.map new file mode 100644 index 0000000..dbe11d7 --- /dev/null +++ b/dist/simplyCountdown.js.map @@ -0,0 +1 @@ +{"version":3,"file":"simplyCountdown.js","sources":["../src/core/dom.ts","../src/core/simplyCountdown.ts"],"sourcesContent":["/**\n * Creates a countdown section element\n */\nexport const createCountdownSection = (\n sectionClass: string,\n amountClass: string,\n wordClass: string,\n amount: number,\n word: string,\n params: {\n sectionClass: string;\n amountClass: string;\n wordClass: string;\n }\n): HTMLElement => {\n const section = document.createElement(\"div\");\n section.className = `${sectionClass} ${params.sectionClass}`;\n\n const wrap = document.createElement(\"div\");\n const amount_elem = document.createElement(\"span\");\n const word_elem = document.createElement(\"span\");\n\n amount_elem.className = `${amountClass} ${params.amountClass}`;\n word_elem.className = `${wordClass} ${params.wordClass}`;\n\n amount_elem.textContent = String(amount);\n word_elem.textContent = word;\n\n wrap.appendChild(amount_elem);\n wrap.appendChild(word_elem);\n section.appendChild(wrap);\n\n return section;\n};\n\n/**\n * Retrieves a countdown section element from a container\n */\nexport const getCountdownSection = (sectionClass: string, container: HTMLElement): HTMLElement | null => {\n return container.querySelector(`.simply-section.${sectionClass}`);\n};\n\n/**\n * Updates a countdown section element\n */\nexport const updateCountdownSection = (section: HTMLElement, amount: number | string, word: string): void => {\n const amountElement = section.querySelector(\".simply-amount\");\n const wordElement = section.querySelector(\".simply-word\");\n\n if (amountElement) {\n amountElement.textContent = String(amount);\n }\n if (wordElement) {\n wordElement.textContent = word;\n }\n};\n\n/**\n * Creates all countdown elements\n */\nexport const createCountdown = (\n container: HTMLElement,\n params: {\n sectionClass: string;\n amountClass: string;\n wordClass: string;\n }\n): {\n days: HTMLElement;\n hours: HTMLElement;\n minutes: HTMLElement;\n seconds: HTMLElement;\n} => {\n const amountCls = \"simply-amount\";\n const wordCls = \"simply-word\";\n\n const days = createCountdownSection(\"simply-section simply-days-section\", amountCls, wordCls, 0, \"day\", params);\n const hours = createCountdownSection(\"simply-section simply-hours-section\", amountCls, wordCls, 0, \"hour\", params);\n const minutes = createCountdownSection(\"simply-section simply-minutes-section\", amountCls, wordCls, 0, \"minute\", params);\n const seconds = createCountdownSection(\"simply-section simply-seconds-section\", amountCls, wordCls, 0, \"second\", params);\n\n container.appendChild(days);\n container.appendChild(hours);\n container.appendChild(minutes);\n container.appendChild(seconds);\n\n return {\n days,\n hours,\n minutes,\n seconds,\n };\n};\n","/*!\n * Project : simplyCountdown.js\n * Date : 2024-12-27\n * License : MIT\n * Version : 3.0.0\n * Author : Vincent Loy-Serre\n * Contributors :\n * - Justin Beasley\n * - Nathan Smith\n * - Mehdi Rezaei\n * - mira01\n */\n\nimport type { CountdownParameters, CountdownSelector, CountdownState, CountdownController, CountdownControllerArray } from \"../types\";\nimport { createCountdown, updateCountdownSection } from \"./dom\";\n\nconst defaultParams: CountdownParameters = {\n year: 2024,\n month: 12,\n day: 25,\n hours: 0,\n minutes: 0,\n seconds: 0,\n words: {\n days: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"day\" },\n hours: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"hour\" },\n minutes: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"minute\" },\n seconds: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"second\" },\n },\n plural: true,\n inline: false,\n inlineSeparator: \", \",\n enableUtc: false,\n onEnd: () => {},\n refresh: 1000,\n inlineClass: \"simply-countdown-inline\",\n sectionClass: \"simply-section\",\n amountClass: \"simply-amount\",\n wordClass: \"simply-word\",\n zeroPad: false,\n countUp: false,\n removeZeroUnits: false,\n onStop: () => {},\n onResume: () => {},\n onUpdate: () => {},\n};\n\nconst isNodeList = (element: CountdownSelector): element is NodeListOf => {\n return element instanceof NodeList;\n};\n\ninterface TimeUnit {\n value: number;\n word: keyof CountdownParameters[\"words\"];\n element?: HTMLElement;\n}\n\n/**\n * Formats a time unit with optional zero padding and pluralization\n * @param unit - The time unit object containing value and word properties\n * @param params - The countdown parameters containing formatting options and word definitions\n * @returns A formatted string containing the value and pluralized word for the time unit\n * @example\n * // With zeroPad: true\n * formatTimeUnit({value: 5, word: 'days'}, params) // returns \"05 days\"\n * // With zeroPad: false\n * formatTimeUnit({value: 5, word: 'days'}, params) // returns \"5 days\"\n */\nfunction formatTimeUnit(unit: TimeUnit, params: CountdownParameters): string {\n const value = params.zeroPad ? String(unit.value).padStart(2, \"0\") : unit.value;\n return `${value} ${params.words[unit.word].lambda(params.words[unit.word].root, unit.value)}`;\n}\n\n/**\n * Determines whether a time unit should be displayed based on its value and the values of previous units\n * @param unit - The current time unit to evaluate\n * @param previousUnits - Array of time units that come before the current unit\n * @param params - Configuration parameters for the countdown\n * @returns True if the unit should be displayed, false otherwise\n *\n * If removeZeroUnits is false in params, always returns true.\n * Otherwise, returns true if either:\n * - The current unit value is not zero\n * - Any previous unit has a non-zero value\n */\nfunction shouldDisplay(unit: TimeUnit, previousUnits: TimeUnit[], params: CountdownParameters): boolean {\n if (!params.removeZeroUnits) return true;\n return unit.value !== 0 || previousUnits.some((u) => u.value !== 0);\n}\n\n/**\n * Displays the countdown timer inline within the specified HTML element.\n *\n * @param timeUnits - Array of time units containing values and labels for display\n * @param params - Configuration parameters for the countdown display\n * @param element - The HTML element where the countdown will be rendered\n *\n * @remarks\n * The function filters and formats time units based on display rules, then joins them with\n * the specified separator from params.inlineSeparator before setting the element's innerHTML.\n */\nfunction displayInline(timeUnits: TimeUnit[], params: CountdownParameters, element: HTMLElement): void {\n const displayStr = timeUnits\n .filter((unit, index) => shouldDisplay(unit, timeUnits.slice(0, index), params))\n .map((unit) => formatTimeUnit(unit as { value: number; word: keyof typeof params.words }, params))\n .join(params.inlineSeparator);\n\n element.innerHTML = displayStr;\n}\n\n/**\n * Updates the display of time units in the countdown based on their values and display conditions\n * @param timeUnits - Array of TimeUnit objects containing the time values and their corresponding words\n * @param params - Configuration parameters for the countdown display\n * @param countdown - DOM elements representing the countdown display sections\n * @returns void\n *\n * @remarks\n * This function iterates through each time unit and determines whether it should be shown based on:\n * - If it's the seconds unit (always shown)\n * - If it meets display criteria based on previous units\n *\n * For units that should be shown, it:\n * - Updates the display value (with optional zero padding)\n * - Updates the word label using the configured lambda function\n * - Shows the unit's DOM element\n *\n * For units that shouldn't be shown, it hides their DOM elements\n */\nfunction displayBlocks(timeUnits: TimeUnit[], params: CountdownParameters, countdown: any): void {\n timeUnits.forEach((unit, index) => {\n const shouldShow = unit.word === \"seconds\" || shouldDisplay(unit, timeUnits.slice(0, index), params);\n\n if (shouldShow) {\n updateCountdownSection(\n countdown[unit.word],\n params.zeroPad ? String(unit.value).padStart(2, \"0\") : unit.value,\n params.words[unit.word].lambda(params.words[unit.word].root, unit.value)\n );\n countdown[unit.word].style.display = \"\";\n } else {\n countdown[unit.word].style.display = \"none\";\n }\n });\n}\n\n/**\n * Creates a countdown instance that manages the countdown timer functionality.\n *\n * @param targetElement - The HTML element where the countdown will be rendered\n * @param parameters - Configuration parameters for the countdown\n *\n * @returns A controller object with methods to control the countdown:\n * - stopCountdown: Pauses the countdown and triggers onStop callback\n * - resumeCountdown: Resumes a paused countdown and triggers onResume callback\n * - updateCountdown: Updates countdown parameters and triggers onUpdate callback\n * - getState: Returns current state of the countdown\n */\nconst createCountdownInstance = (targetElement: HTMLElement, parameters: CountdownParameters): CountdownController => {\n let state: CountdownState = {\n isPaused: false,\n interval: null,\n targetDate: new Date(),\n };\n\n const getTargetDate = (params: CountdownParameters): Date => {\n return params.enableUtc\n ? new Date(Date.UTC(params.year, params.month - 1, params.day, params.hours, params.minutes, params.seconds))\n : new Date(params.year, params.month - 1, params.day, params.hours, params.minutes, params.seconds);\n };\n\n state.targetDate = getTargetDate(parameters);\n\n // Create span element for inline mode\n let inlineElement: HTMLElement | null = null;\n if (parameters.inline) {\n inlineElement = document.createElement(\"span\");\n inlineElement.className = parameters.inlineClass;\n targetElement.appendChild(inlineElement);\n }\n\n const countdown = parameters.inline\n ? null\n : createCountdown(targetElement, {\n sectionClass: parameters.sectionClass,\n amountClass: parameters.amountClass,\n wordClass: parameters.wordClass,\n });\n\n const refresh = () => {\n // Fix UTC current date handling\n const currentDate = parameters.enableUtc\n ? new Date(\n Date.UTC(\n new Date().getUTCFullYear(),\n new Date().getUTCMonth(),\n new Date().getUTCDate(),\n new Date().getUTCHours(),\n new Date().getUTCMinutes(),\n new Date().getUTCSeconds()\n )\n )\n : new Date();\n\n let diff = parameters.countUp ? currentDate.getTime() - state.targetDate.getTime() : state.targetDate.getTime() - currentDate.getTime();\n\n if (diff <= 0 && !parameters.countUp) {\n diff = 0;\n // Clear interval before calling onEnd to prevent multiple calls\n if (state.interval !== null) {\n clearInterval(state.interval);\n }\n\n if (parameters.onEnd) {\n parameters.onEnd();\n }\n }\n\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\n diff -= days * 1000 * 60 * 60 * 24;\n\n const hours = Math.floor(diff / (1000 * 60 * 60));\n diff -= hours * 1000 * 60 * 60;\n\n const minutes = Math.floor(diff / (1000 * 60));\n diff -= minutes * 1000 * 60;\n\n const seconds = Math.floor(diff / 1000);\n\n if (parameters.inline && inlineElement) {\n const timeUnits: TimeUnit[] = [\n { value: days, word: \"days\" as keyof CountdownParameters[\"words\"] },\n {\n value: hours,\n word: \"hours\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: minutes,\n word: \"minutes\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: seconds,\n word: \"seconds\" as keyof CountdownParameters[\"words\"],\n },\n ];\n displayInline(timeUnits, parameters, inlineElement);\n } else if (countdown) {\n const timeUnits: TimeUnit[] = [\n { value: days, word: \"days\" as keyof CountdownParameters[\"words\"] },\n {\n value: hours,\n word: \"hours\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: minutes,\n word: \"minutes\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: seconds,\n word: \"seconds\" as keyof CountdownParameters[\"words\"],\n },\n ];\n displayBlocks(timeUnits, parameters, countdown);\n }\n };\n\n const startInterval = () => {\n state.interval = setInterval(refresh, parameters.refresh);\n refresh();\n };\n\n const stopCountdown = () => {\n if (state.interval !== null) {\n clearInterval(state.interval);\n state.interval = null;\n }\n state.isPaused = true;\n parameters.onStop?.();\n };\n\n const resumeCountdown = () => {\n if (state.isPaused) {\n startInterval();\n state.isPaused = false;\n parameters.onResume?.();\n }\n };\n\n const updateCountdown = (newParams: Partial) => {\n Object.assign(parameters, newParams);\n if (\n newParams.year !== undefined ||\n newParams.month !== undefined ||\n newParams.day !== undefined ||\n newParams.hours !== undefined ||\n newParams.minutes !== undefined ||\n newParams.seconds !== undefined\n ) {\n state.targetDate = getTargetDate(parameters);\n }\n\n parameters.onUpdate?.(newParams);\n\n if (!state.isPaused) {\n if (state.interval) {\n clearInterval(state.interval);\n }\n startInterval();\n }\n };\n\n const getState = () => ({ ...state });\n\n // Start the countdown\n startInterval();\n\n // Cleanup on element removal\n const observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.removedNodes.forEach((node) => {\n if (node === targetElement) {\n if (state.interval !== null) {\n clearInterval(state.interval);\n }\n observer.disconnect();\n }\n });\n });\n });\n\n if (targetElement.parentNode) {\n observer.observe(targetElement.parentNode, { childList: true });\n }\n\n // Return controller object\n return {\n stopCountdown,\n resumeCountdown,\n updateCountdown,\n getState,\n };\n};\n\n/**\n * Creates an enhanced array of countdown controllers with additional control methods.\n *\n * @param controllers - Array of individual countdown controllers to be combined\n * @returns An array of controllers enhanced with collective control methods:\n * - `stopCountdown()`: Stops all countdowns in the array\n * - `resumeCountdown()`: Resumes all countdowns in the array\n * - `updateCountdown(newParams)`: Updates all countdowns with new parameters\n * - `getState()`: Returns an array of states from all countdowns\n */\nconst createControllerArray = (controllers: CountdownController[]): CountdownControllerArray => {\n const array = controllers as CountdownControllerArray;\n\n array.stopCountdown = () => controllers.forEach((c) => c.stopCountdown());\n array.resumeCountdown = () => controllers.forEach((c) => c.resumeCountdown());\n array.updateCountdown = (newParams) => controllers.forEach((c) => c.updateCountdown(newParams));\n array.getState = () => controllers.map((c) => c.getState());\n\n return array;\n};\n\n/**\n * Creates a countdown timer on specified HTML elements\n * @param element - A CSS selector string, HTMLElement, or NodeList targeting the countdown container(s)\n * @param args - Optional configuration parameters for the countdown\n * @returns A CountdownController for single element or CountdownControllerArray for multiple elements\n */\nconst simplyCountdown = (\n element: CountdownSelector,\n args: Partial = defaultParams\n): CountdownController | CountdownControllerArray => {\n const parameters: CountdownParameters = { ...defaultParams, ...args };\n\n if (typeof element === \"string\") {\n const elements = document.querySelectorAll(element);\n const controllers = Array.from(elements).map((el) => createCountdownInstance(el, parameters));\n return controllers.length === 1 ? controllers[0] : createControllerArray(controllers);\n }\n\n if (isNodeList(element)) {\n const controllers = Array.from(element).map((el) => createCountdownInstance(el, parameters));\n return controllers.length === 1 ? controllers[0] : createControllerArray(controllers);\n }\n\n return createCountdownInstance(element, parameters);\n};\n\nexport default simplyCountdown;\n"],"names":["createCountdownSection","sectionClass","amountClass","wordClass","amount","word","params","section","wrap","amount_elem","word_elem","updateCountdownSection","amountElement","wordElement","createCountdown","container","amountCls","wordCls","days","hours","minutes","seconds","defaultParams","root","n","isNodeList","element","formatTimeUnit","unit","shouldDisplay","previousUnits","u","displayInline","timeUnits","displayStr","index","displayBlocks","countdown","createCountdownInstance","targetElement","parameters","state","getTargetDate","inlineElement","refresh","currentDate","diff","startInterval","stopCountdown","_a","resumeCountdown","updateCountdown","newParams","getState","observer","mutations","mutation","node","createControllerArray","controllers","array","c","simplyCountdown","args","elements","el"],"mappings":"AAGO,MAAMA,IAAyB,CAClCC,GACAC,GACAC,GACAC,GACAC,GACAC,MAKc;AACR,QAAAC,IAAU,SAAS,cAAc,KAAK;AAC5C,EAAAA,EAAQ,YAAY,GAAGN,CAAY,IAAIK,EAAO,YAAY;AAEpD,QAAAE,IAAO,SAAS,cAAc,KAAK,GACnCC,IAAc,SAAS,cAAc,MAAM,GAC3CC,IAAY,SAAS,cAAc,MAAM;AAE/C,SAAAD,EAAY,YAAY,GAAGP,CAAW,IAAII,EAAO,WAAW,IAC5DI,EAAU,YAAY,GAAGP,CAAS,IAAIG,EAAO,SAAS,IAE1CG,EAAA,cAAc,OAAOL,CAAM,GACvCM,EAAU,cAAcL,GAExBG,EAAK,YAAYC,CAAW,GAC5BD,EAAK,YAAYE,CAAS,GAC1BH,EAAQ,YAAYC,CAAI,GAEjBD;AACX,GAYaI,IAAyB,CAACJ,GAAsBH,GAAyBC,MAAuB;AACnG,QAAAO,IAAgBL,EAAQ,cAAc,gBAAgB,GACtDM,IAAcN,EAAQ,cAAc,cAAc;AAExD,EAAIK,MACcA,EAAA,cAAc,OAAOR,CAAM,IAEzCS,MACAA,EAAY,cAAcR;AAElC,GAKaS,IAAkB,CAC3BC,GACAT,MAUC;AACD,QAAMU,IAAY,iBACZC,IAAU,eAEVC,IAAOlB,EAAuB,sCAAsCgB,GAAWC,GAAS,GAAG,OAAOX,CAAM,GACxGa,IAAQnB,EAAuB,uCAAuCgB,GAAWC,GAAS,GAAG,QAAQX,CAAM,GAC3Gc,IAAUpB,EAAuB,yCAAyCgB,GAAWC,GAAS,GAAG,UAAUX,CAAM,GACjHe,IAAUrB,EAAuB,yCAAyCgB,GAAWC,GAAS,GAAG,UAAUX,CAAM;AAEvH,SAAAS,EAAU,YAAYG,CAAI,GAC1BH,EAAU,YAAYI,CAAK,GAC3BJ,EAAU,YAAYK,CAAO,GAC7BL,EAAU,YAAYM,CAAO,GAEtB;AAAA,IACH,MAAAH;AAAA,IACA,OAAAC;AAAA,IACA,SAAAC;AAAA,IACA,SAAAC;AAAA,EACJ;AACJ;AC5FA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,MAAMC,IAAqC;AAAA,EACvC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,IACH,MAAM,EAAE,QAAQ,CAACC,GAAMC,MAAOA,IAAI,IAAID,IAAO,MAAMA,GAAO,MAAM,MAAM;AAAA,IACtE,OAAO,EAAE,QAAQ,CAACA,GAAMC,MAAOA,IAAI,IAAID,IAAO,MAAMA,GAAO,MAAM,OAAO;AAAA,IACxE,SAAS,EAAE,QAAQ,CAACA,GAAMC,MAAOA,IAAI,IAAID,IAAO,MAAMA,GAAO,MAAM,SAAS;AAAA,IAC5E,SAAS,EAAE,QAAQ,CAACA,GAAMC,MAAOA,IAAI,IAAID,IAAO,MAAMA,GAAO,MAAM,SAAS;AAAA,EAChF;AAAA,EACA,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,OAAO,MAAM;AAAA,EAAC;AAAA,EACd,SAAS;AAAA,EACT,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,QAAQ,MAAM;AAAA,EAAC;AAAA,EACf,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,UAAU,MAAM;AAAA,EAAA;AACpB,GAEME,IAAa,CAACC,MACTA,aAAmB;AAoB9B,SAASC,EAAeC,GAAgBtB,GAAqC;AAEzE,SAAO,GADOA,EAAO,UAAU,OAAOsB,EAAK,KAAK,EAAE,SAAS,GAAG,GAAG,IAAIA,EAAK,KAC3D,IAAItB,EAAO,MAAMsB,EAAK,IAAI,EAAE,OAAOtB,EAAO,MAAMsB,EAAK,IAAI,EAAE,MAAMA,EAAK,KAAK,CAAC;AAC/F;AAcA,SAASC,EAAcD,GAAgBE,GAA2BxB,GAAsC;AAChG,SAACA,EAAO,kBACLsB,EAAK,UAAU,KAAKE,EAAc,KAAK,CAACC,MAAMA,EAAE,UAAU,CAAC,IAD9B;AAExC;AAaA,SAASC,EAAcC,GAAuB3B,GAA6BoB,GAA4B;AAC7F,QAAAQ,IAAaD,EACd,OAAO,CAACL,GAAMO,MAAUN,EAAcD,GAAMK,EAAU,MAAM,GAAGE,CAAK,GAAG7B,CAAM,CAAC,EAC9E,IAAI,CAACsB,MAASD,EAAeC,GAA4DtB,CAAM,CAAC,EAChG,KAAKA,EAAO,eAAe;AAEhC,EAAAoB,EAAQ,YAAYQ;AACxB;AAqBA,SAASE,EAAcH,GAAuB3B,GAA6B+B,GAAsB;AACnF,EAAAJ,EAAA,QAAQ,CAACL,GAAMO,MAAU;AAG/B,IAFmBP,EAAK,SAAS,aAAaC,EAAcD,GAAMK,EAAU,MAAM,GAAGE,CAAK,GAAG7B,CAAM,KAG/FK;AAAA,MACI0B,EAAUT,EAAK,IAAI;AAAA,MACnBtB,EAAO,UAAU,OAAOsB,EAAK,KAAK,EAAE,SAAS,GAAG,GAAG,IAAIA,EAAK;AAAA,MAC5DtB,EAAO,MAAMsB,EAAK,IAAI,EAAE,OAAOtB,EAAO,MAAMsB,EAAK,IAAI,EAAE,MAAMA,EAAK,KAAK;AAAA,IAC3E,GACAS,EAAUT,EAAK,IAAI,EAAE,MAAM,UAAU,MAErCS,EAAUT,EAAK,IAAI,EAAE,MAAM,UAAU;AAAA,EACzC,CACH;AACL;AAcA,MAAMU,IAA0B,CAACC,GAA4BC,MAAyD;AAClH,MAAIC,IAAwB;AAAA,IACxB,UAAU;AAAA,IACV,UAAU;AAAA,IACV,gCAAgB,KAAK;AAAA,EACzB;AAEM,QAAAC,IAAgB,CAACpC,MACZA,EAAO,YACR,IAAI,KAAK,KAAK,IAAIA,EAAO,MAAMA,EAAO,QAAQ,GAAGA,EAAO,KAAKA,EAAO,OAAOA,EAAO,SAASA,EAAO,OAAO,CAAC,IAC1G,IAAI,KAAKA,EAAO,MAAMA,EAAO,QAAQ,GAAGA,EAAO,KAAKA,EAAO,OAAOA,EAAO,SAASA,EAAO,OAAO;AAGpG,EAAAmC,EAAA,aAAaC,EAAcF,CAAU;AAG3C,MAAIG,IAAoC;AACxC,EAAIH,EAAW,WACKG,IAAA,SAAS,cAAc,MAAM,GAC7CA,EAAc,YAAYH,EAAW,aACrCD,EAAc,YAAYI,CAAa;AAG3C,QAAMN,IAAYG,EAAW,SACvB,OACA1B,EAAgByB,GAAe;AAAA,IAC3B,cAAcC,EAAW;AAAA,IACzB,aAAaA,EAAW;AAAA,IACxB,WAAWA,EAAW;AAAA,EAAA,CACzB,GAEDI,IAAU,MAAM;AAEZ,UAAAC,IAAcL,EAAW,YACzB,IAAI;AAAA,MACA,KAAK;AAAA,SACD,oBAAI,KAAK,GAAE,eAAe;AAAA,SAC1B,oBAAI,KAAK,GAAE,YAAY;AAAA,SACvB,oBAAI,KAAK,GAAE,WAAW;AAAA,SACtB,oBAAI,KAAK,GAAE,YAAY;AAAA,SACvB,oBAAI,KAAK,GAAE,cAAc;AAAA,SACzB,oBAAI,KAAK,GAAE,cAAc;AAAA,MAAA;AAAA,IAEjC,wBACI,KAAK;AAEf,QAAIM,IAAON,EAAW,UAAUK,EAAY,QAAY,IAAAJ,EAAM,WAAW,QAAA,IAAYA,EAAM,WAAW,QAAQ,IAAII,EAAY,QAAQ;AAEtI,IAAIC,KAAQ,KAAK,CAACN,EAAW,YAClBM,IAAA,GAEHL,EAAM,aAAa,QACnB,cAAcA,EAAM,QAAQ,GAG5BD,EAAW,SACXA,EAAW,MAAM;AAIzB,UAAMtB,IAAO,KAAK,MAAM4B,KAAQ,MAAO,KAAK,KAAK,GAAG;AAC5C,IAAAA,KAAA5B,IAAO,MAAO,KAAK,KAAK;AAEhC,UAAMC,IAAQ,KAAK,MAAM2B,KAAQ,MAAO,KAAK,GAAG;AACxC,IAAAA,KAAA3B,IAAQ,MAAO,KAAK;AAE5B,UAAMC,IAAU,KAAK,MAAM0B,KAAQ,MAAO,GAAG;AAC7C,IAAAA,KAAQ1B,IAAU,MAAO;AAEzB,UAAMC,IAAU,KAAK,MAAMyB,IAAO,GAAI;AAElC,IAAAN,EAAW,UAAUG,IAgBPX,EAfgB;AAAA,MAC1B,EAAE,OAAOd,GAAM,MAAM,OAA6C;AAAA,MAClE;AAAA,QACI,OAAOC;AAAA,QACP,MAAM;AAAA,MACV;AAAA,MACA;AAAA,QACI,OAAOC;AAAA,QACP,MAAM;AAAA,MACV;AAAA,MACA;AAAA,QACI,OAAOC;AAAA,QACP,MAAM;AAAA,MAAA;AAAA,IAEd,GACyBmB,GAAYG,CAAa,IAC3CN,KAgBOD,EAfgB;AAAA,MAC1B,EAAE,OAAOlB,GAAM,MAAM,OAA6C;AAAA,MAClE;AAAA,QACI,OAAOC;AAAA,QACP,MAAM;AAAA,MACV;AAAA,MACA;AAAA,QACI,OAAOC;AAAA,QACP,MAAM;AAAA,MACV;AAAA,MACA;AAAA,QACI,OAAOC;AAAA,QACP,MAAM;AAAA,MAAA;AAAA,IAEd,GACyBmB,GAAYH,CAAS;AAAA,EAEtD,GAEMU,IAAgB,MAAM;AACxB,IAAAN,EAAM,WAAW,YAAYG,GAASJ,EAAW,OAAO,GAChDI,EAAA;AAAA,EACZ,GAEMI,IAAgB,MAAM;AD5QzB,QAAAC;AC6QK,IAAAR,EAAM,aAAa,SACnB,cAAcA,EAAM,QAAQ,GAC5BA,EAAM,WAAW,OAErBA,EAAM,WAAW,KACjBQ,IAAAT,EAAW,WAAX,QAAAS,EAAA,KAAAT;AAAA,EACJ,GAEMU,IAAkB,MAAM;ADrR3B,QAAAD;ACsRC,IAAIR,EAAM,aACQM,EAAA,GACdN,EAAM,WAAW,KACjBQ,IAAAT,EAAW,aAAX,QAAAS,EAAA,KAAAT;AAAA,EAER,GAEMW,IAAkB,CAACC,MAA4C;AD7RlE,QAAAH;AC8RQ,WAAA,OAAOT,GAAYY,CAAS,IAE/BA,EAAU,SAAS,UACnBA,EAAU,UAAU,UACpBA,EAAU,QAAQ,UAClBA,EAAU,UAAU,UACpBA,EAAU,YAAY,UACtBA,EAAU,YAAY,YAEhBX,EAAA,aAAaC,EAAcF,CAAU,KAG/CS,IAAAT,EAAW,aAAX,QAAAS,EAAA,KAAAT,GAAsBY,IAEjBX,EAAM,aACHA,EAAM,YACN,cAAcA,EAAM,QAAQ,GAElBM,EAAA;AAAA,EAEtB,GAEMM,IAAW,OAAO,EAAE,GAAGZ;AAGf,EAAAM,EAAA;AAGd,QAAMO,IAAW,IAAI,iBAAiB,CAACC,MAAc;AACvC,IAAAA,EAAA,QAAQ,CAACC,MAAa;AACnB,MAAAA,EAAA,aAAa,QAAQ,CAACC,MAAS;AACpC,QAAIA,MAASlB,MACLE,EAAM,aAAa,QACnB,cAAcA,EAAM,QAAQ,GAEhCa,EAAS,WAAW;AAAA,MACxB,CACH;AAAA,IAAA,CACJ;AAAA,EAAA,CACJ;AAED,SAAIf,EAAc,cACde,EAAS,QAAQf,EAAc,YAAY,EAAE,WAAW,IAAM,GAI3D;AAAA,IACH,eAAAS;AAAA,IACA,iBAAAE;AAAA,IACA,iBAAAC;AAAA,IACA,UAAAE;AAAA,EACJ;AACJ,GAYMK,IAAwB,CAACC,MAAiE;AAC5F,QAAMC,IAAQD;AAER,SAAAC,EAAA,gBAAgB,MAAMD,EAAY,QAAQ,CAACE,MAAMA,EAAE,eAAe,GAClED,EAAA,kBAAkB,MAAMD,EAAY,QAAQ,CAACE,MAAMA,EAAE,iBAAiB,GACtED,EAAA,kBAAkB,CAACR,MAAcO,EAAY,QAAQ,CAACE,MAAMA,EAAE,gBAAgBT,CAAS,CAAC,GACxFQ,EAAA,WAAW,MAAMD,EAAY,IAAI,CAACE,MAAMA,EAAE,UAAU,GAEnDD;AACX,GAQME,IAAkB,CACpBpC,GACAqC,IAAqCzC,MACY;AACjD,QAAMkB,IAAkC,EAAE,GAAGlB,GAAe,GAAGyC,EAAK;AAEhE,MAAA,OAAOrC,KAAY,UAAU;AACvB,UAAAsC,IAAW,SAAS,iBAA8BtC,CAAO,GACzDiC,IAAc,MAAM,KAAKK,CAAQ,EAAE,IAAI,CAACC,MAAO3B,EAAwB2B,GAAIzB,CAAU,CAAC;AAC5F,WAAOmB,EAAY,WAAW,IAAIA,EAAY,CAAC,IAAID,EAAsBC,CAAW;AAAA,EAAA;AAGpF,MAAAlC,EAAWC,CAAO,GAAG;AACf,UAAAiC,IAAc,MAAM,KAAKjC,CAAO,EAAE,IAAI,CAACuC,MAAO3B,EAAwB2B,GAAIzB,CAAU,CAAC;AAC3F,WAAOmB,EAAY,WAAW,IAAIA,EAAY,CAAC,IAAID,EAAsBC,CAAW;AAAA,EAAA;AAGjF,SAAArB,EAAwBZ,GAASc,CAAU;AACtD;"} \ No newline at end of file diff --git a/dist/simplyCountdown.min.js b/dist/simplyCountdown.min.js deleted file mode 100644 index fc5d6e2..0000000 --- a/dist/simplyCountdown.min.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; -/*! -* Project : simply-countdown -* Date : 06/12/2024 -* License : MIT -* Version : 2.0.1 -* Author : Vincent Loy -* Contributors : -* - Justin Beasley -* - Nathan Smith -*/!function(e){let t=function(e){let n,o=e||{};for(let e=1;e{let o=document.createElement("div"),s=document.createElement("span"),l=document.createElement("span"),a=document.createElement("div");return a.appendChild(s),a.appendChild(l),o.appendChild(a),o.classList.add(t.sectionClass),o.classList.add(n),s.classList.add(t.amountClass),l.classList.add(t.wordClass),e.appendChild(o),{full:o,amount:s,word:l}};e.simplyCountdown=(e,o)=>{const s=Object.getPrototypeOf(e);let l,a,r,i,d,u,c,m,y,p=t({year:2015,month:6,day:28,hours:0,minutes:0,seconds:0,words:{days:{lambda:(e,t)=>t>1?e+"s":e,root:"day"},hours:{lambda:(e,t)=>t>1?e+"s":e,root:"hour"},minutes:{lambda:(e,t)=>t>1?e+"s":e,root:"minute"},seconds:{lambda:(e,t)=>t>1?e+"s":e,root:"second"}},plural:!0,inline:!1,inlineSeparator:", ",enableUtc:!1,onEnd:()=>{},refresh:1e3,inlineClass:"simply-countdown-inline",sectionClass:"simply-section",amountClass:"simply-amount",wordClass:"simply-word",zeroPad:!1,removeZeroUnits:!1,countUp:!1},o);y=s===String.prototype?document.querySelectorAll(e):e,a=p.enableUtc?new Date(Date.UTC(p.year,p.month-1,p.day,p.hours,p.minutes,p.seconds)):new Date(p.year,p.month-1,p.day,p.hours,p.minutes,p.seconds);let h=e=>{let t,o=e,s=((e,t)=>{let o;return e.inline?(o=document.createElement("span"),o.classList.add(e.inlineClass),o):{days:n(t,e,"simply-days-section"),hours:n(t,e,"simply-hours-section"),minutes:n(t,e,"simply-minutes-section"),seconds:n(t,e,"simply-seconds-section")}})(p,o);t=function(){let e,t,n,y,h=()=>{d=parseInt(i/86400,10),i%=86400,u=parseInt(i/3600,10),i%=3600,c=parseInt(i/60,10),m=parseInt(i%60,10)};p.enableUtc?(r=new Date,r=new Date(Date.UTC(r.getUTCFullYear(),r.getUTCMonth(),r.getUTCDate(),r.getUTCHours(),r.getUTCMinutes(),r.getUTCSeconds()))):r=new Date,i=Math.floor((a-r.getTime())/1e3),i>0?h():p.countUp?(i=(r.getTime()-a)/1e3,h()):(d=0,u=0,c=0,m=0,window.clearInterval(l),p.onEnd());let C=(e,t)=>e.hasOwnProperty("lambda")?e.lambda(e.root,t):e.root,w=p.words;if(e=C(w.days,d),t=C(w.hours,u),n=C(w.minutes,c),y=C(w.seconds,m),p.inline){let s="";p.removeZeroUnits&&0===d||(s+=`${d} ${e}${p.inlineSeparator}`),p.removeZeroUnits&&0===d&&0===u||(s+=`${u} ${t}${p.inlineSeparator}`),p.removeZeroUnits&&0===d&&0===u&&0===c||(s+=`${c} ${n}${p.inlineSeparator}`),s+=`${m} ${y}`,o.innerHTML=s.replace(/, $/,"")}else p.removeZeroUnits&&0===d?s.days.full.style.display="none":(s.days.amount.textContent=(p.zeroPad&&d.toString().length<2?"0":"")+d,s.days.word.textContent=e,s.days.full.style.display=""),p.removeZeroUnits&&0===d&&0===u?s.hours.full.style.display="none":(s.hours.amount.textContent=(p.zeroPad&&u.toString().length<2?"0":"")+u,s.hours.word.textContent=t,s.hours.full.style.display=""),p.removeZeroUnits&&0===d&&0===u&&0===c?s.minutes.full.style.display="none":(s.minutes.amount.textContent=(p.zeroPad&&c.toString().length<2?"0":"")+c,s.minutes.word.textContent=n,s.minutes.full.style.display=""),s.seconds.amount.textContent=(p.zeroPad&&m.toString().length<2?"0":"")+m,s.seconds.word.textContent=y,s.seconds.full.style.display=""},t(),l=window.setInterval(t,p.refresh)};var C;null!==(C=y)&&Symbol.iterator in Object(C)?Array.prototype.forEach.call(y,(e=>{h(e)})):h(y)}}(window),window.jQuery&&function(e,t){e.fn.simplyCountdown=function(e){return function(e,n){t(e,n)}(this.selector,e)}}(jQuery,simplyCountdown); \ No newline at end of file diff --git a/dist/simplyCountdown.umd.js b/dist/simplyCountdown.umd.js new file mode 100644 index 0000000..de33ed3 --- /dev/null +++ b/dist/simplyCountdown.umd.js @@ -0,0 +1,2 @@ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define("simplyCountdown",n):(e="undefined"!=typeof globalThis?globalThis:e||self).simplyCountdown=n()}(this,(function(){"use strict";const e=(e,n,t,o,s,a)=>{const l=document.createElement("div");l.className=`${e} ${a.sectionClass}`;const r=document.createElement("div"),d=document.createElement("span"),i=document.createElement("span");return d.className=`${n} ${a.amountClass}`,i.className=`${t} ${a.wordClass}`,d.textContent=String(o),i.textContent=s,r.appendChild(d),r.appendChild(i),l.appendChild(r),l},n={year:2024,month:12,day:25,hours:0,minutes:0,seconds:0,words:{days:{lambda:(e,n)=>n>1?e+"s":e,root:"day"},hours:{lambda:(e,n)=>n>1?e+"s":e,root:"hour"},minutes:{lambda:(e,n)=>n>1?e+"s":e,root:"minute"},seconds:{lambda:(e,n)=>n>1?e+"s":e,root:"second"}},plural:!0,inline:!1,inlineSeparator:", ",enableUtc:!1,onEnd:()=>{},refresh:1e3,inlineClass:"simply-countdown-inline",sectionClass:"simply-section",amountClass:"simply-amount",wordClass:"simply-word",zeroPad:!1,countUp:!1,removeZeroUnits:!1,onStop:()=>{},onResume:()=>{},onUpdate:()=>{}};function t(e,n,t){return!t.removeZeroUnits||(0!==e.value||n.some((e=>0!==e.value)))}function o(e,n,o){const s=e.filter(((o,s)=>t(o,e.slice(0,s),n))).map((e=>function(e,n){return`${n.zeroPad?String(e.value).padStart(2,"0"):e.value} ${n.words[e.word].lambda(n.words[e.word].root,e.value)}`}(e,n))).join(n.inlineSeparator);o.innerHTML=s}function s(e,n,o){e.forEach(((s,a)=>{"seconds"===s.word||t(s,e.slice(0,a),n)?(((e,n,t)=>{const o=e.querySelector(".simply-amount"),s=e.querySelector(".simply-word");o&&(o.textContent=String(n)),s&&(s.textContent=t)})(o[s.word],n.zeroPad?String(s.value).padStart(2,"0"):s.value,n.words[s.word].lambda(n.words[s.word].root,s.value)),o[s.word].style.display=""):o[s.word].style.display="none"}))}const a=(n,t)=>{let a={isPaused:!1,interval:null,targetDate:new Date};const l=e=>e.enableUtc?new Date(Date.UTC(e.year,e.month-1,e.day,e.hours,e.minutes,e.seconds)):new Date(e.year,e.month-1,e.day,e.hours,e.minutes,e.seconds);a.targetDate=l(t);let r=null;t.inline&&(r=document.createElement("span"),r.className=t.inlineClass,n.appendChild(r));const d=t.inline?null:((n,t)=>{const o="simply-amount",s="simply-word",a=e("simply-section simply-days-section",o,s,0,"day",t),l=e("simply-section simply-hours-section",o,s,0,"hour",t),r=e("simply-section simply-minutes-section",o,s,0,"minute",t),d=e("simply-section simply-seconds-section",o,s,0,"second",t);return n.appendChild(a),n.appendChild(l),n.appendChild(r),n.appendChild(d),{days:a,hours:l,minutes:r,seconds:d}})(n,{sectionClass:t.sectionClass,amountClass:t.amountClass,wordClass:t.wordClass}),i=()=>{const e=t.enableUtc?new Date(Date.UTC((new Date).getUTCFullYear(),(new Date).getUTCMonth(),(new Date).getUTCDate(),(new Date).getUTCHours(),(new Date).getUTCMinutes(),(new Date).getUTCSeconds())):new Date;let n=t.countUp?e.getTime()-a.targetDate.getTime():a.targetDate.getTime()-e.getTime();n<=0&&!t.countUp&&(n=0,null!==a.interval&&clearInterval(a.interval),t.onEnd&&t.onEnd());const l=Math.floor(n/864e5);n-=1e3*l*60*60*24;const i=Math.floor(n/36e5);n-=1e3*i*60*60;const u=Math.floor(n/6e4);n-=1e3*u*60;const c=Math.floor(n/1e3);if(t.inline&&r){o([{value:l,word:"days"},{value:i,word:"hours"},{value:u,word:"minutes"},{value:c,word:"seconds"}],t,r)}else if(d){s([{value:l,word:"days"},{value:i,word:"hours"},{value:u,word:"minutes"},{value:c,word:"seconds"}],t,d)}},u=()=>{a.interval=setInterval(i,t.refresh),i()};u();const c=new MutationObserver((e=>{e.forEach((e=>{e.removedNodes.forEach((e=>{e===n&&(null!==a.interval&&clearInterval(a.interval),c.disconnect())}))}))}));return n.parentNode&&c.observe(n.parentNode,{childList:!0}),{stopCountdown:()=>{var e;null!==a.interval&&(clearInterval(a.interval),a.interval=null),a.isPaused=!0,null==(e=t.onStop)||e.call(t)},resumeCountdown:()=>{var e;a.isPaused&&(u(),a.isPaused=!1,null==(e=t.onResume)||e.call(t))},updateCountdown:e=>{var n;Object.assign(t,e),void 0===e.year&&void 0===e.month&&void 0===e.day&&void 0===e.hours&&void 0===e.minutes&&void 0===e.seconds||(a.targetDate=l(t)),null==(n=t.onUpdate)||n.call(t,e),a.isPaused||(a.interval&&clearInterval(a.interval),u())},getState:()=>({...a})}},l=e=>{const n=e;return n.stopCountdown=()=>e.forEach((e=>e.stopCountdown())),n.resumeCountdown=()=>e.forEach((e=>e.resumeCountdown())),n.updateCountdown=n=>e.forEach((e=>e.updateCountdown(n))),n.getState=()=>e.map((e=>e.getState())),n},r=(e,t=n)=>{const o={...n,...t};if("string"==typeof e){const n=document.querySelectorAll(e),t=Array.from(n).map((e=>a(e,o)));return 1===t.length?t[0]:l(t)}if((e=>e instanceof NodeList)(e)){const n=Array.from(e).map((e=>a(e,o)));return 1===n.length?n[0]:l(n)}return a(e,o)};return"function"==typeof define&&define.amd?define((function(){return r})):"object"==typeof module&&module.exports?module.exports=r:window.simplyCountdown=r,r})); +//# sourceMappingURL=simplyCountdown.umd.js.map diff --git a/dist/simplyCountdown.umd.js.map b/dist/simplyCountdown.umd.js.map new file mode 100644 index 0000000..aedb97e --- /dev/null +++ b/dist/simplyCountdown.umd.js.map @@ -0,0 +1 @@ +{"version":3,"file":"simplyCountdown.umd.js","sources":["../src/core/dom.ts","../src/core/simplyCountdown.ts","../src/core/simplyCountdown.umd.ts"],"sourcesContent":["/**\n * Creates a countdown section element\n */\nexport const createCountdownSection = (\n sectionClass: string,\n amountClass: string,\n wordClass: string,\n amount: number,\n word: string,\n params: {\n sectionClass: string;\n amountClass: string;\n wordClass: string;\n }\n): HTMLElement => {\n const section = document.createElement(\"div\");\n section.className = `${sectionClass} ${params.sectionClass}`;\n\n const wrap = document.createElement(\"div\");\n const amount_elem = document.createElement(\"span\");\n const word_elem = document.createElement(\"span\");\n\n amount_elem.className = `${amountClass} ${params.amountClass}`;\n word_elem.className = `${wordClass} ${params.wordClass}`;\n\n amount_elem.textContent = String(amount);\n word_elem.textContent = word;\n\n wrap.appendChild(amount_elem);\n wrap.appendChild(word_elem);\n section.appendChild(wrap);\n\n return section;\n};\n\n/**\n * Retrieves a countdown section element from a container\n */\nexport const getCountdownSection = (sectionClass: string, container: HTMLElement): HTMLElement | null => {\n return container.querySelector(`.simply-section.${sectionClass}`);\n};\n\n/**\n * Updates a countdown section element\n */\nexport const updateCountdownSection = (section: HTMLElement, amount: number | string, word: string): void => {\n const amountElement = section.querySelector(\".simply-amount\");\n const wordElement = section.querySelector(\".simply-word\");\n\n if (amountElement) {\n amountElement.textContent = String(amount);\n }\n if (wordElement) {\n wordElement.textContent = word;\n }\n};\n\n/**\n * Creates all countdown elements\n */\nexport const createCountdown = (\n container: HTMLElement,\n params: {\n sectionClass: string;\n amountClass: string;\n wordClass: string;\n }\n): {\n days: HTMLElement;\n hours: HTMLElement;\n minutes: HTMLElement;\n seconds: HTMLElement;\n} => {\n const amountCls = \"simply-amount\";\n const wordCls = \"simply-word\";\n\n const days = createCountdownSection(\"simply-section simply-days-section\", amountCls, wordCls, 0, \"day\", params);\n const hours = createCountdownSection(\"simply-section simply-hours-section\", amountCls, wordCls, 0, \"hour\", params);\n const minutes = createCountdownSection(\"simply-section simply-minutes-section\", amountCls, wordCls, 0, \"minute\", params);\n const seconds = createCountdownSection(\"simply-section simply-seconds-section\", amountCls, wordCls, 0, \"second\", params);\n\n container.appendChild(days);\n container.appendChild(hours);\n container.appendChild(minutes);\n container.appendChild(seconds);\n\n return {\n days,\n hours,\n minutes,\n seconds,\n };\n};\n","/*!\n * Project : simplyCountdown.js\n * Date : 2024-12-27\n * License : MIT\n * Version : 3.0.0\n * Author : Vincent Loy-Serre\n * Contributors :\n * - Justin Beasley\n * - Nathan Smith\n * - Mehdi Rezaei\n * - mira01\n */\n\nimport type { CountdownParameters, CountdownSelector, CountdownState, CountdownController, CountdownControllerArray } from \"../types\";\nimport { createCountdown, updateCountdownSection } from \"./dom\";\n\nconst defaultParams: CountdownParameters = {\n year: 2024,\n month: 12,\n day: 25,\n hours: 0,\n minutes: 0,\n seconds: 0,\n words: {\n days: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"day\" },\n hours: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"hour\" },\n minutes: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"minute\" },\n seconds: { lambda: (root, n) => (n > 1 ? root + \"s\" : root), root: \"second\" },\n },\n plural: true,\n inline: false,\n inlineSeparator: \", \",\n enableUtc: false,\n onEnd: () => {},\n refresh: 1000,\n inlineClass: \"simply-countdown-inline\",\n sectionClass: \"simply-section\",\n amountClass: \"simply-amount\",\n wordClass: \"simply-word\",\n zeroPad: false,\n countUp: false,\n removeZeroUnits: false,\n onStop: () => {},\n onResume: () => {},\n onUpdate: () => {},\n};\n\nconst isNodeList = (element: CountdownSelector): element is NodeListOf => {\n return element instanceof NodeList;\n};\n\ninterface TimeUnit {\n value: number;\n word: keyof CountdownParameters[\"words\"];\n element?: HTMLElement;\n}\n\n/**\n * Formats a time unit with optional zero padding and pluralization\n * @param unit - The time unit object containing value and word properties\n * @param params - The countdown parameters containing formatting options and word definitions\n * @returns A formatted string containing the value and pluralized word for the time unit\n * @example\n * // With zeroPad: true\n * formatTimeUnit({value: 5, word: 'days'}, params) // returns \"05 days\"\n * // With zeroPad: false\n * formatTimeUnit({value: 5, word: 'days'}, params) // returns \"5 days\"\n */\nfunction formatTimeUnit(unit: TimeUnit, params: CountdownParameters): string {\n const value = params.zeroPad ? String(unit.value).padStart(2, \"0\") : unit.value;\n return `${value} ${params.words[unit.word].lambda(params.words[unit.word].root, unit.value)}`;\n}\n\n/**\n * Determines whether a time unit should be displayed based on its value and the values of previous units\n * @param unit - The current time unit to evaluate\n * @param previousUnits - Array of time units that come before the current unit\n * @param params - Configuration parameters for the countdown\n * @returns True if the unit should be displayed, false otherwise\n *\n * If removeZeroUnits is false in params, always returns true.\n * Otherwise, returns true if either:\n * - The current unit value is not zero\n * - Any previous unit has a non-zero value\n */\nfunction shouldDisplay(unit: TimeUnit, previousUnits: TimeUnit[], params: CountdownParameters): boolean {\n if (!params.removeZeroUnits) return true;\n return unit.value !== 0 || previousUnits.some((u) => u.value !== 0);\n}\n\n/**\n * Displays the countdown timer inline within the specified HTML element.\n *\n * @param timeUnits - Array of time units containing values and labels for display\n * @param params - Configuration parameters for the countdown display\n * @param element - The HTML element where the countdown will be rendered\n *\n * @remarks\n * The function filters and formats time units based on display rules, then joins them with\n * the specified separator from params.inlineSeparator before setting the element's innerHTML.\n */\nfunction displayInline(timeUnits: TimeUnit[], params: CountdownParameters, element: HTMLElement): void {\n const displayStr = timeUnits\n .filter((unit, index) => shouldDisplay(unit, timeUnits.slice(0, index), params))\n .map((unit) => formatTimeUnit(unit as { value: number; word: keyof typeof params.words }, params))\n .join(params.inlineSeparator);\n\n element.innerHTML = displayStr;\n}\n\n/**\n * Updates the display of time units in the countdown based on their values and display conditions\n * @param timeUnits - Array of TimeUnit objects containing the time values and their corresponding words\n * @param params - Configuration parameters for the countdown display\n * @param countdown - DOM elements representing the countdown display sections\n * @returns void\n *\n * @remarks\n * This function iterates through each time unit and determines whether it should be shown based on:\n * - If it's the seconds unit (always shown)\n * - If it meets display criteria based on previous units\n *\n * For units that should be shown, it:\n * - Updates the display value (with optional zero padding)\n * - Updates the word label using the configured lambda function\n * - Shows the unit's DOM element\n *\n * For units that shouldn't be shown, it hides their DOM elements\n */\nfunction displayBlocks(timeUnits: TimeUnit[], params: CountdownParameters, countdown: any): void {\n timeUnits.forEach((unit, index) => {\n const shouldShow = unit.word === \"seconds\" || shouldDisplay(unit, timeUnits.slice(0, index), params);\n\n if (shouldShow) {\n updateCountdownSection(\n countdown[unit.word],\n params.zeroPad ? String(unit.value).padStart(2, \"0\") : unit.value,\n params.words[unit.word].lambda(params.words[unit.word].root, unit.value)\n );\n countdown[unit.word].style.display = \"\";\n } else {\n countdown[unit.word].style.display = \"none\";\n }\n });\n}\n\n/**\n * Creates a countdown instance that manages the countdown timer functionality.\n *\n * @param targetElement - The HTML element where the countdown will be rendered\n * @param parameters - Configuration parameters for the countdown\n *\n * @returns A controller object with methods to control the countdown:\n * - stopCountdown: Pauses the countdown and triggers onStop callback\n * - resumeCountdown: Resumes a paused countdown and triggers onResume callback\n * - updateCountdown: Updates countdown parameters and triggers onUpdate callback\n * - getState: Returns current state of the countdown\n */\nconst createCountdownInstance = (targetElement: HTMLElement, parameters: CountdownParameters): CountdownController => {\n let state: CountdownState = {\n isPaused: false,\n interval: null,\n targetDate: new Date(),\n };\n\n const getTargetDate = (params: CountdownParameters): Date => {\n return params.enableUtc\n ? new Date(Date.UTC(params.year, params.month - 1, params.day, params.hours, params.minutes, params.seconds))\n : new Date(params.year, params.month - 1, params.day, params.hours, params.minutes, params.seconds);\n };\n\n state.targetDate = getTargetDate(parameters);\n\n // Create span element for inline mode\n let inlineElement: HTMLElement | null = null;\n if (parameters.inline) {\n inlineElement = document.createElement(\"span\");\n inlineElement.className = parameters.inlineClass;\n targetElement.appendChild(inlineElement);\n }\n\n const countdown = parameters.inline\n ? null\n : createCountdown(targetElement, {\n sectionClass: parameters.sectionClass,\n amountClass: parameters.amountClass,\n wordClass: parameters.wordClass,\n });\n\n const refresh = () => {\n // Fix UTC current date handling\n const currentDate = parameters.enableUtc\n ? new Date(\n Date.UTC(\n new Date().getUTCFullYear(),\n new Date().getUTCMonth(),\n new Date().getUTCDate(),\n new Date().getUTCHours(),\n new Date().getUTCMinutes(),\n new Date().getUTCSeconds()\n )\n )\n : new Date();\n\n let diff = parameters.countUp ? currentDate.getTime() - state.targetDate.getTime() : state.targetDate.getTime() - currentDate.getTime();\n\n if (diff <= 0 && !parameters.countUp) {\n diff = 0;\n // Clear interval before calling onEnd to prevent multiple calls\n if (state.interval !== null) {\n clearInterval(state.interval);\n }\n\n if (parameters.onEnd) {\n parameters.onEnd();\n }\n }\n\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\n diff -= days * 1000 * 60 * 60 * 24;\n\n const hours = Math.floor(diff / (1000 * 60 * 60));\n diff -= hours * 1000 * 60 * 60;\n\n const minutes = Math.floor(diff / (1000 * 60));\n diff -= minutes * 1000 * 60;\n\n const seconds = Math.floor(diff / 1000);\n\n if (parameters.inline && inlineElement) {\n const timeUnits: TimeUnit[] = [\n { value: days, word: \"days\" as keyof CountdownParameters[\"words\"] },\n {\n value: hours,\n word: \"hours\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: minutes,\n word: \"minutes\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: seconds,\n word: \"seconds\" as keyof CountdownParameters[\"words\"],\n },\n ];\n displayInline(timeUnits, parameters, inlineElement);\n } else if (countdown) {\n const timeUnits: TimeUnit[] = [\n { value: days, word: \"days\" as keyof CountdownParameters[\"words\"] },\n {\n value: hours,\n word: \"hours\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: minutes,\n word: \"minutes\" as keyof CountdownParameters[\"words\"],\n },\n {\n value: seconds,\n word: \"seconds\" as keyof CountdownParameters[\"words\"],\n },\n ];\n displayBlocks(timeUnits, parameters, countdown);\n }\n };\n\n const startInterval = () => {\n state.interval = setInterval(refresh, parameters.refresh);\n refresh();\n };\n\n const stopCountdown = () => {\n if (state.interval !== null) {\n clearInterval(state.interval);\n state.interval = null;\n }\n state.isPaused = true;\n parameters.onStop?.();\n };\n\n const resumeCountdown = () => {\n if (state.isPaused) {\n startInterval();\n state.isPaused = false;\n parameters.onResume?.();\n }\n };\n\n const updateCountdown = (newParams: Partial) => {\n Object.assign(parameters, newParams);\n if (\n newParams.year !== undefined ||\n newParams.month !== undefined ||\n newParams.day !== undefined ||\n newParams.hours !== undefined ||\n newParams.minutes !== undefined ||\n newParams.seconds !== undefined\n ) {\n state.targetDate = getTargetDate(parameters);\n }\n\n parameters.onUpdate?.(newParams);\n\n if (!state.isPaused) {\n if (state.interval) {\n clearInterval(state.interval);\n }\n startInterval();\n }\n };\n\n const getState = () => ({ ...state });\n\n // Start the countdown\n startInterval();\n\n // Cleanup on element removal\n const observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.removedNodes.forEach((node) => {\n if (node === targetElement) {\n if (state.interval !== null) {\n clearInterval(state.interval);\n }\n observer.disconnect();\n }\n });\n });\n });\n\n if (targetElement.parentNode) {\n observer.observe(targetElement.parentNode, { childList: true });\n }\n\n // Return controller object\n return {\n stopCountdown,\n resumeCountdown,\n updateCountdown,\n getState,\n };\n};\n\n/**\n * Creates an enhanced array of countdown controllers with additional control methods.\n *\n * @param controllers - Array of individual countdown controllers to be combined\n * @returns An array of controllers enhanced with collective control methods:\n * - `stopCountdown()`: Stops all countdowns in the array\n * - `resumeCountdown()`: Resumes all countdowns in the array\n * - `updateCountdown(newParams)`: Updates all countdowns with new parameters\n * - `getState()`: Returns an array of states from all countdowns\n */\nconst createControllerArray = (controllers: CountdownController[]): CountdownControllerArray => {\n const array = controllers as CountdownControllerArray;\n\n array.stopCountdown = () => controllers.forEach((c) => c.stopCountdown());\n array.resumeCountdown = () => controllers.forEach((c) => c.resumeCountdown());\n array.updateCountdown = (newParams) => controllers.forEach((c) => c.updateCountdown(newParams));\n array.getState = () => controllers.map((c) => c.getState());\n\n return array;\n};\n\n/**\n * Creates a countdown timer on specified HTML elements\n * @param element - A CSS selector string, HTMLElement, or NodeList targeting the countdown container(s)\n * @param args - Optional configuration parameters for the countdown\n * @returns A CountdownController for single element or CountdownControllerArray for multiple elements\n */\nconst simplyCountdown = (\n element: CountdownSelector,\n args: Partial = defaultParams\n): CountdownController | CountdownControllerArray => {\n const parameters: CountdownParameters = { ...defaultParams, ...args };\n\n if (typeof element === \"string\") {\n const elements = document.querySelectorAll(element);\n const controllers = Array.from(elements).map((el) => createCountdownInstance(el, parameters));\n return controllers.length === 1 ? controllers[0] : createControllerArray(controllers);\n }\n\n if (isNodeList(element)) {\n const controllers = Array.from(element).map((el) => createCountdownInstance(el, parameters));\n return controllers.length === 1 ? controllers[0] : createControllerArray(controllers);\n }\n\n return createCountdownInstance(element, parameters);\n};\n\nexport default simplyCountdown;\n","import simplyCountdownCore from \"./simplyCountdown\";\n\ndeclare const define: {\n (factory: () => any): void;\n amd: boolean;\n};\n\nif (typeof define === \"function\" && define.amd) {\n // AMD\n define(function () {\n return simplyCountdownCore;\n });\n} else if (typeof module === \"object\" && module.exports) {\n // Node\n module.exports = simplyCountdownCore;\n} else {\n // Browser\n (window as any).simplyCountdown = simplyCountdownCore;\n}\n\n// Export for Vite/Rollup\nexport default simplyCountdownCore;\n"],"names":["createCountdownSection","sectionClass","amountClass","wordClass","amount","word","params","section","document","createElement","className","wrap","amount_elem","word_elem","textContent","String","appendChild","defaultParams","year","month","day","hours","minutes","seconds","words","days","lambda","root","n","plural","inline","inlineSeparator","enableUtc","onEnd","refresh","inlineClass","zeroPad","countUp","removeZeroUnits","onStop","onResume","onUpdate","shouldDisplay","unit","previousUnits","value","some","u","displayInline","timeUnits","element","displayStr","filter","index","slice","map","padStart","formatTimeUnit","join","innerHTML","displayBlocks","countdown","forEach","amountElement","querySelector","wordElement","updateCountdownSection","style","display","createCountdownInstance","targetElement","parameters","state","isPaused","interval","targetDate","Date","getTargetDate","UTC","inlineElement","container","amountCls","wordCls","createCountdown","currentDate","getUTCFullYear","getUTCMonth","getUTCDate","getUTCHours","getUTCMinutes","getUTCSeconds","diff","getTime","clearInterval","Math","floor","startInterval","setInterval","observer","MutationObserver","mutations","mutation","removedNodes","node","disconnect","parentNode","observe","childList","stopCountdown","_a","call","resumeCountdown","updateCountdown","newParams","Object","assign","getState","createControllerArray","controllers","array","c","simplyCountdown","args","elements","querySelectorAll","Array","from","el","length","NodeList","isNodeList","define","amd","simplyCountdownCore","module","exports","window"],"mappings":"kQAGO,MAAMA,EAAyB,CAClCC,EACAC,EACAC,EACAC,EACAC,EACAC,KAMM,MAAAC,EAAUC,SAASC,cAAc,OACvCF,EAAQG,UAAY,GAAGT,KAAgBK,EAAOL,eAExC,MAAAU,EAAOH,SAASC,cAAc,OAC9BG,EAAcJ,SAASC,cAAc,QACrCI,EAAYL,SAASC,cAAc,QAYlC,OAVPG,EAAYF,UAAY,GAAGR,KAAeI,EAAOJ,cACjDW,EAAUH,UAAY,GAAGP,KAAaG,EAAOH,YAEjCS,EAAAE,YAAcC,OAAOX,GACjCS,EAAUC,YAAcT,EAExBM,EAAKK,YAAYJ,GACjBD,EAAKK,YAAYH,GACjBN,EAAQS,YAAYL,GAEbJ,CAAA,EChBLU,EAAqC,CACvCC,KAAM,KACNC,MAAO,GACPC,IAAK,GACLC,MAAO,EACPC,QAAS,EACTC,QAAS,EACTC,MAAO,CACHC,KAAM,CAAEC,OAAQ,CAACC,EAAMC,IAAOA,EAAI,EAAID,EAAO,IAAMA,EAAOA,KAAM,OAChEN,MAAO,CAAEK,OAAQ,CAACC,EAAMC,IAAOA,EAAI,EAAID,EAAO,IAAMA,EAAOA,KAAM,QACjEL,QAAS,CAAEI,OAAQ,CAACC,EAAMC,IAAOA,EAAI,EAAID,EAAO,IAAMA,EAAOA,KAAM,UACnEJ,QAAS,CAAEG,OAAQ,CAACC,EAAMC,IAAOA,EAAI,EAAID,EAAO,IAAMA,EAAOA,KAAM,WAEvEE,QAAQ,EACRC,QAAQ,EACRC,gBAAiB,KACjBC,WAAW,EACXC,MAAO,OACPC,QAAS,IACTC,YAAa,0BACblC,aAAc,iBACdC,YAAa,gBACbC,UAAW,cACXiC,SAAS,EACTC,SAAS,EACTC,iBAAiB,EACjBC,OAAQ,OACRC,SAAU,OACVC,SAAU,QAyCL,SAAAC,EAAcC,EAAgBC,EAA2BtC,GAC1D,OAACA,EAAOgC,kBACU,IAAfK,EAAKE,OAAeD,EAAcE,MAAMC,GAAkB,IAAZA,EAAEF,QAC3D,CAaS,SAAAG,EAAcC,EAAuB3C,EAA6B4C,GACjE,MAAAC,EAAaF,EACdG,QAAO,CAACT,EAAMU,IAAUX,EAAcC,EAAMM,EAAUK,MAAM,EAAGD,GAAQ/C,KACvEiD,KAAKZ,GApCL,SAAeA,EAAgBrC,GAEpC,MAAO,GADOA,EAAO8B,QAAUrB,OAAO4B,EAAKE,OAAOW,SAAS,EAAG,KAAOb,EAAKE,SACvDvC,EAAOkB,MAAMmB,EAAKtC,MAAMqB,OAAOpB,EAAOkB,MAAMmB,EAAKtC,MAAMsB,KAAMgB,EAAKE,QACzF,CAiCuBY,CAAed,EAA4DrC,KACzFoD,KAAKpD,EAAOyB,iBAEjBmB,EAAQS,UAAYR,CACxB,CAqBS,SAAAS,EAAcX,EAAuB3C,EAA6BuD,GAC7DZ,EAAAa,SAAQ,CAACnB,EAAMU,KACY,YAAdV,EAAKtC,MAAsBqC,EAAcC,EAAMM,EAAUK,MAAM,EAAGD,GAAQ/C,IDtF/D,EAACC,EAAsBH,EAAyBC,KAC5E,MAAA0D,EAAgBxD,EAAQyD,cAAc,kBACtCC,EAAc1D,EAAQyD,cAAc,gBAEtCD,IACcA,EAAAjD,YAAcC,OAAOX,IAEnC6D,IACAA,EAAYnD,YAAcT,EAAA,ECiFtB6D,CACIL,EAAUlB,EAAKtC,MACfC,EAAO8B,QAAUrB,OAAO4B,EAAKE,OAAOW,SAAS,EAAG,KAAOb,EAAKE,MAC5DvC,EAAOkB,MAAMmB,EAAKtC,MAAMqB,OAAOpB,EAAOkB,MAAMmB,EAAKtC,MAAMsB,KAAMgB,EAAKE,QAEtEgB,EAAUlB,EAAKtC,MAAM8D,MAAMC,QAAU,IAErCP,EAAUlB,EAAKtC,MAAM8D,MAAMC,QAAU,MAAA,GAGjD,CAcM,MAAAC,EAA0B,CAACC,EAA4BC,KACzD,IAAIC,EAAwB,CACxBC,UAAU,EACVC,SAAU,KACVC,eAAgBC,MAGd,MAAAC,EAAiBvE,GACZA,EAAO0B,UACR,IAAI4C,KAAKA,KAAKE,IAAIxE,EAAOY,KAAMZ,EAAOa,MAAQ,EAAGb,EAAOc,IAAKd,EAAOe,MAAOf,EAAOgB,QAAShB,EAAOiB,UAClG,IAAIqD,KAAKtE,EAAOY,KAAMZ,EAAOa,MAAQ,EAAGb,EAAOc,IAAKd,EAAOe,MAAOf,EAAOgB,QAAShB,EAAOiB,SAG7FiD,EAAAG,WAAaE,EAAcN,GAGjC,IAAIQ,EAAoC,KACpCR,EAAWzC,SACKiD,EAAAvE,SAASC,cAAc,QACvCsE,EAAcrE,UAAY6D,EAAWpC,YACrCmC,EAActD,YAAY+D,IAG9B,MAAMlB,EAAYU,EAAWzC,OACvB,KD1HqB,EAC3BkD,EACA1E,KAWA,MAAM2E,EAAY,gBACZC,EAAU,cAEVzD,EAAOzB,EAAuB,qCAAsCiF,EAAWC,EAAS,EAAG,MAAO5E,GAClGe,EAAQrB,EAAuB,sCAAuCiF,EAAWC,EAAS,EAAG,OAAQ5E,GACrGgB,EAAUtB,EAAuB,wCAAyCiF,EAAWC,EAAS,EAAG,SAAU5E,GAC3GiB,EAAUvB,EAAuB,wCAAyCiF,EAAWC,EAAS,EAAG,SAAU5E,GAO1G,OALP0E,EAAUhE,YAAYS,GACtBuD,EAAUhE,YAAYK,GACtB2D,EAAUhE,YAAYM,GACtB0D,EAAUhE,YAAYO,GAEf,CACHE,OACAJ,QACAC,UACAC,UACJ,EC4FM4D,CAAgBb,EAAe,CAC3BrE,aAAcsE,EAAWtE,aACzBC,YAAaqE,EAAWrE,YACxBC,UAAWoE,EAAWpE,YAG1B+B,EAAU,KAEN,MAAAkD,EAAcb,EAAWvC,UACzB,IAAI4C,KACAA,KAAKE,KACD,IAAIF,MAAOS,kBACX,IAAIT,MAAOU,eACX,IAAIV,MAAOW,cACX,IAAIX,MAAOY,eACX,IAAIZ,MAAOa,iBACX,IAAIb,MAAOc,sBAGfd,KAEV,IAAIe,EAAOpB,EAAWlC,QAAU+C,EAAYQ,UAAYpB,EAAMG,WAAWiB,UAAYpB,EAAMG,WAAWiB,UAAYR,EAAYQ,UAE1HD,GAAQ,IAAMpB,EAAWlC,UAClBsD,EAAA,EAEgB,OAAnBnB,EAAME,UACNmB,cAAcrB,EAAME,UAGpBH,EAAWtC,OACXsC,EAAWtC,SAInB,MAAMR,EAAOqE,KAAKC,MAAMJ,SAChBA,GAAO,IAAPlE,EAAc,GAAK,GAAK,GAEhC,MAAMJ,EAAQyE,KAAKC,MAAMJ,EAAQ,MACzBA,GAAQ,IAARtE,EAAe,GAAK,GAE5B,MAAMC,EAAUwE,KAAKC,MAAMJ,EAAA,KAC3BA,GAAkB,IAAVrE,EAAiB,GAEzB,MAAMC,EAAUuE,KAAKC,MAAMJ,EAAO,KAE9B,GAAApB,EAAWzC,QAAUiD,EAAe,CAgBtB/B,EAfgB,CAC1B,CAAEH,MAAOpB,EAAMpB,KAAM,QACrB,CACIwC,MAAOxB,EACPhB,KAAM,SAEV,CACIwC,MAAOvB,EACPjB,KAAM,WAEV,CACIwC,MAAOtB,EACPlB,KAAM,YAGWkE,EAAYQ,WAC9BlB,EAAW,CAgBJD,EAfgB,CAC1B,CAAEf,MAAOpB,EAAMpB,KAAM,QACrB,CACIwC,MAAOxB,EACPhB,KAAM,SAEV,CACIwC,MAAOvB,EACPjB,KAAM,WAEV,CACIwC,MAAOtB,EACPlB,KAAM,YAGWkE,EAAYV,EAAS,GAIhDmC,EAAgB,KAClBxB,EAAME,SAAWuB,YAAY/D,EAASqC,EAAWrC,SACzCA,GAAA,EA8CE8D,IAGd,MAAME,EAAW,IAAIC,kBAAkBC,IACzBA,EAAAtC,SAASuC,IACNA,EAAAC,aAAaxC,SAASyC,IACvBA,IAASjC,IACc,OAAnBE,EAAME,UACNmB,cAAcrB,EAAME,UAExBwB,EAASM,aAAW,GAE3B,GACJ,IAQE,OALHlC,EAAcmC,YACdP,EAASQ,QAAQpC,EAAcmC,WAAY,CAAEE,WAAW,IAIrD,CACHC,cAjEkB,WACK,OAAnBpC,EAAME,WACNmB,cAAcrB,EAAME,UACpBF,EAAME,SAAW,MAErBF,EAAMC,UAAW,EACjB,OAAAoC,EAAAtC,EAAWhC,SAAXsE,EAAAC,KAAAvC,EAAA,EA4DAwC,gBAzDoB,WAChBvC,EAAMC,WACQuB,IACdxB,EAAMC,UAAW,EACjB,OAAAoC,EAAAtC,EAAW/B,WAAXqE,EAAAC,KAAAvC,GAAsB,EAsD1ByC,gBAlDqBC,UACdC,OAAAC,OAAO5C,EAAY0C,QAEH,IAAnBA,EAAU/F,WACU,IAApB+F,EAAU9F,YACQ,IAAlB8F,EAAU7F,UACU,IAApB6F,EAAU5F,YACY,IAAtB4F,EAAU3F,cACY,IAAtB2F,EAAU1F,UAEJiD,EAAAG,WAAaE,EAAcN,IAGrC,OAAAsC,EAAAtC,EAAW9B,WAAWoE,EAAAC,KAAAvC,EAAA0C,GAEjBzC,EAAMC,WACHD,EAAME,UACNmB,cAAcrB,EAAME,UAEVsB,IAAA,EAgClBoB,SA5Ba,KAAA,IAAY5C,IA6B7B,EAaE6C,EAAyBC,IAC3B,MAAMC,EAAQD,EAOP,OALDC,EAAAX,cAAgB,IAAMU,EAAYxD,SAAS0D,GAAMA,EAAEZ,kBACnDW,EAAAR,gBAAkB,IAAMO,EAAYxD,SAAS0D,GAAMA,EAAET,oBACrDQ,EAAAP,gBAAmBC,GAAcK,EAAYxD,SAAS0D,GAAMA,EAAER,gBAAgBC,KAC9EM,EAAAH,SAAW,IAAME,EAAY/D,KAAKiE,GAAMA,EAAEJ,aAEzCG,CAAA,EASLE,EAAkB,CACpBvE,EACAwE,EAAqCzG,KAErC,MAAMsD,EAAkC,IAAKtD,KAAkByG,GAE3D,GAAmB,iBAAZxE,EAAsB,CACvB,MAAAyE,EAAWnH,SAASoH,iBAA8B1E,GAClDoE,EAAcO,MAAMC,KAAKH,GAAUpE,KAAKwE,GAAO1D,EAAwB0D,EAAIxD,KACjF,OAA8B,IAAvB+C,EAAYU,OAAeV,EAAY,GAAKD,EAAsBC,EAAW,CAGpF,GA/UW,CAACpE,GACTA,aAAmB+E,SA8UtBC,CAAWhF,GAAU,CACf,MAAAoE,EAAcO,MAAMC,KAAK5E,GAASK,KAAKwE,GAAO1D,EAAwB0D,EAAIxD,KAChF,OAA8B,IAAvB+C,EAAYU,OAAeV,EAAY,GAAKD,EAAsBC,EAAW,CAGjF,OAAAjD,EAAwBnB,EAASqB,EAAU,QC5XhC,mBAAX4D,QAAyBA,OAAOC,IAEvCD,QAAO,WACIE,OAAAA,CAAA,IAEc,iBAAXC,QAAuBA,OAAOC,QAE5CD,OAAOC,QAAUF,EAGhBG,OAAef,gBAAkBY"} \ No newline at end of file diff --git a/dist/themes/circle.css b/dist/themes/circle.css new file mode 100644 index 0000000..c43dcfc --- /dev/null +++ b/dist/themes/circle.css @@ -0,0 +1,73 @@ +.simply-countdown-circle { + --sc-circle-primary: #6366f1; + --sc-circle-secondary: #818cf8; + --sc-circle-bg: #1e1b4b; + --sc-circle-text: #fff; + + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.5rem; + font-family: "Inter", sans-serif; +} + +.simply-countdown-circle > .simply-section { + position: relative; + width: 100px; + height: 100px; + padding: 1rem; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + border-radius: 50%; + background: linear-gradient(45deg, var(--sc-circle-primary), var(--sc-circle-secondary)); + box-shadow: 0 0 25px -5px var(--sc-circle-primary); + animation: pulse-circle 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.simply-countdown-circle > .simply-section::before { + content: ""; + position: absolute; + inset: 6px; + border-radius: 50%; + background: var(--sc-circle-bg); + z-index: 0; +} + +.simply-countdown-circle > .simply-section > div { + position: relative; + z-index: 1; + color: var(--sc-circle-text); + text-align: center; +} + +.simply-countdown-circle .simply-amount { + display: block; + font-size: 1.75rem; + font-weight: 700; + line-height: 1; + background: linear-gradient(to right, var(--sc-circle-primary), var(--sc-circle-secondary)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.simply-countdown-circle .simply-word { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + opacity: 0.8; +} + +@keyframes pulse-circle { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(0.98); + opacity: 0.9; + } +} diff --git a/dist/themes/circle.min.css b/dist/themes/circle.min.css new file mode 100644 index 0000000..f8caa70 --- /dev/null +++ b/dist/themes/circle.min.css @@ -0,0 +1 @@ +.simply-countdown-circle{--sc-circle-primary:#6366f1;--sc-circle-secondary:#818cf8;--sc-circle-bg:#1e1b4b;--sc-circle-text:#fff;display:flex;flex-wrap:wrap;justify-content:center;gap:1.5rem;font-family:Inter,sans-serif}.simply-countdown-circle>.simply-section{position:relative;width:100px;height:100px;padding:1rem;display:flex;align-items:center;justify-content:center;flex-direction:column;border-radius:50%;background:linear-gradient(45deg,var(--sc-circle-primary),var(--sc-circle-secondary));box-shadow:0 0 25px -5px var(--sc-circle-primary);animation:2s cubic-bezier(.4,0,.6,1) infinite pulse-circle}.simply-countdown-circle>.simply-section::before{content:"";position:absolute;inset:6px;border-radius:50%;background:var(--sc-circle-bg);z-index:0}.simply-countdown-circle>.simply-section>div{position:relative;z-index:1;color:var(--sc-circle-text);text-align:center}.simply-countdown-circle .simply-amount{display:block;font-size:1.75rem;font-weight:700;line-height:1;background:linear-gradient(to right,var(--sc-circle-primary),var(--sc-circle-secondary));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.simply-countdown-circle .simply-word{font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;opacity:.8}@keyframes pulse-circle{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(.98);opacity:.9}} \ No newline at end of file diff --git a/dist/themes/cyber.css b/dist/themes/cyber.css new file mode 100644 index 0000000..9b733d0 --- /dev/null +++ b/dist/themes/cyber.css @@ -0,0 +1,155 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.cyberpunk +* Author : Vincent Loy +* Theme : Modern Cyberpunk +*/ + +.simply-countdown-cyber { + overflow: visible; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.75rem; + font-family: "Inter", system-ui, -apple-system, sans-serif; + perspective: 1000px; +} + +.simply-countdown-cyber > .simply-section { + width: 70px; + height: 70px; + padding: 1.5rem; + position: relative; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba(23, 25, 35, 0.9), rgba(15, 17, 25, 0.95)); + border-radius: 0.5rem; + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + backdrop-filter: blur(12px); + transform-style: preserve-3d; +} + +.simply-countdown-cyber > .simply-section::before { + content: ""; + position: absolute; + inset: -1px; + background: linear-gradient(135deg, rgba(120, 240, 255, 0.2), rgba(255, 90, 220, 0.2)); + border-radius: 0.5rem; + z-index: -1; + opacity: 0; + transition: opacity 0.3s ease; +} + +.simply-countdown-cyber > .simply-section::after { + content: ""; + position: absolute; + inset: -2px; + background: linear-gradient(135deg, #78f0ff, #ff5adc); + border-radius: 0.5rem; + z-index: -2; + opacity: 0.15; + filter: blur(4px); + animation: pulse 4s ease-in-out infinite; +} + +.simply-countdown-cyber > .simply-section .glass-overlay { + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); + border-radius: 0.5rem; +} + +.simply-countdown-cyber > .simply-section:hover { + transform: translateY(-4px) translateZ(10px) rotateX(5deg); + box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.5), 0 0 20px rgba(120, 240, 255, 0.2), 0 0 0 1px rgba(120, 240, 255, 0.1); +} + +.simply-countdown-cyber > .simply-section:hover::before { + opacity: 1; +} + +.simply-countdown-cyber > .simply-section > div { + display: flex; + flex-direction: column; + gap: 0.4rem; + align-items: center; + transform-style: preserve-3d; +} + +.simply-countdown-cyber > .simply-section .simply-amount { + font-size: 1.75rem; + font-weight: 700; + background: linear-gradient(to bottom right, #78f0ff, #ff5adc); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + text-shadow: 0 0 20px rgba(120, 240, 255, 0.3), 0 0 40px rgba(120, 240, 255, 0.2); + letter-spacing: -0.02em; + transform: translateZ(10px); +} + +.simply-countdown-cyber > .simply-section .simply-word { + font-size: 0.6rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.2em; + color: rgba(255, 255, 255, 0.7); + transform: translateZ(5px); + position: relative; +} + +.simply-countdown-cyber > .simply-section .simply-word::after { + content: ""; + position: absolute; + left: -10%; + bottom: -4px; + width: 120%; + height: 1px; + background: linear-gradient(to right, rgba(120, 240, 255, 0), rgba(120, 240, 255, 0.5), rgba(255, 90, 220, 0.5), rgba(255, 90, 220, 0)); +} + +@media (min-width: 640px) { + .simply-countdown-cyber > .simply-section { + width: 80px; + height: 80px; + padding: 1.75rem; + } + + .simply-countdown-cyber > .simply-section .simply-amount { + font-size: 2rem; + } + + .simply-countdown-cyber > .simply-section .simply-word { + font-size: 0.75rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown-cyber > .simply-section { + width: 100px; + height: 100px; + padding: 2rem; + } + + .simply-countdown-cyber > .simply-section .simply-amount { + font-size: 2.5rem; + } + + .simply-countdown-cyber > .simply-section .simply-word { + font-size: 0.8rem; + } +} + +/* Add subtle animation for extra futuristic feel */ +@keyframes pulse { + 0%, + 100% { + opacity: 0.15; + transform: scale(1); + } + 50% { + opacity: 0.25; + transform: scale(1.05); + } +} diff --git a/dist/themes/cyber.min.css b/dist/themes/cyber.min.css new file mode 100644 index 0000000..ed31a2f --- /dev/null +++ b/dist/themes/cyber.min.css @@ -0,0 +1 @@ +.simply-countdown-cyber{overflow:visible;display:flex;flex-wrap:wrap;justify-content:center;gap:1.75rem;font-family:Inter,system-ui,-apple-system,sans-serif;perspective:1000px}.simply-countdown-cyber>.simply-section{width:70px;height:70px;padding:1.5rem;position:relative;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(23,25,35,.9),rgba(15,17,25,.95));border-radius:.5rem;transition:.4s cubic-bezier(.175, .885, .32, 1.275);backdrop-filter:blur(12px);transform-style:preserve-3d}.simply-countdown-cyber>.simply-section::before{content:"";position:absolute;inset:-1px;background:linear-gradient(135deg,rgba(120,240,255,.2),rgba(255,90,220,.2));border-radius:.5rem;z-index:-1;opacity:0;transition:opacity .3s}.simply-countdown-cyber>.simply-section::after{content:"";position:absolute;inset:-2px;background:linear-gradient(135deg,#78f0ff,#ff5adc);border-radius:.5rem;z-index:-2;opacity:.15;filter:blur(4px);animation:4s ease-in-out infinite pulse}.simply-countdown-cyber>.simply-section .glass-overlay{position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.1),rgba(255,255,255,.05));border-radius:.5rem}.simply-countdown-cyber>.simply-section:hover{transform:translateY(-4px) translateZ(10px) rotateX(5deg);box-shadow:0 20px 40px -10px rgba(0,0,0,.5),0 0 20px rgba(120,240,255,.2),0 0 0 1px rgba(120,240,255,.1)}.simply-countdown-cyber>.simply-section:hover::before{opacity:1}.simply-countdown-cyber>.simply-section>div{display:flex;flex-direction:column;gap:.4rem;align-items:center;transform-style:preserve-3d}.simply-countdown-cyber>.simply-section .simply-amount{font-size:1.75rem;font-weight:700;background:linear-gradient(to bottom right,#78f0ff,#ff5adc);-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:0 0 20px rgba(120,240,255,.3),0 0 40px rgba(120,240,255,.2);letter-spacing:-.02em;transform:translateZ(10px)}.simply-countdown-cyber>.simply-section .simply-word{font-size:.6rem;font-weight:500;text-transform:uppercase;letter-spacing:.2em;color:rgba(255,255,255,.7);transform:translateZ(5px);position:relative}.simply-countdown-cyber>.simply-section .simply-word::after{content:"";position:absolute;left:-10%;bottom:-4px;width:120%;height:1px;background:linear-gradient(to right,rgba(120,240,255,0),rgba(120,240,255,.5),rgba(255,90,220,.5),rgba(255,90,220,0))}@media (min-width:640px){.simply-countdown-cyber>.simply-section{width:80px;height:80px;padding:1.75rem}.simply-countdown-cyber>.simply-section .simply-amount{font-size:2rem}.simply-countdown-cyber>.simply-section .simply-word{font-size:.75rem}}@media (min-width:1024px){.simply-countdown-cyber>.simply-section{width:100px;height:100px;padding:2rem}.simply-countdown-cyber>.simply-section .simply-amount{font-size:2.5rem}.simply-countdown-cyber>.simply-section .simply-word{font-size:.8rem}}@keyframes pulse{0%,100%{opacity:.15;transform:scale(1)}50%{opacity:.25;transform:scale(1.05)}} \ No newline at end of file diff --git a/dist/themes/dark.css b/dist/themes/dark.css new file mode 100644 index 0000000..386f246 --- /dev/null +++ b/dist/themes/dark.css @@ -0,0 +1,85 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.dark +* Author : Vincent Loy +* Theme : Dark Modern +*/ + +.simply-countdown-dark { + overflow: hidden; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.25rem; + font-family: "Inter", system-ui, -apple-system, sans-serif; +} + +.simply-countdown-dark > .simply-section { + width: 65px; + height: 65px; + padding: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + background: rgba(15, 23, 42, 0.75); + border: 1px solid rgba(51, 65, 85, 0.6); + border-radius: 1rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + backdrop-filter: blur(10px); +} + +.simply-countdown-dark > .simply-section > div { + display: flex; + flex-direction: column; + line-height: 1; + align-items: center; +} + +.simply-countdown-dark > .simply-section .simply-amount { + font-size: 1.5rem; + font-weight: 700; + color: #f1f5f9; + line-height: 1.2; + letter-spacing: -0.025em; +} + +.simply-countdown-dark > .simply-section .simply-word { + font-size: 0.6rem; + font-weight: 500; + color: #94a3b8; + text-transform: uppercase; + letter-spacing: 0.1em; +} + +@media (min-width: 640px) { + .simply-countdown-dark > .simply-section { + width: 75px; + height: 75px; + padding: 1.75rem; + } + + .simply-countdown-dark > .simply-section .simply-amount { + font-size: 1.75rem; + } + + .simply-countdown-dark > .simply-section .simply-word { + font-size: 0.75rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown-dark > .simply-section { + width: 90px; + height: 90px; + padding: 2rem; + } + + .simply-countdown-dark > .simply-section .simply-amount { + font-size: 2rem; + } + + .simply-countdown-dark > .simply-section .simply-word { + font-size: 0.8rem; + } +} diff --git a/dist/themes/dark.min.css b/dist/themes/dark.min.css new file mode 100644 index 0000000..1495b0c --- /dev/null +++ b/dist/themes/dark.min.css @@ -0,0 +1 @@ +.simply-countdown-dark{overflow:hidden;display:flex;flex-wrap:wrap;justify-content:center;gap:1.25rem;font-family:Inter,system-ui,-apple-system,sans-serif}.simply-countdown-dark>.simply-section{width:65px;height:65px;padding:1.5rem;display:flex;align-items:center;justify-content:center;background:rgba(15,23,42,.75);border:1px solid rgba(51,65,85,.6);border-radius:1rem;box-shadow:0 4px 6px -1px rgba(0,0,0,.2),0 2px 4px -1px rgba(0,0,0,.1),0 0 0 1px rgba(255,255,255,.05);transition:.3s cubic-bezier(.4, 0, .2, 1);backdrop-filter:blur(10px)}.simply-countdown-dark>.simply-section>div{display:flex;flex-direction:column;line-height:1;align-items:center}.simply-countdown-dark>.simply-section .simply-amount{font-size:1.5rem;font-weight:700;color:#f1f5f9;line-height:1.2;letter-spacing:-.025em}.simply-countdown-dark>.simply-section .simply-word{font-size:.6rem;font-weight:500;color:#94a3b8;text-transform:uppercase;letter-spacing:.1em}@media (min-width:640px){.simply-countdown-dark>.simply-section{width:75px;height:75px;padding:1.75rem}.simply-countdown-dark>.simply-section .simply-amount{font-size:1.75rem}.simply-countdown-dark>.simply-section .simply-word{font-size:.75rem}}@media (min-width:1024px){.simply-countdown-dark>.simply-section{width:90px;height:90px;padding:2rem}.simply-countdown-dark>.simply-section .simply-amount{font-size:2rem}.simply-countdown-dark>.simply-section .simply-word{font-size:.8rem}} \ No newline at end of file diff --git a/dist/themes/default.css b/dist/themes/default.css new file mode 100644 index 0000000..80e1514 --- /dev/null +++ b/dist/themes/default.css @@ -0,0 +1,85 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.default +* Author : Vincent Loy +* Theme : Light Modern +*/ + +.simply-countdown { + overflow: hidden; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.25rem; + font-family: "Inter", system-ui, -apple-system, sans-serif; +} + +.simply-countdown > .simply-section { + width: 65px; + height: 65px; + padding: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(226, 232, 240, 0.8); + border-radius: 1rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.02); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + backdrop-filter: blur(10px); +} + +.simply-countdown > .simply-section > div { + display: flex; + flex-direction: column; + line-height: 1; + align-items: center; +} + +.simply-countdown > .simply-section .simply-amount { + font-size: 1.5rem; + font-weight: 700; + color: #1e293b; + line-height: 1.2; + letter-spacing: -0.025em; +} + +.simply-countdown > .simply-section .simply-word { + font-size: 0.6rem; + font-weight: 500; + color: #64748b; + text-transform: uppercase; + letter-spacing: 0.1em; +} + +@media (min-width: 640px) { + .simply-countdown > .simply-section { + width: 75px; + height: 75px; + padding: 1.75rem; + } + + .simply-countdown > .simply-section .simply-amount { + font-size: 1.75rem; + } + + .simply-countdown > .simply-section .simply-word { + font-size: 0.75rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown > .simply-section { + width: 90px; + height: 90px; + padding: 2rem; + } + + .simply-countdown > .simply-section .simply-amount { + font-size: 2rem; + } + + .simply-countdown > .simply-section .simply-word { + font-size: 0.8rem; + } +} diff --git a/dist/themes/default.min.css b/dist/themes/default.min.css new file mode 100644 index 0000000..a1d7f18 --- /dev/null +++ b/dist/themes/default.min.css @@ -0,0 +1 @@ +.simply-countdown{overflow:hidden;display:flex;flex-wrap:wrap;justify-content:center;gap:1.25rem;font-family:Inter,system-ui,-apple-system,sans-serif}.simply-countdown>.simply-section{width:65px;height:65px;padding:1.5rem;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.9);border:1px solid rgba(226,232,240,.8);border-radius:1rem;box-shadow:0 4px 6px -1px rgba(0,0,0,.05),0 2px 4px -1px rgba(0,0,0,.03),0 0 0 1px rgba(0,0,0,.02);transition:.3s cubic-bezier(.4, 0, .2, 1);backdrop-filter:blur(10px)}.simply-countdown>.simply-section>div{display:flex;flex-direction:column;line-height:1;align-items:center}.simply-countdown>.simply-section .simply-amount{font-size:1.5rem;font-weight:700;color:#1e293b;line-height:1.2;letter-spacing:-.025em}.simply-countdown>.simply-section .simply-word{font-size:.6rem;font-weight:500;color:#64748b;text-transform:uppercase;letter-spacing:.1em}@media (min-width:640px){.simply-countdown>.simply-section{width:75px;height:75px;padding:1.75rem}.simply-countdown>.simply-section .simply-amount{font-size:1.75rem}.simply-countdown>.simply-section .simply-word{font-size:.75rem}}@media (min-width:1024px){.simply-countdown>.simply-section{width:90px;height:90px;padding:2rem}.simply-countdown>.simply-section .simply-amount{font-size:2rem}.simply-countdown>.simply-section .simply-word{font-size:.8rem}} \ No newline at end of file diff --git a/dist/themes/losange.css b/dist/themes/losange.css new file mode 100644 index 0000000..198873b --- /dev/null +++ b/dist/themes/losange.css @@ -0,0 +1,83 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.losange +* Author : Vincent Loy +*/ + +.simply-countdown-losange { + overflow: visible; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 3rem; + font-family: "Inter", sans-serif; +} + +.simply-countdown-losange > .simply-section { + width: 70px; + height: 70px; + display: flex; + justify-content: center; + align-items: center; + transform: rotate(-45deg); + background: linear-gradient(135deg, #4f46e5, #7c3aed); + border-radius: 0.5rem; + transition: all 0.2s ease-in-out; +} + +.simply-countdown-losange > .simply-section > div { + transform: rotate(45deg); + display: flex; + flex-direction: column; + line-height: 1.2; +} + +.simply-countdown-losange > .simply-section .simply-amount, +.simply-countdown-losange > .simply-section .simply-word { + display: block; + text-align: center; +} + +.simply-countdown-losange > .simply-section .simply-amount { + font-size: 1.25rem; + font-weight: 700; + color: #fff; +} + +.simply-countdown-losange > .simply-section .simply-word { + font-size: 0.65rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +@media (min-width: 640px) { + .simply-countdown-losange > .simply-section { + width: 80px; + height: 80px; + } + + .simply-countdown-losange > .simply-section .simply-amount { + font-size: 1.5rem; + } + + .simply-countdown-losange > .simply-section .simply-word { + font-size: 0.7rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown-losange > .simply-section { + width: 90px; + height: 90px; + } + + .simply-countdown-losange > .simply-section .simply-amount { + font-size: 1.75rem; + } + + .simply-countdown-losange > .simply-section .simply-word { + font-size: 0.75rem; + } +} diff --git a/dist/themes/losange.min.css b/dist/themes/losange.min.css new file mode 100644 index 0000000..4ddbfdd --- /dev/null +++ b/dist/themes/losange.min.css @@ -0,0 +1 @@ +.simply-countdown-losange{overflow:visible;display:flex;flex-wrap:wrap;justify-content:center;gap:3rem;font-family:Inter,sans-serif}.simply-countdown-losange>.simply-section{width:70px;height:70px;display:flex;justify-content:center;align-items:center;transform:rotate(-45deg);background:linear-gradient(135deg,#4f46e5,#7c3aed);border-radius:.5rem;transition:.2s ease-in-out}.simply-countdown-losange>.simply-section>div{transform:rotate(45deg);display:flex;flex-direction:column;line-height:1.2}.simply-countdown-losange>.simply-section .simply-amount,.simply-countdown-losange>.simply-section .simply-word{display:block;text-align:center}.simply-countdown-losange>.simply-section .simply-amount{font-size:1.25rem;font-weight:700;color:#fff}.simply-countdown-losange>.simply-section .simply-word{font-size:.65rem;font-weight:500;color:rgba(255,255,255,.9);text-transform:uppercase;letter-spacing:.05em}@media (min-width:640px){.simply-countdown-losange>.simply-section{width:80px;height:80px}.simply-countdown-losange>.simply-section .simply-amount{font-size:1.5rem}.simply-countdown-losange>.simply-section .simply-word{font-size:.7rem}}@media (min-width:1024px){.simply-countdown-losange>.simply-section{width:90px;height:90px}.simply-countdown-losange>.simply-section .simply-amount{font-size:1.75rem}.simply-countdown-losange>.simply-section .simply-word{font-size:.75rem}} \ No newline at end of file diff --git a/docs/dist/assets/main.min.css b/docs/dist/assets/main.min.css new file mode 100644 index 0000000..59c2daf --- /dev/null +++ b/docs/dist/assets/main.min.css @@ -0,0 +1 @@ +*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Montserrat,Arial,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"β€œ""”""β€˜""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.icon-\[lucide--alarm-clock-plus\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='13' r='8'/%3E%3Cpath d='M5 3L2 6m20 0l-3-3M6.38 18.7L4 21m13.64-2.33L20 21m-8-11v6m-3-3h6'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--book-open\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 7v14m-9-3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4a4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3a3 3 0 0 0-3-3z'/%3E%3C/svg%3E")}.icon-\[lucide--braces\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2a2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1m8 0h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'/%3E%3C/svg%3E")}.icon-\[lucide--check\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M20 6L9 17l-5-5'/%3E%3C/svg%3E")}.icon-\[lucide--chevron-up\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m18 15l-6-6l-6 6'/%3E%3C/svg%3E")}.icon-\[lucide--circle-play\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m10 8l6 4l-6 4z'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--circle-stop\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Crect width='6' height='6' x='9' y='9' rx='1'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--clock\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 6v6l4 2'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--code\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m16 18l6-6l-6-6M8 6l-6 6l6 6'/%3E%3C/svg%3E")}.icon-\[lucide--copy\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--crown\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294zM5 21h14'/%3E%3C/svg%3E")}.icon-\[lucide--download\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4m4-5l5 5l5-5m-5 5V3'/%3E%3C/svg%3E")}.icon-\[lucide--git-branch\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M6 3v12'/%3E%3Ccircle cx='18' cy='6' r='3'/%3E%3Ccircle cx='6' cy='18' r='3'/%3E%3Cpath d='M18 9a9 9 0 0 1-9 9'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--github\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5c.08-1.25-.27-2.48-1-3.5c.28-1.15.28-2.35 0-3.5c0 0-1 0-3 1.5c-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5c-.39.49-.68 1.05-.85 1.65S8.93 17.38 9 18v4'/%3E%3Cpath d='M9 18c-4.51 2-5-2-7-2'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--heart\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2c-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z'/%3E%3C/svg%3E")}.icon-\[lucide--package\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73zm1 .27V12'/%3E%3Cpath d='m3.3 7l7.703 4.734a2 2 0 0 0 1.994 0L20.7 7M7.5 4.27l9 5.15'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--palette\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='13.5' cy='6.5' r='.5' fill='black'/%3E%3Ccircle cx='17.5' cy='10.5' r='.5' fill='black'/%3E%3Ccircle cx='8.5' cy='7.5' r='.5' fill='black'/%3E%3Ccircle cx='6.5' cy='12.5' r='.5' fill='black'/%3E%3Cpath d='M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688c0-.437-.18-.835-.437-1.125c-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--plug-zap\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6l-2.3 2.3a2.4 2.4 0 0 0 0 3.4ZM2 22l3-3m2.5-5.5L10 11m.5 5.5L13 14m5-11l-4 4h6l-4 4'/%3E%3C/svg%3E")}.icon-\[lucide--star\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.12 2.12 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.12 2.12 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.12 2.12 0 0 0 1.597-1.16z'/%3E%3C/svg%3E")}.icon-\[lucide--terminal\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m4 17l6-6l-6-6m8 14h8'/%3E%3C/svg%3E")}.icon-\[lucide--timer-reset\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M10 2h4m-2 12v-4m-8 3a8 8 0 0 1 8-7a8 8 0 1 1-5.3 14L4 17.6'/%3E%3Cpath d='M9 17H4v5'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[lucide--toggle-left\]{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:var(--svg);mask-image:var(--svg);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Crect width='20' height='12' x='2' y='6' rx='6' ry='6'/%3E%3Ccircle cx='8' cy='12' r='2'/%3E%3C/g%3E%3C/svg%3E")}.icon-\[vscode-icons--file-type-bun\]{display:inline-block;width:1em;height:1em;background-repeat:no-repeat;background-size:100% 100%;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32'%3E%3Cpath fill='%23fbf0df' d='M29 17c0 5.65-5.82 10.23-13 10.23S3 22.61 3 17c0-3.5 2.24-6.6 5.66-8.44S14.21 4.81 16 4.81s3.32 1.54 7.34 3.71C26.76 10.36 29 13.46 29 17'/%3E%3Cpath fill='none' stroke='%23000' d='M16 27.65c7.32 0 13.46-4.65 13.46-10.65c0-3.72-2.37-7-5.89-8.85c-1.39-.75-2.46-1.41-3.37-2l-1.13-.69A6.14 6.14 0 0 0 16 4.35a6.9 6.9 0 0 0-3.3 1.23c-.42.24-.86.51-1.32.8c-.87.54-1.83 1.13-3 1.73C4.91 10 2.54 13.24 2.54 17c0 6 6.14 10.65 13.46 10.65Z'/%3E%3Cellipse cx='21.65' cy='18.62' fill='%23febbd0' rx='2.17' ry='1.28'/%3E%3Cellipse cx='10.41' cy='18.62' fill='%23febbd0' rx='2.17' ry='1.28'/%3E%3Cpath fill-rule='evenodd' d='M11.43 18.11a2 2 0 1 0-2-2.05a2.05 2.05 0 0 0 2 2.05m9.2 0a2 2 0 1 0-2-2.05a2 2 0 0 0 2 2.05'/%3E%3Cpath fill='%23fff' fill-rule='evenodd' d='M10.79 16.19a.77.77 0 1 0-.76-.77a.76.76 0 0 0 .76.77m9.2 0a.77.77 0 1 0 0-1.53a.77.77 0 0 0 0 1.53'/%3E%3Cpath fill='%23b71422' stroke='%23000' stroke-width='.75' d='M18.62 19.67a3.3 3.3 0 0 1-1.09 1.75a2.48 2.48 0 0 1-1.5.69a2.53 2.53 0 0 1-1.5-.69a3.28 3.28 0 0 1-1.08-1.75a.26.26 0 0 1 .29-.3h4.58a.27.27 0 0 1 .3.3Z'/%3E%3Cpath fill='%23ccbea7' fill-rule='evenodd' d='M14.93 5.75a6.1 6.1 0 0 1-2.09 4.62c-.1.09 0 .27.11.22c1.25-.49 2.94-1.94 2.23-4.88c-.03-.15-.25-.11-.25.04m.85 0a6 6 0 0 1 .57 5c0 .13.12.24.21.13c.83-1 1.54-3.11-.59-5.31c-.1-.11-.27.04-.19.17Zm1-.06a6.1 6.1 0 0 1 2.53 4.38c0 .14.21.17.24 0c.34-1.3.15-3.51-2.66-4.66c-.12-.02-.21.18-.09.27ZM9.94 9.55a6.27 6.27 0 0 0 3.89-3.33c.07-.13.28-.08.25.07c-.64 3-2.79 3.59-4.13 3.51c-.14-.01-.14-.21-.01-.25'/%3E%3C/svg%3E")}.icon-\[vscode-icons--file-type-npm\]{display:inline-block;width:1em;height:1em;background-repeat:no-repeat;background-size:100% 100%;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32'%3E%3Cpath fill='%23cb3837' d='M2 10.555h28v9.335H16v1.556H9.778v-1.557H2Zm1.556 7.779h3.111v-4.668h1.555v4.667h1.556v-6.222H3.556Zm7.778-6.223v7.779h3.111v-1.556h3.111v-6.223Zm3.111 1.556H16v3.112h-1.556Zm4.667-1.556v6.223h3.111v-4.668h1.556v4.667h1.556v-4.667h1.556v4.667h1.556v-6.222Z'/%3E%3C/svg%3E")}.icon-\[vscode-icons--file-type-pnpm\]{display:inline-block;width:1em;height:1em;background-repeat:no-repeat;background-size:100% 100%;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32'%3E%3Cpath fill='%23f9ad00' d='M30 10.75h-8.749V2H30Zm-9.626 0h-8.75V2h8.75Zm-9.625 0H2V2h8.749ZM30 20.375h-8.749v-8.75H30Z'/%3E%3Cpath fill='%23fff' d='M20.374 20.375h-8.75v-8.75h8.75Zm0 9.625h-8.75v-8.75h8.75ZM30 30h-8.749v-8.75H30Zm-19.251 0H2v-8.75h8.749Z'/%3E%3C/svg%3E")}.icon-\[vscode-icons--file-type-typescript\]{display:inline-block;width:1em;height:1em;background-repeat:no-repeat;background-size:100% 100%;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32'%3E%3Cpath fill='%23007acc' d='M23.827 8.243a4.4 4.4 0 0 1 2.223 1.281a6 6 0 0 1 .852 1.143c.011.045-1.534 1.083-2.471 1.662c-.034.023-.169-.124-.322-.35a2.01 2.01 0 0 0-1.67-1c-1.077-.074-1.771.49-1.766 1.433a1.3 1.3 0 0 0 .153.666c.237.49.677.784 2.059 1.383c2.544 1.095 3.636 1.817 4.31 2.843a5.16 5.16 0 0 1 .416 4.333a4.76 4.76 0 0 1-3.932 2.815a11 11 0 0 1-2.708-.028a6.53 6.53 0 0 1-3.616-1.884a6.3 6.3 0 0 1-.926-1.371a3 3 0 0 1 .327-.208c.158-.09.756-.434 1.32-.761l1.024-.6l.214.312a4.8 4.8 0 0 0 1.35 1.292a3.3 3.3 0 0 0 3.458-.175a1.545 1.545 0 0 0 .2-1.974c-.276-.395-.84-.727-2.443-1.422a8.8 8.8 0 0 1-3.349-2.055a4.7 4.7 0 0 1-.976-1.777a7.1 7.1 0 0 1-.062-2.268a4.33 4.33 0 0 1 3.644-3.374a9 9 0 0 1 2.691.084m-8.343 1.483l.011 1.454h-4.63v13.148H7.6V11.183H2.97V9.755a14 14 0 0 1 .04-1.466c.017-.023 2.832-.034 6.245-.028l6.211.017Z'/%3E%3C/svg%3E")}.icon-\[vscode-icons--file-type-vite\]{display:inline-block;width:1em;height:1em;background-repeat:no-repeat;background-size:100% 100%;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32'%3E%3Cg fill='none'%3E%3Cpath fill='url(%23vscodeIconsFileTypeVite0)' d='m29.884 6.146l-13.142 23.5a.714.714 0 0 1-1.244.005L2.096 6.148a.714.714 0 0 1 .746-1.057l13.156 2.352a.7.7 0 0 0 .253 0l12.881-2.348a.714.714 0 0 1 .752 1.05z'/%3E%3Cpath fill='url(%23vscodeIconsFileTypeVite1)' d='M22.264 2.007L12.54 3.912a.36.36 0 0 0-.288.33l-.598 10.104a.357.357 0 0 0 .437.369l2.707-.625a.357.357 0 0 1 .43.42l-.804 3.939a.357.357 0 0 0 .454.413l1.672-.508a.357.357 0 0 1 .454.414l-1.279 6.187c-.08.387.435.598.65.267l.143-.222l7.925-15.815a.357.357 0 0 0-.387-.51l-2.787.537a.357.357 0 0 1-.41-.45l1.818-6.306a.357.357 0 0 0-.412-.45'/%3E%3Cdefs%3E%3ClinearGradient id='vscodeIconsFileTypeVite0' x1='6' x2='235' y1='33' y2='344' gradientTransform='translate(1.34 1.894)scale(.07142)' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%2341D1FF'/%3E%3Cstop offset='1' stop-color='%23BD34FE'/%3E%3C/linearGradient%3E%3ClinearGradient id='vscodeIconsFileTypeVite1' x1='194.651' x2='236.076' y1='8.818' y2='292.989' gradientTransform='translate(1.34 1.894)scale(.07142)' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFEA83'/%3E%3Cstop offset='.083' stop-color='%23FFDD35'/%3E%3Cstop offset='1' stop-color='%23FFA800'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/g%3E%3C/svg%3E")}.icon-\[vscode-icons--file-type-yarn\]{display:inline-block;width:1em;height:1em;background-repeat:no-repeat;background-size:100% 100%;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32'%3E%3Cpath fill='%232188b6' d='M28.208 24.409a10.5 10.5 0 0 0-3.959 1.822a23.7 23.7 0 0 1-5.835 2.642a1.63 1.63 0 0 1-.983.55a62 62 0 0 1-6.447.577c-1.163.009-1.876-.3-2.074-.776a1.573 1.573 0 0 1 .866-2.074a4 4 0 0 1-.514-.379c-.171-.171-.352-.514-.406-.388c-.225.55-.343 1.894-.947 2.5c-.83.839-2.4.559-3.328.072c-1.019-.541.072-1.813.072-1.813a.73.73 0 0 1-.992-.343a4.85 4.85 0 0 1-.667-2.949a5.37 5.37 0 0 1 1.749-2.895a9.3 9.3 0 0 1 .658-4.4a10.45 10.45 0 0 1 3.165-3.661S6.628 10.747 7.35 8.817c.469-1.262.658-1.253.812-1.308a3.6 3.6 0 0 0 1.452-.857a5.27 5.27 0 0 1 4.41-1.7S15.2 1.4 16.277 2.09a18.4 18.4 0 0 1 1.533 2.886s1.281-.748 1.425-.469a11.33 11.33 0 0 1 .523 6.132a14 14 0 0 1-2.6 5.411c-.135.225 1.551.938 2.615 3.887c.983 2.7.108 4.96.262 5.212c.027.045.036.063.036.063s1.127.09 3.391-1.308a8.5 8.5 0 0 1 4.277-1.604a1.081 1.081 0 0 1 .469 2.11Z'/%3E%3C/svg%3E")}.btn{display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;padding:.75rem 1.5rem;font-weight:600;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn:hover{--tw-translate-y: -3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.btn-light{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity, 1))}.btn-indigo{--tw-bg-opacity: 1;background-color:rgb(55 48 163 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.sc-doc-block{border-radius:.75rem;--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1));padding:1.5rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.bottom-8{bottom:2rem}.left-0{left:0}.right-0{right:0}.right-3{right:.75rem}.right-8{right:2rem}.top-0{top:0}.top-3{top:.75rem}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-0{margin-bottom:0}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-20{margin-bottom:5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:.5rem}.mt-10{margin-top:2.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-12{height:3rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.w-12{width:3rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-full{width:100%}.flex-shrink-0{flex-shrink:0}.translate-y-10{--tw-translate-y: 2.5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s infinite}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-indigo-400\/30{border-color:#818cf84d}.border-indigo-500\/20{border-color:#6366f133}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-indigo-500\/10{background-color:#6366f11a}.bg-indigo-500\/20{background-color:#6366f133}.bg-indigo-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity, 1))}.bg-indigo-800{--tw-bg-opacity: 1;background-color:rgb(55 48 163 / var(--tw-bg-opacity, 1))}.bg-indigo-950\/50{background-color:#1e1b4b80}.bg-slate-700\/50{background-color:#33415580}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-800\/50{background-color:#1e293b80}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-indigo-600{--tw-gradient-from: #4f46e5 var(--tw-gradient-from-position);--tw-gradient-to: rgb(79 70 229 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-violet-600{--tw-gradient-to: #7c3aed var(--tw-gradient-to-position)}.p-0{padding:0}.p-2{padding:.5rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-20{padding-bottom:5rem}.pl-6{padding-left:1.5rem}.pt-40{padding-top:10rem}.text-center{text-align:center}.font-sans{font-family:Montserrat,Arial,sans-serif}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-indigo-100{--tw-text-opacity: 1;color:rgb(224 231 255 / var(--tw-text-opacity, 1))}.text-indigo-200{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity, 1))}.text-indigo-300{--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity, 1))}.text-indigo-400{--tw-text-opacity: 1;color:rgb(129 140 248 / var(--tw-text-opacity, 1))}.text-indigo-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity, 1))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-indigo-950\/10{--tw-shadow-color: rgb(30 27 75 / .1);--tw-shadow: var(--tw-shadow-colored)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.package-manager-btn{display:flex;align-items:center;gap:.375rem;border-radius:.5rem;background-color:#33415580;padding:.375rem .75rem;font-size:.75rem;line-height:1rem;font-weight:500;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.package-manager-btn:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}@media (min-width: 1024px){.package-manager-btn{font-size:.875rem;line-height:1.25rem}}.package-manager-btn.active{background-color:#6366f133;--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity, 1))}.package-manager-btn.active:hover{background-color:#6366f14d;--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity, 1))}.package-manager-content{display:none}.package-manager-content.active{display:block}.relative:hover .copy-button{opacity:1}pre{overflow-x:auto}.sc-doc-block{position:relative}.sc-doc-block:hover .copy-button{opacity:1}.sc-doc-block-glow{position:fixed;width:600px;height:600px;background:radial-gradient(circle at center,rgba(99,102,241,.06),transparent 40%);border-radius:50%;pointer-events:none;z-index:0;opacity:0;transform:translate(-50%,-50%);will-change:left,top,opacity;transition:opacity .2s ease-out}.sc-doc-block:hover .sc-doc-block-glow{opacity:1}.hover\:-translate-y-\[3px\]:hover{--tw-translate-y: -3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-indigo-300:hover{--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity, 1))}.hover\:bg-indigo-500\/20:hover{background-color:#6366f133}.hover\:bg-indigo-500\/30:hover{background-color:#6366f14d}.hover\:bg-indigo-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-700:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.hover\:text-indigo-200:hover{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity, 1))}.hover\:text-indigo-300:hover{--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity, 1))}.hover\:text-slate-300:hover{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-indigo-200{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity, 1))}.group[data-copied] .group-data-\[copied\]\:block{display:block}.group[data-copied] .group-data-\[copied\]\:hidden{display:none}@media (min-width: 640px){.sm\:flex-row{flex-direction:row}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (min-width: 768px){.md\:flex{display:flex}.md\:w-auto{width:auto}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:justify-start{justify-content:flex-start}.md\:gap-6{gap:1.5rem}.md\:text-left{text-align:left}.md\:text-6xl{font-size:3.75rem;line-height:1}.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width: 1024px){.lg\:h-5{height:1.25rem}.lg\:w-5{width:1.25rem}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:text-2xl{font-size:1.5rem;line-height:2rem}.lg\:text-sm{font-size:.875rem;line-height:1.25rem}}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-keyword,.hljs-formula{color:#c678dd}.hljs-section,.hljs-name,.hljs-selector-tag,.hljs-deletion,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-string,.hljs-regexp,.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string{color:#98c379}.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-type,.hljs-selector-class,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-number{color:#d19a66}.hljs-symbol,.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-title.class_,.hljs-class .hljs-title{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}.simply-countdown{overflow:hidden;display:flex;flex-wrap:wrap;justify-content:center;gap:1.25rem;font-family:Inter,system-ui,-apple-system,sans-serif}.simply-countdown>.simply-section{width:65px;height:65px;padding:1.5rem;display:flex;align-items:center;justify-content:center;background:#ffffffe6;border:1px solid rgba(226,232,240,.8);border-radius:1rem;box-shadow:0 4px 6px -1px #0000000d,0 2px 4px -1px #00000008,0 0 0 1px #00000005;transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.simply-countdown>.simply-section>div{display:flex;flex-direction:column;line-height:1;align-items:center}.simply-countdown>.simply-section .simply-amount{font-size:1.5rem;font-weight:700;color:#1e293b;line-height:1.2;letter-spacing:-.025em}.simply-countdown>.simply-section .simply-word{font-size:.6rem;font-weight:500;color:#64748b;text-transform:uppercase;letter-spacing:.1em}@media (min-width: 640px){.simply-countdown>.simply-section{width:75px;height:75px;padding:1.75rem}.simply-countdown>.simply-section .simply-amount{font-size:1.75rem}.simply-countdown>.simply-section .simply-word{font-size:.75rem}}@media (min-width: 1024px){.simply-countdown>.simply-section{width:90px;height:90px;padding:2rem}.simply-countdown>.simply-section .simply-amount{font-size:2rem}.simply-countdown>.simply-section .simply-word{font-size:.8rem}}.simply-countdown-dark{overflow:hidden;display:flex;flex-wrap:wrap;justify-content:center;gap:1.25rem;font-family:Inter,system-ui,-apple-system,sans-serif}.simply-countdown-dark>.simply-section{width:65px;height:65px;padding:1.5rem;display:flex;align-items:center;justify-content:center;background:#0f172abf;border:1px solid rgba(51,65,85,.6);border-radius:1rem;box-shadow:0 4px 6px -1px #0003,0 2px 4px -1px #0000001a,0 0 0 1px #ffffff0d;transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.simply-countdown-dark>.simply-section>div{display:flex;flex-direction:column;line-height:1;align-items:center}.simply-countdown-dark>.simply-section .simply-amount{font-size:1.5rem;font-weight:700;color:#f1f5f9;line-height:1.2;letter-spacing:-.025em}.simply-countdown-dark>.simply-section .simply-word{font-size:.6rem;font-weight:500;color:#94a3b8;text-transform:uppercase;letter-spacing:.1em}@media (min-width: 640px){.simply-countdown-dark>.simply-section{width:75px;height:75px;padding:1.75rem}.simply-countdown-dark>.simply-section .simply-amount{font-size:1.75rem}.simply-countdown-dark>.simply-section .simply-word{font-size:.75rem}}@media (min-width: 1024px){.simply-countdown-dark>.simply-section{width:90px;height:90px;padding:2rem}.simply-countdown-dark>.simply-section .simply-amount{font-size:2rem}.simply-countdown-dark>.simply-section .simply-word{font-size:.8rem}}.simply-countdown-cyber{overflow:visible;display:flex;flex-wrap:wrap;justify-content:center;gap:1.75rem;font-family:Inter,system-ui,-apple-system,sans-serif;perspective:1000px}.simply-countdown-cyber>.simply-section{width:70px;height:70px;padding:1.5rem;position:relative;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#171923e6,#0f1119f2);border-radius:.5rem;transition:all .4s cubic-bezier(.175,.885,.32,1.275);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transform-style:preserve-3d}.simply-countdown-cyber>.simply-section:before{content:"";position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;background:linear-gradient(135deg,#78f0ff33,#ff5adc33);border-radius:.5rem;z-index:-1;opacity:0;transition:opacity .3s ease}.simply-countdown-cyber>.simply-section:after{content:"";position:absolute;top:-2px;right:-2px;bottom:-2px;left:-2px;background:linear-gradient(135deg,#78f0ff,#ff5adc);border-radius:.5rem;z-index:-2;opacity:.15;filter:blur(4px);animation:pulse 4s ease-in-out infinite}.simply-countdown-cyber>.simply-section .glass-overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(135deg,#ffffff1a,#ffffff0d);border-radius:.5rem}.simply-countdown-cyber>.simply-section:hover{transform:translateY(-4px) translateZ(10px) rotateX(5deg);box-shadow:0 20px 40px -10px #00000080,0 0 20px #78f0ff33,0 0 0 1px #78f0ff1a}.simply-countdown-cyber>.simply-section:hover:before{opacity:1}.simply-countdown-cyber>.simply-section>div{display:flex;flex-direction:column;gap:.4rem;align-items:center;transform-style:preserve-3d}.simply-countdown-cyber>.simply-section .simply-amount{font-size:1.75rem;font-weight:700;background:linear-gradient(to bottom right,#78f0ff,#ff5adc);-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:0 0 20px rgba(120,240,255,.3),0 0 40px rgba(120,240,255,.2);letter-spacing:-.02em;transform:translateZ(10px)}.simply-countdown-cyber>.simply-section .simply-word{font-size:.6rem;font-weight:500;text-transform:uppercase;letter-spacing:.2em;color:#ffffffb3;transform:translateZ(5px);position:relative}.simply-countdown-cyber>.simply-section .simply-word:after{content:"";position:absolute;left:-10%;bottom:-4px;width:120%;height:1px;background:linear-gradient(to right,#78f0ff00,#78f0ff80,#ff5adc80,#ff5adc00)}@media (min-width: 640px){.simply-countdown-cyber>.simply-section{width:80px;height:80px;padding:1.75rem}.simply-countdown-cyber>.simply-section .simply-amount{font-size:2rem}.simply-countdown-cyber>.simply-section .simply-word{font-size:.75rem}}@media (min-width: 1024px){.simply-countdown-cyber>.simply-section{width:100px;height:100px;padding:2rem}.simply-countdown-cyber>.simply-section .simply-amount{font-size:2.5rem}.simply-countdown-cyber>.simply-section .simply-word{font-size:.8rem}}@keyframes pulse{0%,to{opacity:.15;transform:scale(1)}50%{opacity:.25;transform:scale(1.05)}}.simply-countdown-losange{overflow:visible;display:flex;flex-wrap:wrap;justify-content:center;gap:3rem;font-family:Inter,sans-serif}.simply-countdown-losange>.simply-section{width:70px;height:70px;display:flex;justify-content:center;align-items:center;transform:rotate(-45deg);background:linear-gradient(135deg,#4f46e5,#7c3aed);border-radius:.5rem;transition:all .2s ease-in-out}.simply-countdown-losange>.simply-section>div{transform:rotate(45deg);display:flex;flex-direction:column;line-height:1.2}.simply-countdown-losange>.simply-section .simply-amount,.simply-countdown-losange>.simply-section .simply-word{display:block;text-align:center}.simply-countdown-losange>.simply-section .simply-amount{font-size:1.25rem;font-weight:700;color:#fff}.simply-countdown-losange>.simply-section .simply-word{font-size:.65rem;font-weight:500;color:#ffffffe6;text-transform:uppercase;letter-spacing:.05em}@media (min-width: 640px){.simply-countdown-losange>.simply-section{width:80px;height:80px}.simply-countdown-losange>.simply-section .simply-amount{font-size:1.5rem}.simply-countdown-losange>.simply-section .simply-word{font-size:.7rem}}@media (min-width: 1024px){.simply-countdown-losange>.simply-section{width:90px;height:90px}.simply-countdown-losange>.simply-section .simply-amount{font-size:1.75rem}.simply-countdown-losange>.simply-section .simply-word{font-size:.75rem}}.simply-countdown-circle{--sc-circle-primary: #6366f1;--sc-circle-secondary: #818cf8;--sc-circle-bg: #1e1b4b;--sc-circle-text: #fff;display:flex;flex-wrap:wrap;justify-content:center;gap:1.5rem;font-family:Inter,sans-serif}.simply-countdown-circle>.simply-section{position:relative;width:100px;height:100px;padding:1rem;display:flex;align-items:center;justify-content:center;flex-direction:column;border-radius:50%;background:linear-gradient(45deg,var(--sc-circle-primary),var(--sc-circle-secondary));box-shadow:0 0 25px -5px var(--sc-circle-primary);animation:pulse-circle 2s cubic-bezier(.4,0,.6,1) infinite}.simply-countdown-circle>.simply-section:before{content:"";position:absolute;top:6px;right:6px;bottom:6px;left:6px;border-radius:50%;background:var(--sc-circle-bg);z-index:0}.simply-countdown-circle>.simply-section>div{position:relative;z-index:1;color:var(--sc-circle-text);text-align:center}.simply-countdown-circle .simply-amount{display:block;font-size:1.75rem;font-weight:700;line-height:1;background:linear-gradient(to right,var(--sc-circle-primary),var(--sc-circle-secondary));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.simply-countdown-circle .simply-word{font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;opacity:.8}@keyframes pulse-circle{0%,to{transform:scale(1);opacity:1}50%{transform:scale(.98);opacity:.9}} diff --git a/docs/dist/assets/main.min.js b/docs/dist/assets/main.min.js new file mode 100644 index 0000000..be257cd --- /dev/null +++ b/docs/dist/assets/main.min.js @@ -0,0 +1,15 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))i(a);new MutationObserver(a=>{for(const l of a)if(l.type==="childList")for(const s of l.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&i(s)}).observe(document,{childList:!0,subtree:!0});function n(a){const l={};return a.integrity&&(l.integrity=a.integrity),a.referrerPolicy&&(l.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?l.credentials="include":a.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function i(a){if(a.ep)return;a.ep=!0;const l=n(a);fetch(a.href,l)}})();function vt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Xe(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{const n=e[t],i=typeof n;(i==="object"||i==="function")&&!Object.isFrozen(n)&&Xe(n)}),e}class ze{constructor(t){t.data===void 0&&(t.data={}),this.data=t.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function Ye(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function P(e,...t){const n=Object.create(null);for(const i in e)n[i]=e[i];return t.forEach(function(i){for(const a in i)n[a]=i[a]}),n}const St="",$e=e=>!!e.scope,Nt=(e,{prefix:t})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const n=e.split(".");return[`${t}${n.shift()}`,...n.map((i,a)=>`${i}${"_".repeat(a+1)}`)].join(" ")}return`${t}${e}`};class xt{constructor(t,n){this.buffer="",this.classPrefix=n.classPrefix,t.walk(this)}addText(t){this.buffer+=Ye(t)}openNode(t){if(!$e(t))return;const n=Nt(t.scope,{prefix:this.classPrefix});this.span(n)}closeNode(t){$e(t)&&(this.buffer+=St)}value(){return this.buffer}span(t){this.buffer+=``}}const He=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class ke{constructor(){this.rootNode=He(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(t){this.top.children.push(t)}openNode(t){const n=He({scope:t});this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(t){return this.constructor._walk(t,this.rootNode)}static _walk(t,n){return typeof n=="string"?t.addText(n):n.children&&(t.openNode(n),n.children.forEach(i=>this._walk(t,i)),t.closeNode(n)),t}static _collapse(t){typeof t!="string"&&t.children&&(t.children.every(n=>typeof n=="string")?t.children=[t.children.join("")]:t.children.forEach(n=>{ke._collapse(n)}))}}class Mt extends ke{constructor(t){super(),this.options=t}addText(t){t!==""&&this.add(t)}startScope(t){this.openNode(t)}endScope(){this.closeNode()}__addSublanguage(t,n){const i=t.root;n&&(i.scope=`language:${n}`),this.add(i)}toHTML(){return new xt(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function oe(e){return e?typeof e=="string"?e:e.source:null}function Je(e){return Z("(?=",e,")")}function Tt(e){return Z("(?:",e,")*")}function Ot(e){return Z("(?:",e,")?")}function Z(...e){return e.map(n=>oe(n)).join("")}function kt(e){const t=e[e.length-1];return typeof t=="object"&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function Ae(...e){return"("+(kt(e).capture?"":"?:")+e.map(i=>oe(i)).join("|")+")"}function Qe(e){return new RegExp(e.toString()+"|").exec("").length-1}function At(e,t){const n=e&&e.exec(t);return n&&n.index===0}const Rt=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function Re(e,{joinWith:t}){let n=0;return e.map(i=>{n+=1;const a=n;let l=oe(i),s="";for(;l.length>0;){const o=Rt.exec(l);if(!o){s+=l;break}s+=l.substring(0,o.index),l=l.substring(o.index+o[0].length),o[0][0]==="\\"&&o[1]?s+="\\"+String(Number(o[1])+a):(s+=o[0],o[0]==="("&&n++)}return s}).map(i=>`(${i})`).join(t)}const Ct=/\b\B/,Ve="[a-zA-Z]\\w*",Ce="[a-zA-Z_]\\w*",et="\\b\\d+(\\.\\d+)?",tt="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",nt="\\b(0b[01]+)",It="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",Lt=(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=Z(t,/.*\b/,e.binary,/\b.*/)),P({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(n,i)=>{n.index!==0&&i.ignoreMatch()}},e)},re={begin:"\\\\[\\s\\S]",relevance:0},Dt={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[re]},Bt={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[re]},Ut={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},me=function(e,t,n={}){const i=P({scope:"comment",begin:e,end:t,contains:[]},n);i.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const a=Ae("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return i.contains.push({begin:Z(/[ ]+/,"(",a,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i},Pt=me("//","$"),zt=me("/\\*","\\*/"),$t=me("#","$"),Ht={scope:"number",begin:et,relevance:0},Gt={scope:"number",begin:tt,relevance:0},Ft={scope:"number",begin:nt,relevance:0},jt={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[re,{begin:/\[/,end:/\]/,relevance:0,contains:[re]}]},Kt={scope:"title",begin:Ve,relevance:0},Zt={scope:"title",begin:Ce,relevance:0},Wt={begin:"\\.\\s*"+Ce,relevance:0},qt=function(e){return Object.assign(e,{"on:begin":(t,n)=>{n.data._beginMatch=t[1]},"on:end":(t,n)=>{n.data._beginMatch!==t[1]&&n.ignoreMatch()}})};var ue=Object.freeze({__proto__:null,APOS_STRING_MODE:Dt,BACKSLASH_ESCAPE:re,BINARY_NUMBER_MODE:Ft,BINARY_NUMBER_RE:nt,COMMENT:me,C_BLOCK_COMMENT_MODE:zt,C_LINE_COMMENT_MODE:Pt,C_NUMBER_MODE:Gt,C_NUMBER_RE:tt,END_SAME_AS_BEGIN:qt,HASH_COMMENT_MODE:$t,IDENT_RE:Ve,MATCH_NOTHING_RE:Ct,METHOD_GUARD:Wt,NUMBER_MODE:Ht,NUMBER_RE:et,PHRASAL_WORDS_MODE:Ut,QUOTE_STRING_MODE:Bt,REGEXP_MODE:jt,RE_STARTERS_RE:It,SHEBANG:Lt,TITLE_MODE:Kt,UNDERSCORE_IDENT_RE:Ce,UNDERSCORE_TITLE_MODE:Zt});function Xt(e,t){e.input[e.index-1]==="."&&t.ignoreMatch()}function Yt(e,t){e.className!==void 0&&(e.scope=e.className,delete e.className)}function Jt(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=Xt,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,e.relevance===void 0&&(e.relevance=0))}function Qt(e,t){Array.isArray(e.illegal)&&(e.illegal=Ae(...e.illegal))}function Vt(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function en(e,t){e.relevance===void 0&&(e.relevance=1)}const tn=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const n=Object.assign({},e);Object.keys(e).forEach(i=>{delete e[i]}),e.keywords=n.keywords,e.begin=Z(n.beforeMatch,Je(n.begin)),e.starts={relevance:0,contains:[Object.assign(n,{endsParent:!0})]},e.relevance=0,delete n.beforeMatch},nn=["of","and","for","in","not","or","if","then","parent","list","value"],on="keyword";function it(e,t,n=on){const i=Object.create(null);return typeof e=="string"?a(n,e.split(" ")):Array.isArray(e)?a(n,e):Object.keys(e).forEach(function(l){Object.assign(i,it(e[l],t,l))}),i;function a(l,s){t&&(s=s.map(o=>o.toLowerCase())),s.forEach(function(o){const d=o.split("|");i[d[0]]=[l,rn(d[0],d[1])]})}}function rn(e,t){return t?Number(t):sn(e)?0:1}function sn(e){return nn.includes(e.toLowerCase())}const Ge={},K=e=>{console.error(e)},Fe=(e,...t)=>{console.log(`WARN: ${e}`,...t)},X=(e,t)=>{Ge[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Ge[`${e}/${t}`]=!0)},be=new Error;function ot(e,t,{key:n}){let i=0;const a=e[n],l={},s={};for(let o=1;o<=t.length;o++)s[o+i]=a[o],l[o+i]=!0,i+=Qe(t[o-1]);e[n]=s,e[n]._emit=l,e[n]._multi=!0}function an(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),be;if(typeof e.beginScope!="object"||e.beginScope===null)throw K("beginScope must be object"),be;ot(e,e.begin,{key:"beginScope"}),e.begin=Re(e.begin,{joinWith:""})}}function cn(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"),be;if(typeof e.endScope!="object"||e.endScope===null)throw K("endScope must be object"),be;ot(e,e.end,{key:"endScope"}),e.end=Re(e.end,{joinWith:""})}}function ln(e){e.scope&&typeof e.scope=="object"&&e.scope!==null&&(e.beginScope=e.scope,delete e.scope)}function dn(e){ln(e),typeof e.beginScope=="string"&&(e.beginScope={_wrap:e.beginScope}),typeof e.endScope=="string"&&(e.endScope={_wrap:e.endScope}),an(e),cn(e)}function un(e){function t(s,o){return new RegExp(oe(s),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(o?"g":""))}class n{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(o,d){d.position=this.position++,this.matchIndexes[this.matchAt]=d,this.regexes.push([d,o]),this.matchAt+=Qe(o)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const o=this.regexes.map(d=>d[1]);this.matcherRe=t(Re(o,{joinWith:"|"}),!0),this.lastIndex=0}exec(o){this.matcherRe.lastIndex=this.lastIndex;const d=this.matcherRe.exec(o);if(!d)return null;const m=d.findIndex((C,T)=>T>0&&C!==void 0),_=this.matchIndexes[m];return d.splice(0,m),Object.assign(d,_)}}class i{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(o){if(this.multiRegexes[o])return this.multiRegexes[o];const d=new n;return this.rules.slice(o).forEach(([m,_])=>d.addRule(m,_)),d.compile(),this.multiRegexes[o]=d,d}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(o,d){this.rules.push([o,d]),d.type==="begin"&&this.count++}exec(o){const d=this.getMatcher(this.regexIndex);d.lastIndex=this.lastIndex;let m=d.exec(o);if(this.resumingScanAtSamePosition()&&!(m&&m.index===this.lastIndex)){const _=this.getMatcher(0);_.lastIndex=this.lastIndex+1,m=_.exec(o)}return m&&(this.regexIndex+=m.position+1,this.regexIndex===this.count&&this.considerAll()),m}}function a(s){const o=new i;return s.contains.forEach(d=>o.addRule(d.begin,{rule:d,type:"begin"})),s.terminatorEnd&&o.addRule(s.terminatorEnd,{type:"end"}),s.illegal&&o.addRule(s.illegal,{type:"illegal"}),o}function l(s,o){const d=s;if(s.isCompiled)return d;[Yt,Vt,dn,tn].forEach(_=>_(s,o)),e.compilerExtensions.forEach(_=>_(s,o)),s.__beforeBegin=null,[Jt,Qt,en].forEach(_=>_(s,o)),s.isCompiled=!0;let m=null;return typeof s.keywords=="object"&&s.keywords.$pattern&&(s.keywords=Object.assign({},s.keywords),m=s.keywords.$pattern,delete s.keywords.$pattern),m=m||/\w+/,s.keywords&&(s.keywords=it(s.keywords,e.case_insensitive)),d.keywordPatternRe=t(m,!0),o&&(s.begin||(s.begin=/\B|\b/),d.beginRe=t(d.begin),!s.end&&!s.endsWithParent&&(s.end=/\B|\b/),s.end&&(d.endRe=t(d.end)),d.terminatorEnd=oe(d.end)||"",s.endsWithParent&&o.terminatorEnd&&(d.terminatorEnd+=(s.end?"|":"")+o.terminatorEnd)),s.illegal&&(d.illegalRe=t(s.illegal)),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map(function(_){return gn(_==="self"?s:_)})),s.contains.forEach(function(_){l(_,d)}),s.starts&&l(s.starts,o),d.matcher=a(d),d}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=P(e.classNameAliases||{}),l(e)}function rt(e){return e?e.endsWithParent||rt(e.starts):!1}function gn(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(function(t){return P(e,{variants:null},t)})),e.cachedVariants?e.cachedVariants:rt(e)?P(e,{starts:e.starts?P(e.starts):null}):Object.isFrozen(e)?P(e):e}var fn="11.11.1";class pn extends Error{constructor(t,n){super(t),this.name="HTMLInjectionError",this.html=n}}const Me=Ye,je=P,Ke=Symbol("nomatch"),hn=7,st=function(e){const t=Object.create(null),n=Object.create(null),i=[];let a=!0;const l="Could not find the language '{}', did you forget to load/include a language module?",s={disableAutodetect:!0,name:"Plain text",contains:[]};let o={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:Mt};function d(r){return o.noHighlightRe.test(r)}function m(r){let g=r.className+" ";g+=r.parentNode?r.parentNode.className:"";const f=o.languageDetectRe.exec(g);if(f){const E=R(f[1]);return E||(Fe(l.replace("{}",f[1])),Fe("Falling back to no-highlight mode for this block.",r)),E?f[1]:"no-highlight"}return g.split(/\s+/).find(E=>d(E)||R(E))}function _(r,g,f){let E="",v="";typeof g=="object"?(E=r,f=g.ignoreIllegals,v=g.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."),X("10.7.0",`Please use highlight(code, options) instead. +https://github.com/highlightjs/highlight.js/issues/2277`),v=r,E=g),f===void 0&&(f=!0);const x={code:E,language:v};q("before:highlight",x);const A=x.result?x.result:C(x.language,x.code,f);return A.code=x.code,q("after:highlight",A),A}function C(r,g,f,E){const v=Object.create(null);function x(c,u){return c.keywords[u]}function A(){if(!p.keywords){N.addText(w);return}let c=0;p.keywordPatternRe.lastIndex=0;let u=p.keywordPatternRe.exec(w),b="";for(;u;){b+=w.substring(c,u.index);const y=L.case_insensitive?u[0].toLowerCase():u[0],M=x(p,y);if(M){const[D,wt]=M;if(N.addText(b),b="",v[y]=(v[y]||0)+1,v[y]<=hn&&(de+=wt),D.startsWith("_"))b+=u[0];else{const _t=L.classNameAliases[D]||D;I(u[0],_t)}}else b+=u[0];c=p.keywordPatternRe.lastIndex,u=p.keywordPatternRe.exec(w)}b+=w.substring(c),N.addText(b)}function F(){if(w==="")return;let c=null;if(typeof p.subLanguage=="string"){if(!t[p.subLanguage]){N.addText(w);return}c=C(p.subLanguage,w,!0,Pe[p.subLanguage]),Pe[p.subLanguage]=c._top}else c=h(w,p.subLanguage.length?p.subLanguage:null);p.relevance>0&&(de+=c.relevance),N.__addSublanguage(c._emitter,c.language)}function O(){p.subLanguage!=null?F():A(),w=""}function I(c,u){c!==""&&(N.startScope(u),N.addText(c),N.endScope())}function Le(c,u){let b=1;const y=u.length-1;for(;b<=y;){if(!c._emit[b]){b++;continue}const M=L.classNameAliases[c[b]]||c[b],D=u[b];M?I(D,M):(w=D,A(),w=""),b++}}function De(c,u){return c.scope&&typeof c.scope=="string"&&N.openNode(L.classNameAliases[c.scope]||c.scope),c.beginScope&&(c.beginScope._wrap?(I(w,L.classNameAliases[c.beginScope._wrap]||c.beginScope._wrap),w=""):c.beginScope._multi&&(Le(c.beginScope,u),w="")),p=Object.create(c,{parent:{value:p}}),p}function Be(c,u,b){let y=At(c.endRe,b);if(y){if(c["on:end"]){const M=new ze(c);c["on:end"](u,M),M.isMatchIgnored&&(y=!1)}if(y){for(;c.endsParent&&c.parent;)c=c.parent;return c}}if(c.endsWithParent)return Be(c.parent,u,b)}function ht(c){return p.matcher.regexIndex===0?(w+=c[0],1):(xe=!0,0)}function bt(c){const u=c[0],b=c.rule,y=new ze(b),M=[b.__beforeBegin,b["on:begin"]];for(const D of M)if(D&&(D(c,y),y.isMatchIgnored))return ht(u);return b.skip?w+=u:(b.excludeBegin&&(w+=u),O(),!b.returnBegin&&!b.excludeBegin&&(w=u)),De(b,c),b.returnBegin?0:u.length}function mt(c){const u=c[0],b=g.substring(c.index),y=Be(p,c,b);if(!y)return Ke;const M=p;p.endScope&&p.endScope._wrap?(O(),I(u,p.endScope._wrap)):p.endScope&&p.endScope._multi?(O(),Le(p.endScope,c)):M.skip?w+=u:(M.returnEnd||M.excludeEnd||(w+=u),O(),M.excludeEnd&&(w=u));do p.scope&&N.closeNode(),!p.skip&&!p.subLanguage&&(de+=p.relevance),p=p.parent;while(p!==y.parent);return y.starts&&De(y.starts,c),M.returnEnd?0:u.length}function Et(){const c=[];for(let u=p;u!==L;u=u.parent)u.scope&&c.unshift(u.scope);c.forEach(u=>N.openNode(u))}let le={};function Ue(c,u){const b=u&&u[0];if(w+=c,b==null)return O(),0;if(le.type==="begin"&&u.type==="end"&&le.index===u.index&&b===""){if(w+=g.slice(u.index,u.index+1),!a){const y=new Error(`0 width match regex (${r})`);throw y.languageName=r,y.badRule=le.rule,y}return 1}if(le=u,u.type==="begin")return bt(u);if(u.type==="illegal"&&!f){const y=new Error('Illegal lexeme "'+b+'" for mode "'+(p.scope||"")+'"');throw y.mode=p,y}else if(u.type==="end"){const y=mt(u);if(y!==Ke)return y}if(u.type==="illegal"&&b==="")return w+=` +`,1;if(Ne>1e5&&Ne>u.index*3)throw new Error("potential infinite loop, way more iterations than matches");return w+=b,b.length}const L=R(r);if(!L)throw K(l.replace("{}",r)),new Error('Unknown language: "'+r+'"');const yt=un(L);let Se="",p=E||yt;const Pe={},N=new o.__emitter(o);Et();let w="",de=0,j=0,Ne=0,xe=!1;try{if(L.__emitTokens)L.__emitTokens(g,N);else{for(p.matcher.considerAll();;){Ne++,xe?xe=!1:p.matcher.considerAll(),p.matcher.lastIndex=j;const c=p.matcher.exec(g);if(!c)break;const u=g.substring(j,c.index),b=Ue(u,c);j=c.index+b}Ue(g.substring(j))}return N.finalize(),Se=N.toHTML(),{language:r,value:Se,relevance:de,illegal:!1,_emitter:N,_top:p}}catch(c){if(c.message&&c.message.includes("Illegal"))return{language:r,value:Me(g),illegal:!0,relevance:0,_illegalBy:{message:c.message,index:j,context:g.slice(j-100,j+100),mode:c.mode,resultSoFar:Se},_emitter:N};if(a)return{language:r,value:Me(g),illegal:!1,relevance:0,errorRaised:c,_emitter:N,_top:p};throw c}}function T(r){const g={value:Me(r),illegal:!1,relevance:0,_top:s,_emitter:new o.__emitter(o)};return g._emitter.addText(r),g}function h(r,g){g=g||o.languages||Object.keys(t);const f=T(r),E=g.filter(R).filter(ce).map(O=>C(O,r,!1));E.unshift(f);const v=E.sort((O,I)=>{if(O.relevance!==I.relevance)return I.relevance-O.relevance;if(O.language&&I.language){if(R(O.language).supersetOf===I.language)return 1;if(R(I.language).supersetOf===O.language)return-1}return 0}),[x,A]=v,F=x;return F.secondBest=A,F}function S(r,g,f){const E=g&&n[g]||f;r.classList.add("hljs"),r.classList.add(`language-${E}`)}function k(r){let g=null;const f=m(r);if(d(f))return;if(q("before:highlightElement",{el:r,language:f}),r.dataset.highlighted){console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",r);return}if(r.children.length>0&&(o.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(r)),o.throwUnescapedHTML))throw new pn("One of your code blocks includes unescaped HTML.",r.innerHTML);g=r;const E=g.textContent,v=f?_(E,{language:f,ignoreIllegals:!0}):h(E);r.innerHTML=v.value,r.dataset.highlighted="yes",S(r,f,v.language),r.result={language:v.language,re:v.relevance,relevance:v.relevance},v.secondBest&&(r.secondBest={language:v.secondBest.language,relevance:v.secondBest.relevance}),q("after:highlightElement",{el:r,result:v,text:E})}function B(r){o=je(o,r)}const W=()=>{H(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")};function U(){H(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")}let $=!1;function H(){function r(){H()}if(document.readyState==="loading"){$||window.addEventListener("DOMContentLoaded",r,!1),$=!0;return}document.querySelectorAll(o.cssSelector).forEach(k)}function ee(r,g){let f=null;try{f=g(e)}catch(E){if(K("Language definition for '{}' could not be registered.".replace("{}",r)),a)K(E);else throw E;f=s}f.name||(f.name=r),t[r]=f,f.rawDefinition=g.bind(null,e),f.aliases&&ae(f.aliases,{languageName:r})}function G(r){delete t[r];for(const g of Object.keys(n))n[g]===r&&delete n[g]}function Ee(){return Object.keys(t)}function R(r){return r=(r||"").toLowerCase(),t[r]||t[n[r]]}function ae(r,{languageName:g}){typeof r=="string"&&(r=[r]),r.forEach(f=>{n[f.toLowerCase()]=g})}function ce(r){const g=R(r);return g&&!g.disableAutodetect}function ye(r){r["before:highlightBlock"]&&!r["before:highlightElement"]&&(r["before:highlightElement"]=g=>{r["before:highlightBlock"](Object.assign({block:g.el},g))}),r["after:highlightBlock"]&&!r["after:highlightElement"]&&(r["after:highlightElement"]=g=>{r["after:highlightBlock"](Object.assign({block:g.el},g))})}function we(r){ye(r),i.push(r)}function _e(r){const g=i.indexOf(r);g!==-1&&i.splice(g,1)}function q(r,g){const f=r;i.forEach(function(E){E[f]&&E[f](g)})}function ve(r){return X("10.7.0","highlightBlock will be removed entirely in v12.0"),X("10.7.0","Please use highlightElement now."),k(r)}Object.assign(e,{highlight:_,highlightAuto:h,highlightAll:H,highlightElement:k,highlightBlock:ve,configure:B,initHighlighting:W,initHighlightingOnLoad:U,registerLanguage:ee,unregisterLanguage:G,listLanguages:Ee,getLanguage:R,registerAliases:ae,autoDetection:ce,inherit:je,addPlugin:we,removePlugin:_e}),e.debugMode=function(){a=!1},e.safeMode=function(){a=!0},e.versionString=fn,e.regex={concat:Z,lookahead:Je,either:Ae,optional:Ot,anyNumberOfTimes:Tt};for(const r in ue)typeof ue[r]=="object"&&Xe(ue[r]);return Object.assign(e,ue),e},J=st({});J.newInstance=()=>st({});var bn=J;J.HighlightJS=J;J.default=J;const Q=vt(bn),Ze="[A-Za-z$_][0-9A-Za-z$_]*",mn=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],En=["true","false","null","undefined","NaN","Infinity"],at=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ct=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],lt=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],yn=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],wn=[].concat(lt,at,ct);function _n(e){const t=e.regex,n=(f,{after:E})=>{const v="",end:""},l=/<[A-Za-z0-9\\._:-]+\s*\/>/,s={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(f,E)=>{const v=f[0].length+f.index,x=f.input[v];if(x==="<"||x===","){E.ignoreMatch();return}x===">"&&(n(f,{after:v})||E.ignoreMatch());let A;const F=f.input.substring(v);if(A=F.match(/^\s*=/)){E.ignoreMatch();return}if((A=F.match(/^\s+extends\s+/))&&A.index===0){E.ignoreMatch();return}}},o={$pattern:Ze,keyword:mn,literal:En,built_in:wn,"variable.language":yn},d="[0-9](_?[0-9])*",m=`\\.(${d})`,_="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",C={className:"number",variants:[{begin:`(\\b(${_})((${m})|\\.)?|(${m}))[eE][+-]?(${d})\\b`},{begin:`\\b(${_})\\b((${m})\\b|\\.)?|(${m})\\b`},{begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{begin:"\\b0[0-7]+n?\\b"}],relevance:0},T={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},h={begin:".?html`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,T],subLanguage:"xml"}},S={begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,T],subLanguage:"css"}},k={begin:".?gql`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,T],subLanguage:"graphql"}},B={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,T]},U={className:"comment",variants:[e.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},$=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,h,S,k,B,{match:/\$\d+/},C];T.contains=$.concat({begin:/\{/,end:/\}/,keywords:o,contains:["self"].concat($)});const H=[].concat(U,T.contains),ee=H.concat([{begin:/(\s*)\(/,end:/\)/,keywords:o,contains:["self"].concat(H)}]),G={className:"params",begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:ee},Ee={variants:[{match:[/class/,/\s+/,i,/\s+/,/extends/,/\s+/,t.concat(i,"(",t.concat(/\./,i),")*")],scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{match:[/class/,/\s+/,i],scope:{1:"keyword",3:"title.class"}}]},R={relevance:0,match:t.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:"title.class",keywords:{_:[...at,...ct]}},ae={label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},ce={variants:[{match:[/function/,/\s+/,i,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:"keyword",3:"title.function"},label:"func.def",contains:[G],illegal:/%/},ye={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"};function we(f){return t.concat("(?!",f.join("|"),")")}const _e={match:t.concat(/\b/,we([...lt,"super","import"].map(f=>`${f}\\s*\\(`)),i,t.lookahead(/\s*\(/)),className:"title.function",relevance:0},q={begin:t.concat(/\./,t.lookahead(t.concat(i,/(?![0-9A-Za-z$_(])/))),end:i,excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},ve={match:[/get|set/,/\s+/,i,/(?=\()/],className:{1:"keyword",3:"title.function"},contains:[{begin:/\(\)/},G]},r="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+e.UNDERSCORE_IDENT_RE+")\\s*=>",g={match:[/const|var|let/,/\s+/,i,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(r)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[G]};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,exports:{PARAMS_CONTAINS:ee,CLASS_REFERENCE:R},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),ae,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,h,S,k,B,U,{match:/\$\d+/},C,R,{scope:"attr",match:i+t.lookahead(":"),relevance:0},g,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[U,e.REGEXP_MODE,{className:"function",begin:r,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:ee}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:a.begin,end:a.end},{match:l},{begin:s.begin,"on:begin":s.isTrulyOpeningTag,end:s.end}],subLanguage:"xml",contains:[{begin:s.begin,end:s.end,skip:!0,contains:["self"]}]}]},ce,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[G,e.inherit(e.TITLE_MODE,{begin:i,className:"title.function"})]},{match:/\.\.\./,relevance:0},q,{match:"\\$"+i,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[G]},_e,ye,Ee,ve,{match:/\$[(.]/}]}}const vn=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),Sn=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],Nn=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],xn=[...Sn,...Nn],Mn=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),Tn=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),On=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),kn=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse();function An(e){const t=e.regex,n=vn(e),i={begin:/-(webkit|moz|ms|o)-(?=[a-z])/},a="and or not only",l=/@-?\w[\w]*(-\w+)*/,s="[a-zA-Z-][a-zA-Z0-9_-]*",o=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[n.BLOCK_COMMENT,i,n.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:"selector-class",begin:"\\."+s,relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+Tn.join("|")+")"},{begin:":(:)?("+On.join("|")+")"}]},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+kn.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,n.HEXCOLOR,n.IMPORTANT,n.CSS_NUMBER_MODE,...o,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[...o,{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},n.FUNCTION_DISPATCH]},{begin:t.lookahead(/@/),end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",begin:l},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:a,attribute:Mn.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...o,n.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+xn.join("|")+")\\b"}]}}function Rn(e){const t={variants:[e.COMMENT("--","$"),e.COMMENT(/\{-/,/-\}/,{contains:["self"]})]},n={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},i={begin:"\\(",end:"\\)",illegal:'"',contains:[{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},t]},a={begin:/\{/,end:/\}/,contains:i.contains},l={className:"string",begin:"'\\\\?.",end:"'",illegal:"."};return{name:"Elm",keywords:["let","in","if","then","else","case","of","where","module","import","exposing","type","alias","as","infix","infixl","infixr","port","effect","command","subscription"],contains:[{beginKeywords:"port effect module",end:"exposing",keywords:"port effect module where command subscription exposing",contains:[i,t],illegal:"\\W\\.|;"},{begin:"import",end:"$",keywords:"import as exposing",contains:[i,t],illegal:"\\W\\.|;"},{begin:"type",end:"$",keywords:"type alias",contains:[n,i,a,t]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,t]},{begin:"port",end:"$",keywords:"port",contains:[t]},l,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,n,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),t,{begin:"->|<-"}],illegal:/;/}}function Cn(e){const t=e.regex,n=t.concat(/[\p{L}_]/u,t.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),i=/[\p{L}0-9._:-]+/u,a={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},l={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},s=e.inherit(l,{begin:/\(/,end:/\)/}),o=e.inherit(e.APOS_STRING_MODE,{className:"string"}),d=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),m={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[l,d,o,s,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[l,s,d,o]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[d]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[m],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[m],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:t.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:m}]},{className:"tag",begin:t.concat(/<\//,t.lookahead(t.concat(n,/>/))),contains:[{className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}function In(e){const t={className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},n={match:/[{}[\],:]/,className:"punctuation",relevance:0},i=["true","false","null"],a={scope:"literal",beginKeywords:i.join(" ")};return{name:"JSON",aliases:["jsonc"],keywords:{literal:i},contains:[t,n,e.QUOTE_STRING_MODE,a,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}}const ge=(e,t,n,i,a,l)=>{const s=document.createElement("div");s.className=`${e} ${l.sectionClass}`;const o=document.createElement("div"),d=document.createElement("span"),m=document.createElement("span");return d.className=`${t} ${l.amountClass}`,m.className=`${n} ${l.wordClass}`,d.textContent=String(i),m.textContent=a,o.appendChild(d),o.appendChild(m),s.appendChild(o),s},Ln=(e,t,n)=>{const i=e.querySelector(".simply-amount"),a=e.querySelector(".simply-word");i&&(i.textContent=String(t)),a&&(a.textContent=n)},Dn=(e,t)=>{const n="simply-amount",i="simply-word",a=ge("simply-section simply-days-section",n,i,0,"day",t),l=ge("simply-section simply-hours-section",n,i,0,"hour",t),s=ge("simply-section simply-minutes-section",n,i,0,"minute",t),o=ge("simply-section simply-seconds-section",n,i,0,"second",t);return e.appendChild(a),e.appendChild(l),e.appendChild(s),e.appendChild(o),{days:a,hours:l,minutes:s,seconds:o}};/*! + * Project : simplyCountdown.js + * Date : 2024-12-27 + * License : MIT + * Version : 3.0.0 + * Author : Vincent Loy-Serre + * Contributors : + * - Justin Beasley + * - Nathan Smith + * - Mehdi Rezaei + * - mira01 + */const We={year:2024,month:12,day:25,hours:0,minutes:0,seconds:0,words:{days:{lambda:(e,t)=>t>1?e+"s":e,root:"day"},hours:{lambda:(e,t)=>t>1?e+"s":e,root:"hour"},minutes:{lambda:(e,t)=>t>1?e+"s":e,root:"minute"},seconds:{lambda:(e,t)=>t>1?e+"s":e,root:"second"}},plural:!0,inline:!1,inlineSeparator:", ",enableUtc:!1,onEnd:()=>{},refresh:1e3,inlineClass:"simply-countdown-inline",sectionClass:"simply-section",amountClass:"simply-amount",wordClass:"simply-word",zeroPad:!1,countUp:!1,removeZeroUnits:!1,onStop:()=>{},onResume:()=>{},onUpdate:()=>{}},Bn=e=>e instanceof NodeList;function Un(e,t){return`${t.zeroPad?String(e.value).padStart(2,"0"):e.value} ${t.words[e.word].lambda(t.words[e.word].root,e.value)}`}function dt(e,t,n){return n.removeZeroUnits?e.value!==0||t.some(i=>i.value!==0):!0}function Pn(e,t,n){const i=e.filter((a,l)=>dt(a,e.slice(0,l),t)).map(a=>Un(a,t)).join(t.inlineSeparator);n.innerHTML=i}function zn(e,t,n){e.forEach((i,a)=>{i.word==="seconds"||dt(i,e.slice(0,a),t)?(Ln(n[i.word],t.zeroPad?String(i.value).padStart(2,"0"):i.value,t.words[i.word].lambda(t.words[i.word].root,i.value)),n[i.word].style.display=""):n[i.word].style.display="none"})}const Te=(e,t)=>{let n={isPaused:!1,interval:null,targetDate:new Date};const i=h=>h.enableUtc?new Date(Date.UTC(h.year,h.month-1,h.day,h.hours,h.minutes,h.seconds)):new Date(h.year,h.month-1,h.day,h.hours,h.minutes,h.seconds);n.targetDate=i(t);let a=null;t.inline&&(a=document.createElement("span"),a.className=t.inlineClass,e.appendChild(a));const l=t.inline?null:Dn(e,{sectionClass:t.sectionClass,amountClass:t.amountClass,wordClass:t.wordClass}),s=()=>{const h=t.enableUtc?new Date(Date.UTC(new Date().getUTCFullYear(),new Date().getUTCMonth(),new Date().getUTCDate(),new Date().getUTCHours(),new Date().getUTCMinutes(),new Date().getUTCSeconds())):new Date;let S=t.countUp?h.getTime()-n.targetDate.getTime():n.targetDate.getTime()-h.getTime();S<=0&&!t.countUp&&(S=0,n.interval!==null&&clearInterval(n.interval),t.onEnd&&t.onEnd());const k=Math.floor(S/(1e3*60*60*24));S-=k*1e3*60*60*24;const B=Math.floor(S/(1e3*60*60));S-=B*1e3*60*60;const W=Math.floor(S/(1e3*60));S-=W*1e3*60;const U=Math.floor(S/1e3);t.inline&&a?Pn([{value:k,word:"days"},{value:B,word:"hours"},{value:W,word:"minutes"},{value:U,word:"seconds"}],t,a):l&&zn([{value:k,word:"days"},{value:B,word:"hours"},{value:W,word:"minutes"},{value:U,word:"seconds"}],t,l)},o=()=>{n.interval=setInterval(s,t.refresh),s()},d=()=>{var h;n.interval!==null&&(clearInterval(n.interval),n.interval=null),n.isPaused=!0,(h=t.onStop)==null||h.call(t)},m=()=>{var h;n.isPaused&&(o(),n.isPaused=!1,(h=t.onResume)==null||h.call(t))},_=h=>{var S;Object.assign(t,h),(h.year!==void 0||h.month!==void 0||h.day!==void 0||h.hours!==void 0||h.minutes!==void 0||h.seconds!==void 0)&&(n.targetDate=i(t)),(S=t.onUpdate)==null||S.call(t,h),n.isPaused||(n.interval&&clearInterval(n.interval),o())},C=()=>({...n});o();const T=new MutationObserver(h=>{h.forEach(S=>{S.removedNodes.forEach(k=>{k===e&&(n.interval!==null&&clearInterval(n.interval),T.disconnect())})})});return e.parentNode&&T.observe(e.parentNode,{childList:!0}),{stopCountdown:d,resumeCountdown:m,updateCountdown:_,getState:C}},qe=e=>{const t=e;return t.stopCountdown=()=>e.forEach(n=>n.stopCountdown()),t.resumeCountdown=()=>e.forEach(n=>n.resumeCountdown()),t.updateCountdown=n=>e.forEach(i=>i.updateCountdown(n)),t.getState=()=>e.map(n=>n.getState()),t},z=(e,t=We)=>{const n={...We,...t};if(typeof e=="string"){const i=document.querySelectorAll(e),a=Array.from(i).map(l=>Te(l,n));return a.length===1?a[0]:qe(a)}if(Bn(e)){const i=Array.from(e).map(a=>Te(a,n));return i.length===1?i[0]:qe(i)}return Te(e,n)};Q.registerLanguage("javascript",_n);Q.registerLanguage("css",An);Q.registerLanguage("elm",Rn);Q.registerLanguage("html",Cn);Q.registerLanguage("json",In);Q.highlightAll();document.querySelectorAll(".package-manager-btn").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.manager;document.querySelectorAll(".package-manager-btn").forEach(n=>{n.classList.toggle("active",n===e)}),document.querySelectorAll(".package-manager-content").forEach(n=>{n.classList.toggle("active",n.dataset.manager===t)})})});document.querySelectorAll("pre").forEach(e=>{e.querySelectorAll(".copy-button").forEach(a=>a.remove());const t=document.createElement("button");t.className="copy-button absolute top-3 right-3 p-2 text-slate-400 hover:text-slate-300 bg-slate-800/50 rounded-lg opacity-0 transition-opacity group",t.title="Copy code to clipboard";const n=document.createElement("span");n.className="icon-[lucide--copy] w-4 h-4 copy-icon block group-data-[copied]:hidden";const i=document.createElement("span");i.className="icon-[lucide--check] w-4 h-4 check-icon hidden group-data-[copied]:block text-green-400",t.appendChild(n),t.appendChild(i),e.classList.add("relative"),e.appendChild(t),t.addEventListener("click",async()=>{const l=(e.querySelector("code")||e).textContent;try{await navigator.clipboard.writeText(l),t.dataset.copied=!0,setTimeout(()=>{delete t.dataset.copied},2e3)}catch(s){console.error("Failed to copy:",s)}})});const Y=document.createElement("div");Y.className="sc-doc-block-glow";document.body.appendChild(Y);let pe=null,te=null,ut=0,gt=0;function ft(){pe&&(Y.style.left=`${ut}px`,Y.style.top=`${gt}px`,Y.style.opacity="1",te=requestAnimationFrame(ft))}document.querySelectorAll(".sc-doc-block").forEach(e=>{e.addEventListener("mousemove",t=>{ut=t.clientX,gt=t.clientY,pe||(pe=e,te=requestAnimationFrame(ft))}),e.addEventListener("mouseleave",()=>{pe=null,te&&(cancelAnimationFrame(te),te=null),Y.style.opacity="0"})});const ne=document.querySelector(".nav-wrapper");ne.classList.add("transition-colors","duration-300");function pt(){window.scrollY===0?(ne.classList.remove("bg-indigo-950/50","backdrop-blur-sm"),ne.classList.add("bg-transparent")):(ne.classList.remove("bg-transparent"),ne.classList.add("bg-indigo-950/50","backdrop-blur-sm"))}pt();window.addEventListener("scroll",pt);const se=new Date,V=se.getFullYear()+1,Ie=(se.getMonth()+2)%12,$n=new Date(se.getTime()+24*60*60*1e3);z(".simply-countdown-one",{year:V,month:Ie,day:27,zeroPad:!0,enableUtc:!0,sectionClass:"CUSTOM-SECTION-CLASS",amountClass:"CUSTOM-AMOUNT-CLASS",wordClass:"CUSTOM-WORD-CLASS"});z(".simply-countdown-two",{year:se.getFullYear(),month:se.getMonth()+1,day:$n.getDate(),hours:12,minutes:5,zeroPad:!1,removeZeroUnits:!0});const Hn=document.querySelector(".simply-countdown-inline");z(Hn,{year:V,month:6,day:28,inline:!0});z(".simply-countdown-countup",{year:2023,month:1,day:1,countUp:!0,removeZeroUnits:!0});z("#simply-countdown-losange",{year:V,month:6,day:28});z(".simply-countdown-circle-demo",{year:V,month:Ie,day:28,zeroPad:!0});z("#custom-plural",{year:V,month:12,day:31,hours:23,minutes:59,seconds:59,words:{days:{lambda:(e,t)=>fe(t,"дСнь","дня","Π΄Π½Π΅ΠΉ"),root:"дСнь"},hours:{lambda:(e,t)=>fe(t,"час","часа","часов"),root:"час"},minutes:{lambda:(e,t)=>fe(t,"ΠΌΠΈΠ½ΡƒΡ‚Π°","ΠΌΠΈΠ½ΡƒΡ‚Ρ‹","ΠΌΠΈΠ½ΡƒΡ‚"),root:"ΠΌΠΈΠ½ΡƒΡ‚Π°"},seconds:{lambda:(e,t)=>fe(t,"сСкунда","сСкунды","сСкунд"),root:"сСкунда"}},zeroPad:!0});const ie=z(".simply-countdown-control",{year:V,month:Ie,day:27,zeroPad:!1,enableUtc:!0,onUpdate:()=>{he(ie.getState())},onResume:()=>{he(ie.getState())},onStop:()=>{he(ie.getState())}}),he=e=>{let t=document.querySelector(".state-box");t.innerHTML=JSON.stringify(e,null,2)};he(ie.getState());window.controlDemo=ie;function fe(e,t,n,i){const a=e%10,l=e%100;return l>=11&&l<=14?`${i}`:a===1&&l!==11?`${t}`:a>=2&&a<=4&&!(l>=12&&l<=14)?`${n}`:`${i}`}document.querySelectorAll('nav a[href^="#"]').forEach(e=>{e.addEventListener("click",function(t){t.preventDefault();const n=this.getAttribute("href"),i=document.querySelector(n);if(i){const l=document.querySelector("nav").offsetHeight+40;window.scrollTo({top:i.offsetTop-l,behavior:"smooth"})}})});const Oe=document.getElementById("scrollTopBtn");function Gn(){const e=document.documentElement.scrollHeight*.05;window.scrollY>e?Oe.classList.remove("opacity-0","translate-y-10"):Oe.classList.add("opacity-0","translate-y-10")}Oe.addEventListener("click",()=>{window.scrollTo({top:0,behavior:"smooth"})});window.addEventListener("scroll",Gn); +//# sourceMappingURL=main.min.js.map diff --git a/docs/dist/assets/main.min.js.map b/docs/dist/assets/main.min.js.map new file mode 100644 index 0000000..585a75a --- /dev/null +++ b/docs/dist/assets/main.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.min.js","sources":["../../../node_modules/highlight.js/lib/core.js","../../../node_modules/highlight.js/es/languages/javascript.js","../../../node_modules/highlight.js/es/languages/css.js","../../../node_modules/highlight.js/es/languages/elm.js","../../../node_modules/highlight.js/es/languages/xml.js","../../../node_modules/highlight.js/es/languages/json.js","../../../src/core/dom.ts","../../../src/core/simplyCountdown.ts","../../src/assets/js/main.js"],"sourcesContent":["/* eslint-disable no-multi-assign */\n\nfunction deepFreeze(obj) {\n if (obj instanceof Map) {\n obj.clear =\n obj.delete =\n obj.set =\n function () {\n throw new Error('map is read-only');\n };\n } else if (obj instanceof Set) {\n obj.add =\n obj.clear =\n obj.delete =\n function () {\n throw new Error('set is read-only');\n };\n }\n\n // Freeze self\n Object.freeze(obj);\n\n Object.getOwnPropertyNames(obj).forEach((name) => {\n const prop = obj[name];\n const type = typeof prop;\n\n // Freeze prop if it is an object or function and also not already frozen\n if ((type === 'object' || type === 'function') && !Object.isFrozen(prop)) {\n deepFreeze(prop);\n }\n });\n\n return obj;\n}\n\n/** @typedef {import('highlight.js').CallbackResponse} CallbackResponse */\n/** @typedef {import('highlight.js').CompiledMode} CompiledMode */\n/** @implements CallbackResponse */\n\nclass Response {\n /**\n * @param {CompiledMode} mode\n */\n constructor(mode) {\n // eslint-disable-next-line no-undefined\n if (mode.data === undefined) mode.data = {};\n\n this.data = mode.data;\n this.isMatchIgnored = false;\n }\n\n ignoreMatch() {\n this.isMatchIgnored = true;\n }\n}\n\n/**\n * @param {string} value\n * @returns {string}\n */\nfunction escapeHTML(value) {\n return value\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * performs a shallow merge of multiple objects into one\n *\n * @template T\n * @param {T} original\n * @param {Record[]} objects\n * @returns {T} a single new object\n */\nfunction inherit$1(original, ...objects) {\n /** @type Record */\n const result = Object.create(null);\n\n for (const key in original) {\n result[key] = original[key];\n }\n objects.forEach(function(obj) {\n for (const key in obj) {\n result[key] = obj[key];\n }\n });\n return /** @type {T} */ (result);\n}\n\n/**\n * @typedef {object} Renderer\n * @property {(text: string) => void} addText\n * @property {(node: Node) => void} openNode\n * @property {(node: Node) => void} closeNode\n * @property {() => string} value\n */\n\n/** @typedef {{scope?: string, language?: string, sublanguage?: boolean}} Node */\n/** @typedef {{walk: (r: Renderer) => void}} Tree */\n/** */\n\nconst SPAN_CLOSE = '';\n\n/**\n * Determines if a node needs to be wrapped in \n *\n * @param {Node} node */\nconst emitsWrappingTags = (node) => {\n // rarely we can have a sublanguage where language is undefined\n // TODO: track down why\n return !!node.scope;\n};\n\n/**\n *\n * @param {string} name\n * @param {{prefix:string}} options\n */\nconst scopeToCSSClass = (name, { prefix }) => {\n // sub-language\n if (name.startsWith(\"language:\")) {\n return name.replace(\"language:\", \"language-\");\n }\n // tiered scope: comment.line\n if (name.includes(\".\")) {\n const pieces = name.split(\".\");\n return [\n `${prefix}${pieces.shift()}`,\n ...(pieces.map((x, i) => `${x}${\"_\".repeat(i + 1)}`))\n ].join(\" \");\n }\n // simple scope\n return `${prefix}${name}`;\n};\n\n/** @type {Renderer} */\nclass HTMLRenderer {\n /**\n * Creates a new HTMLRenderer\n *\n * @param {Tree} parseTree - the parse tree (must support `walk` API)\n * @param {{classPrefix: string}} options\n */\n constructor(parseTree, options) {\n this.buffer = \"\";\n this.classPrefix = options.classPrefix;\n parseTree.walk(this);\n }\n\n /**\n * Adds texts to the output stream\n *\n * @param {string} text */\n addText(text) {\n this.buffer += escapeHTML(text);\n }\n\n /**\n * Adds a node open to the output stream (if needed)\n *\n * @param {Node} node */\n openNode(node) {\n if (!emitsWrappingTags(node)) return;\n\n const className = scopeToCSSClass(node.scope,\n { prefix: this.classPrefix });\n this.span(className);\n }\n\n /**\n * Adds a node close to the output stream (if needed)\n *\n * @param {Node} node */\n closeNode(node) {\n if (!emitsWrappingTags(node)) return;\n\n this.buffer += SPAN_CLOSE;\n }\n\n /**\n * returns the accumulated buffer\n */\n value() {\n return this.buffer;\n }\n\n // helpers\n\n /**\n * Builds a span element\n *\n * @param {string} className */\n span(className) {\n this.buffer += ``;\n }\n}\n\n/** @typedef {{scope?: string, language?: string, children: Node[]} | string} Node */\n/** @typedef {{scope?: string, language?: string, children: Node[]} } DataNode */\n/** @typedef {import('highlight.js').Emitter} Emitter */\n/** */\n\n/** @returns {DataNode} */\nconst newNode = (opts = {}) => {\n /** @type DataNode */\n const result = { children: [] };\n Object.assign(result, opts);\n return result;\n};\n\nclass TokenTree {\n constructor() {\n /** @type DataNode */\n this.rootNode = newNode();\n this.stack = [this.rootNode];\n }\n\n get top() {\n return this.stack[this.stack.length - 1];\n }\n\n get root() { return this.rootNode; }\n\n /** @param {Node} node */\n add(node) {\n this.top.children.push(node);\n }\n\n /** @param {string} scope */\n openNode(scope) {\n /** @type Node */\n const node = newNode({ scope });\n this.add(node);\n this.stack.push(node);\n }\n\n closeNode() {\n if (this.stack.length > 1) {\n return this.stack.pop();\n }\n // eslint-disable-next-line no-undefined\n return undefined;\n }\n\n closeAllNodes() {\n while (this.closeNode());\n }\n\n toJSON() {\n return JSON.stringify(this.rootNode, null, 4);\n }\n\n /**\n * @typedef { import(\"./html_renderer\").Renderer } Renderer\n * @param {Renderer} builder\n */\n walk(builder) {\n // this does not\n return this.constructor._walk(builder, this.rootNode);\n // this works\n // return TokenTree._walk(builder, this.rootNode);\n }\n\n /**\n * @param {Renderer} builder\n * @param {Node} node\n */\n static _walk(builder, node) {\n if (typeof node === \"string\") {\n builder.addText(node);\n } else if (node.children) {\n builder.openNode(node);\n node.children.forEach((child) => this._walk(builder, child));\n builder.closeNode(node);\n }\n return builder;\n }\n\n /**\n * @param {Node} node\n */\n static _collapse(node) {\n if (typeof node === \"string\") return;\n if (!node.children) return;\n\n if (node.children.every(el => typeof el === \"string\")) {\n // node.text = node.children.join(\"\");\n // delete node.children;\n node.children = [node.children.join(\"\")];\n } else {\n node.children.forEach((child) => {\n TokenTree._collapse(child);\n });\n }\n }\n}\n\n/**\n Currently this is all private API, but this is the minimal API necessary\n that an Emitter must implement to fully support the parser.\n\n Minimal interface:\n\n - addText(text)\n - __addSublanguage(emitter, subLanguageName)\n - startScope(scope)\n - endScope()\n - finalize()\n - toHTML()\n\n*/\n\n/**\n * @implements {Emitter}\n */\nclass TokenTreeEmitter extends TokenTree {\n /**\n * @param {*} options\n */\n constructor(options) {\n super();\n this.options = options;\n }\n\n /**\n * @param {string} text\n */\n addText(text) {\n if (text === \"\") { return; }\n\n this.add(text);\n }\n\n /** @param {string} scope */\n startScope(scope) {\n this.openNode(scope);\n }\n\n endScope() {\n this.closeNode();\n }\n\n /**\n * @param {Emitter & {root: DataNode}} emitter\n * @param {string} name\n */\n __addSublanguage(emitter, name) {\n /** @type DataNode */\n const node = emitter.root;\n if (name) node.scope = `language:${name}`;\n\n this.add(node);\n }\n\n toHTML() {\n const renderer = new HTMLRenderer(this, this.options);\n return renderer.value();\n }\n\n finalize() {\n this.closeAllNodes();\n return true;\n }\n}\n\n/**\n * @param {string} value\n * @returns {RegExp}\n * */\n\n/**\n * @param {RegExp | string } re\n * @returns {string}\n */\nfunction source(re) {\n if (!re) return null;\n if (typeof re === \"string\") return re;\n\n return re.source;\n}\n\n/**\n * @param {RegExp | string } re\n * @returns {string}\n */\nfunction lookahead(re) {\n return concat('(?=', re, ')');\n}\n\n/**\n * @param {RegExp | string } re\n * @returns {string}\n */\nfunction anyNumberOfTimes(re) {\n return concat('(?:', re, ')*');\n}\n\n/**\n * @param {RegExp | string } re\n * @returns {string}\n */\nfunction optional(re) {\n return concat('(?:', re, ')?');\n}\n\n/**\n * @param {...(RegExp | string) } args\n * @returns {string}\n */\nfunction concat(...args) {\n const joined = args.map((x) => source(x)).join(\"\");\n return joined;\n}\n\n/**\n * @param { Array } args\n * @returns {object}\n */\nfunction stripOptionsFromArgs(args) {\n const opts = args[args.length - 1];\n\n if (typeof opts === 'object' && opts.constructor === Object) {\n args.splice(args.length - 1, 1);\n return opts;\n } else {\n return {};\n }\n}\n\n/** @typedef { {capture?: boolean} } RegexEitherOptions */\n\n/**\n * Any of the passed expresssions may match\n *\n * Creates a huge this | this | that | that match\n * @param {(RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]} args\n * @returns {string}\n */\nfunction either(...args) {\n /** @type { object & {capture?: boolean} } */\n const opts = stripOptionsFromArgs(args);\n const joined = '('\n + (opts.capture ? \"\" : \"?:\")\n + args.map((x) => source(x)).join(\"|\") + \")\";\n return joined;\n}\n\n/**\n * @param {RegExp | string} re\n * @returns {number}\n */\nfunction countMatchGroups(re) {\n return (new RegExp(re.toString() + '|')).exec('').length - 1;\n}\n\n/**\n * Does lexeme start with a regular expression match at the beginning\n * @param {RegExp} re\n * @param {string} lexeme\n */\nfunction startsWith(re, lexeme) {\n const match = re && re.exec(lexeme);\n return match && match.index === 0;\n}\n\n// BACKREF_RE matches an open parenthesis or backreference. To avoid\n// an incorrect parse, it additionally matches the following:\n// - [...] elements, where the meaning of parentheses and escapes change\n// - other escape sequences, so we do not misparse escape sequences as\n// interesting elements\n// - non-matching or lookahead parentheses, which do not capture. These\n// follow the '(' with a '?'.\nconst BACKREF_RE = /\\[(?:[^\\\\\\]]|\\\\.)*\\]|\\(\\??|\\\\([1-9][0-9]*)|\\\\./;\n\n// **INTERNAL** Not intended for outside usage\n// join logically computes regexps.join(separator), but fixes the\n// backreferences so they continue to match.\n// it also places each individual regular expression into it's own\n// match group, keeping track of the sequencing of those match groups\n// is currently an exercise for the caller. :-)\n/**\n * @param {(string | RegExp)[]} regexps\n * @param {{joinWith: string}} opts\n * @returns {string}\n */\nfunction _rewriteBackreferences(regexps, { joinWith }) {\n let numCaptures = 0;\n\n return regexps.map((regex) => {\n numCaptures += 1;\n const offset = numCaptures;\n let re = source(regex);\n let out = '';\n\n while (re.length > 0) {\n const match = BACKREF_RE.exec(re);\n if (!match) {\n out += re;\n break;\n }\n out += re.substring(0, match.index);\n re = re.substring(match.index + match[0].length);\n if (match[0][0] === '\\\\' && match[1]) {\n // Adjust the backreference.\n out += '\\\\' + String(Number(match[1]) + offset);\n } else {\n out += match[0];\n if (match[0] === '(') {\n numCaptures++;\n }\n }\n }\n return out;\n }).map(re => `(${re})`).join(joinWith);\n}\n\n/** @typedef {import('highlight.js').Mode} Mode */\n/** @typedef {import('highlight.js').ModeCallback} ModeCallback */\n\n// Common regexps\nconst MATCH_NOTHING_RE = /\\b\\B/;\nconst IDENT_RE = '[a-zA-Z]\\\\w*';\nconst UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\\\w*';\nconst NUMBER_RE = '\\\\b\\\\d+(\\\\.\\\\d+)?';\nconst C_NUMBER_RE = '(-?)(\\\\b0[xX][a-fA-F0-9]+|(\\\\b\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)'; // 0x..., 0..., decimal, float\nconst BINARY_NUMBER_RE = '\\\\b(0b[01]+)'; // 0b...\nconst RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\\\*|\\\\*=|\\\\+|\\\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\\\?|\\\\[|\\\\{|\\\\(|\\\\^|\\\\^=|\\\\||\\\\|=|\\\\|\\\\||~';\n\n/**\n* @param { Partial & {binary?: string | RegExp} } opts\n*/\nconst SHEBANG = (opts = {}) => {\n const beginShebang = /^#![ ]*\\//;\n if (opts.binary) {\n opts.begin = concat(\n beginShebang,\n /.*\\b/,\n opts.binary,\n /\\b.*/);\n }\n return inherit$1({\n scope: 'meta',\n begin: beginShebang,\n end: /$/,\n relevance: 0,\n /** @type {ModeCallback} */\n \"on:begin\": (m, resp) => {\n if (m.index !== 0) resp.ignoreMatch();\n }\n }, opts);\n};\n\n// Common modes\nconst BACKSLASH_ESCAPE = {\n begin: '\\\\\\\\[\\\\s\\\\S]', relevance: 0\n};\nconst APOS_STRING_MODE = {\n scope: 'string',\n begin: '\\'',\n end: '\\'',\n illegal: '\\\\n',\n contains: [BACKSLASH_ESCAPE]\n};\nconst QUOTE_STRING_MODE = {\n scope: 'string',\n begin: '\"',\n end: '\"',\n illegal: '\\\\n',\n contains: [BACKSLASH_ESCAPE]\n};\nconst PHRASAL_WORDS_MODE = {\n begin: /\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\\b/\n};\n/**\n * Creates a comment mode\n *\n * @param {string | RegExp} begin\n * @param {string | RegExp} end\n * @param {Mode | {}} [modeOptions]\n * @returns {Partial}\n */\nconst COMMENT = function(begin, end, modeOptions = {}) {\n const mode = inherit$1(\n {\n scope: 'comment',\n begin,\n end,\n contains: []\n },\n modeOptions\n );\n mode.contains.push({\n scope: 'doctag',\n // hack to avoid the space from being included. the space is necessary to\n // match here to prevent the plain text rule below from gobbling up doctags\n begin: '[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)',\n end: /(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,\n excludeBegin: true,\n relevance: 0\n });\n const ENGLISH_WORD = either(\n // list of common 1 and 2 letter words in English\n \"I\",\n \"a\",\n \"is\",\n \"so\",\n \"us\",\n \"to\",\n \"at\",\n \"if\",\n \"in\",\n \"it\",\n \"on\",\n // note: this is not an exhaustive list of contractions, just popular ones\n /[A-Za-z]+['](d|ve|re|ll|t|s|n)/, // contractions - can't we'd they're let's, etc\n /[A-Za-z]+[-][a-z]+/, // `no-way`, etc.\n /[A-Za-z][a-z]{2,}/ // allow capitalized words at beginning of sentences\n );\n // looking like plain text, more likely to be a comment\n mode.contains.push(\n {\n // TODO: how to include \", (, ) without breaking grammars that use these for\n // comment delimiters?\n // begin: /[ ]+([()\"]?([A-Za-z'-]{3,}|is|a|I|so|us|[tT][oO]|at|if|in|it|on)[.]?[()\":]?([.][ ]|[ ]|\\))){3}/\n // ---\n\n // this tries to find sequences of 3 english words in a row (without any\n // \"programming\" type syntax) this gives us a strong signal that we've\n // TRULY found a comment - vs perhaps scanning with the wrong language.\n // It's possible to find something that LOOKS like the start of the\n // comment - but then if there is no readable text - good chance it is a\n // false match and not a comment.\n //\n // for a visual example please see:\n // https://github.com/highlightjs/highlight.js/issues/2827\n\n begin: concat(\n /[ ]+/, // necessary to prevent us gobbling up doctags like /* @author Bob Mcgill */\n '(',\n ENGLISH_WORD,\n /[.]?[:]?([.][ ]|[ ])/,\n '){3}') // look for 3 words in a row\n }\n );\n return mode;\n};\nconst C_LINE_COMMENT_MODE = COMMENT('//', '$');\nconst C_BLOCK_COMMENT_MODE = COMMENT('/\\\\*', '\\\\*/');\nconst HASH_COMMENT_MODE = COMMENT('#', '$');\nconst NUMBER_MODE = {\n scope: 'number',\n begin: NUMBER_RE,\n relevance: 0\n};\nconst C_NUMBER_MODE = {\n scope: 'number',\n begin: C_NUMBER_RE,\n relevance: 0\n};\nconst BINARY_NUMBER_MODE = {\n scope: 'number',\n begin: BINARY_NUMBER_RE,\n relevance: 0\n};\nconst REGEXP_MODE = {\n scope: \"regexp\",\n begin: /\\/(?=[^/\\n]*\\/)/,\n end: /\\/[gimuy]*/,\n contains: [\n BACKSLASH_ESCAPE,\n {\n begin: /\\[/,\n end: /\\]/,\n relevance: 0,\n contains: [BACKSLASH_ESCAPE]\n }\n ]\n};\nconst TITLE_MODE = {\n scope: 'title',\n begin: IDENT_RE,\n relevance: 0\n};\nconst UNDERSCORE_TITLE_MODE = {\n scope: 'title',\n begin: UNDERSCORE_IDENT_RE,\n relevance: 0\n};\nconst METHOD_GUARD = {\n // excludes method names from keyword processing\n begin: '\\\\.\\\\s*' + UNDERSCORE_IDENT_RE,\n relevance: 0\n};\n\n/**\n * Adds end same as begin mechanics to a mode\n *\n * Your mode must include at least a single () match group as that first match\n * group is what is used for comparison\n * @param {Partial} mode\n */\nconst END_SAME_AS_BEGIN = function(mode) {\n return Object.assign(mode,\n {\n /** @type {ModeCallback} */\n 'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; },\n /** @type {ModeCallback} */\n 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); }\n });\n};\n\nvar MODES = /*#__PURE__*/Object.freeze({\n __proto__: null,\n APOS_STRING_MODE: APOS_STRING_MODE,\n BACKSLASH_ESCAPE: BACKSLASH_ESCAPE,\n BINARY_NUMBER_MODE: BINARY_NUMBER_MODE,\n BINARY_NUMBER_RE: BINARY_NUMBER_RE,\n COMMENT: COMMENT,\n C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE,\n C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE,\n C_NUMBER_MODE: C_NUMBER_MODE,\n C_NUMBER_RE: C_NUMBER_RE,\n END_SAME_AS_BEGIN: END_SAME_AS_BEGIN,\n HASH_COMMENT_MODE: HASH_COMMENT_MODE,\n IDENT_RE: IDENT_RE,\n MATCH_NOTHING_RE: MATCH_NOTHING_RE,\n METHOD_GUARD: METHOD_GUARD,\n NUMBER_MODE: NUMBER_MODE,\n NUMBER_RE: NUMBER_RE,\n PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE,\n QUOTE_STRING_MODE: QUOTE_STRING_MODE,\n REGEXP_MODE: REGEXP_MODE,\n RE_STARTERS_RE: RE_STARTERS_RE,\n SHEBANG: SHEBANG,\n TITLE_MODE: TITLE_MODE,\n UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE,\n UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE\n});\n\n/**\n@typedef {import('highlight.js').CallbackResponse} CallbackResponse\n@typedef {import('highlight.js').CompilerExt} CompilerExt\n*/\n\n// Grammar extensions / plugins\n// See: https://github.com/highlightjs/highlight.js/issues/2833\n\n// Grammar extensions allow \"syntactic sugar\" to be added to the grammar modes\n// without requiring any underlying changes to the compiler internals.\n\n// `compileMatch` being the perfect small example of now allowing a grammar\n// author to write `match` when they desire to match a single expression rather\n// than being forced to use `begin`. The extension then just moves `match` into\n// `begin` when it runs. Ie, no features have been added, but we've just made\n// the experience of writing (and reading grammars) a little bit nicer.\n\n// ------\n\n// TODO: We need negative look-behind support to do this properly\n/**\n * Skip a match if it has a preceding dot\n *\n * This is used for `beginKeywords` to prevent matching expressions such as\n * `bob.keyword.do()`. The mode compiler automatically wires this up as a\n * special _internal_ 'on:begin' callback for modes with `beginKeywords`\n * @param {RegExpMatchArray} match\n * @param {CallbackResponse} response\n */\nfunction skipIfHasPrecedingDot(match, response) {\n const before = match.input[match.index - 1];\n if (before === \".\") {\n response.ignoreMatch();\n }\n}\n\n/**\n *\n * @type {CompilerExt}\n */\nfunction scopeClassName(mode, _parent) {\n // eslint-disable-next-line no-undefined\n if (mode.className !== undefined) {\n mode.scope = mode.className;\n delete mode.className;\n }\n}\n\n/**\n * `beginKeywords` syntactic sugar\n * @type {CompilerExt}\n */\nfunction beginKeywords(mode, parent) {\n if (!parent) return;\n if (!mode.beginKeywords) return;\n\n // for languages with keywords that include non-word characters checking for\n // a word boundary is not sufficient, so instead we check for a word boundary\n // or whitespace - this does no harm in any case since our keyword engine\n // doesn't allow spaces in keywords anyways and we still check for the boundary\n // first\n mode.begin = '\\\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\\\.)(?=\\\\b|\\\\s)';\n mode.__beforeBegin = skipIfHasPrecedingDot;\n mode.keywords = mode.keywords || mode.beginKeywords;\n delete mode.beginKeywords;\n\n // prevents double relevance, the keywords themselves provide\n // relevance, the mode doesn't need to double it\n // eslint-disable-next-line no-undefined\n if (mode.relevance === undefined) mode.relevance = 0;\n}\n\n/**\n * Allow `illegal` to contain an array of illegal values\n * @type {CompilerExt}\n */\nfunction compileIllegal(mode, _parent) {\n if (!Array.isArray(mode.illegal)) return;\n\n mode.illegal = either(...mode.illegal);\n}\n\n/**\n * `match` to match a single expression for readability\n * @type {CompilerExt}\n */\nfunction compileMatch(mode, _parent) {\n if (!mode.match) return;\n if (mode.begin || mode.end) throw new Error(\"begin & end are not supported with match\");\n\n mode.begin = mode.match;\n delete mode.match;\n}\n\n/**\n * provides the default 1 relevance to all modes\n * @type {CompilerExt}\n */\nfunction compileRelevance(mode, _parent) {\n // eslint-disable-next-line no-undefined\n if (mode.relevance === undefined) mode.relevance = 1;\n}\n\n// allow beforeMatch to act as a \"qualifier\" for the match\n// the full match begin must be [beforeMatch][begin]\nconst beforeMatchExt = (mode, parent) => {\n if (!mode.beforeMatch) return;\n // starts conflicts with endsParent which we need to make sure the child\n // rule is not matched multiple times\n if (mode.starts) throw new Error(\"beforeMatch cannot be used with starts\");\n\n const originalMode = Object.assign({}, mode);\n Object.keys(mode).forEach((key) => { delete mode[key]; });\n\n mode.keywords = originalMode.keywords;\n mode.begin = concat(originalMode.beforeMatch, lookahead(originalMode.begin));\n mode.starts = {\n relevance: 0,\n contains: [\n Object.assign(originalMode, { endsParent: true })\n ]\n };\n mode.relevance = 0;\n\n delete originalMode.beforeMatch;\n};\n\n// keywords that should have no default relevance value\nconst COMMON_KEYWORDS = [\n 'of',\n 'and',\n 'for',\n 'in',\n 'not',\n 'or',\n 'if',\n 'then',\n 'parent', // common variable name\n 'list', // common variable name\n 'value' // common variable name\n];\n\nconst DEFAULT_KEYWORD_SCOPE = \"keyword\";\n\n/**\n * Given raw keywords from a language definition, compile them.\n *\n * @param {string | Record | Array} rawKeywords\n * @param {boolean} caseInsensitive\n */\nfunction compileKeywords(rawKeywords, caseInsensitive, scopeName = DEFAULT_KEYWORD_SCOPE) {\n /** @type {import(\"highlight.js/private\").KeywordDict} */\n const compiledKeywords = Object.create(null);\n\n // input can be a string of keywords, an array of keywords, or a object with\n // named keys representing scopeName (which can then point to a string or array)\n if (typeof rawKeywords === 'string') {\n compileList(scopeName, rawKeywords.split(\" \"));\n } else if (Array.isArray(rawKeywords)) {\n compileList(scopeName, rawKeywords);\n } else {\n Object.keys(rawKeywords).forEach(function(scopeName) {\n // collapse all our objects back into the parent object\n Object.assign(\n compiledKeywords,\n compileKeywords(rawKeywords[scopeName], caseInsensitive, scopeName)\n );\n });\n }\n return compiledKeywords;\n\n // ---\n\n /**\n * Compiles an individual list of keywords\n *\n * Ex: \"for if when while|5\"\n *\n * @param {string} scopeName\n * @param {Array} keywordList\n */\n function compileList(scopeName, keywordList) {\n if (caseInsensitive) {\n keywordList = keywordList.map(x => x.toLowerCase());\n }\n keywordList.forEach(function(keyword) {\n const pair = keyword.split('|');\n compiledKeywords[pair[0]] = [scopeName, scoreForKeyword(pair[0], pair[1])];\n });\n }\n}\n\n/**\n * Returns the proper score for a given keyword\n *\n * Also takes into account comment keywords, which will be scored 0 UNLESS\n * another score has been manually assigned.\n * @param {string} keyword\n * @param {string} [providedScore]\n */\nfunction scoreForKeyword(keyword, providedScore) {\n // manual scores always win over common keywords\n // so you can force a score of 1 if you really insist\n if (providedScore) {\n return Number(providedScore);\n }\n\n return commonKeyword(keyword) ? 0 : 1;\n}\n\n/**\n * Determines if a given keyword is common or not\n *\n * @param {string} keyword */\nfunction commonKeyword(keyword) {\n return COMMON_KEYWORDS.includes(keyword.toLowerCase());\n}\n\n/*\n\nFor the reasoning behind this please see:\nhttps://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419\n\n*/\n\n/**\n * @type {Record}\n */\nconst seenDeprecations = {};\n\n/**\n * @param {string} message\n */\nconst error = (message) => {\n console.error(message);\n};\n\n/**\n * @param {string} message\n * @param {any} args\n */\nconst warn = (message, ...args) => {\n console.log(`WARN: ${message}`, ...args);\n};\n\n/**\n * @param {string} version\n * @param {string} message\n */\nconst deprecated = (version, message) => {\n if (seenDeprecations[`${version}/${message}`]) return;\n\n console.log(`Deprecated as of ${version}. ${message}`);\n seenDeprecations[`${version}/${message}`] = true;\n};\n\n/* eslint-disable no-throw-literal */\n\n/**\n@typedef {import('highlight.js').CompiledMode} CompiledMode\n*/\n\nconst MultiClassError = new Error();\n\n/**\n * Renumbers labeled scope names to account for additional inner match\n * groups that otherwise would break everything.\n *\n * Lets say we 3 match scopes:\n *\n * { 1 => ..., 2 => ..., 3 => ... }\n *\n * So what we need is a clean match like this:\n *\n * (a)(b)(c) => [ \"a\", \"b\", \"c\" ]\n *\n * But this falls apart with inner match groups:\n *\n * (a)(((b)))(c) => [\"a\", \"b\", \"b\", \"b\", \"c\" ]\n *\n * Our scopes are now \"out of alignment\" and we're repeating `b` 3 times.\n * What needs to happen is the numbers are remapped:\n *\n * { 1 => ..., 2 => ..., 5 => ... }\n *\n * We also need to know that the ONLY groups that should be output\n * are 1, 2, and 5. This function handles this behavior.\n *\n * @param {CompiledMode} mode\n * @param {Array} regexes\n * @param {{key: \"beginScope\"|\"endScope\"}} opts\n */\nfunction remapScopeNames(mode, regexes, { key }) {\n let offset = 0;\n const scopeNames = mode[key];\n /** @type Record */\n const emit = {};\n /** @type Record */\n const positions = {};\n\n for (let i = 1; i <= regexes.length; i++) {\n positions[i + offset] = scopeNames[i];\n emit[i + offset] = true;\n offset += countMatchGroups(regexes[i - 1]);\n }\n // we use _emit to keep track of which match groups are \"top-level\" to avoid double\n // output from inside match groups\n mode[key] = positions;\n mode[key]._emit = emit;\n mode[key]._multi = true;\n}\n\n/**\n * @param {CompiledMode} mode\n */\nfunction beginMultiClass(mode) {\n if (!Array.isArray(mode.begin)) return;\n\n if (mode.skip || mode.excludeBegin || mode.returnBegin) {\n error(\"skip, excludeBegin, returnBegin not compatible with beginScope: {}\");\n throw MultiClassError;\n }\n\n if (typeof mode.beginScope !== \"object\" || mode.beginScope === null) {\n error(\"beginScope must be object\");\n throw MultiClassError;\n }\n\n remapScopeNames(mode, mode.begin, { key: \"beginScope\" });\n mode.begin = _rewriteBackreferences(mode.begin, { joinWith: \"\" });\n}\n\n/**\n * @param {CompiledMode} mode\n */\nfunction endMultiClass(mode) {\n if (!Array.isArray(mode.end)) return;\n\n if (mode.skip || mode.excludeEnd || mode.returnEnd) {\n error(\"skip, excludeEnd, returnEnd not compatible with endScope: {}\");\n throw MultiClassError;\n }\n\n if (typeof mode.endScope !== \"object\" || mode.endScope === null) {\n error(\"endScope must be object\");\n throw MultiClassError;\n }\n\n remapScopeNames(mode, mode.end, { key: \"endScope\" });\n mode.end = _rewriteBackreferences(mode.end, { joinWith: \"\" });\n}\n\n/**\n * this exists only to allow `scope: {}` to be used beside `match:`\n * Otherwise `beginScope` would necessary and that would look weird\n\n {\n match: [ /def/, /\\w+/ ]\n scope: { 1: \"keyword\" , 2: \"title\" }\n }\n\n * @param {CompiledMode} mode\n */\nfunction scopeSugar(mode) {\n if (mode.scope && typeof mode.scope === \"object\" && mode.scope !== null) {\n mode.beginScope = mode.scope;\n delete mode.scope;\n }\n}\n\n/**\n * @param {CompiledMode} mode\n */\nfunction MultiClass(mode) {\n scopeSugar(mode);\n\n if (typeof mode.beginScope === \"string\") {\n mode.beginScope = { _wrap: mode.beginScope };\n }\n if (typeof mode.endScope === \"string\") {\n mode.endScope = { _wrap: mode.endScope };\n }\n\n beginMultiClass(mode);\n endMultiClass(mode);\n}\n\n/**\n@typedef {import('highlight.js').Mode} Mode\n@typedef {import('highlight.js').CompiledMode} CompiledMode\n@typedef {import('highlight.js').Language} Language\n@typedef {import('highlight.js').HLJSPlugin} HLJSPlugin\n@typedef {import('highlight.js').CompiledLanguage} CompiledLanguage\n*/\n\n// compilation\n\n/**\n * Compiles a language definition result\n *\n * Given the raw result of a language definition (Language), compiles this so\n * that it is ready for highlighting code.\n * @param {Language} language\n * @returns {CompiledLanguage}\n */\nfunction compileLanguage(language) {\n /**\n * Builds a regex with the case sensitivity of the current language\n *\n * @param {RegExp | string} value\n * @param {boolean} [global]\n */\n function langRe(value, global) {\n return new RegExp(\n source(value),\n 'm'\n + (language.case_insensitive ? 'i' : '')\n + (language.unicodeRegex ? 'u' : '')\n + (global ? 'g' : '')\n );\n }\n\n /**\n Stores multiple regular expressions and allows you to quickly search for\n them all in a string simultaneously - returning the first match. It does\n this by creating a huge (a|b|c) regex - each individual item wrapped with ()\n and joined by `|` - using match groups to track position. When a match is\n found checking which position in the array has content allows us to figure\n out which of the original regexes / match groups triggered the match.\n\n The match object itself (the result of `Regex.exec`) is returned but also\n enhanced by merging in any meta-data that was registered with the regex.\n This is how we keep track of which mode matched, and what type of rule\n (`illegal`, `begin`, end, etc).\n */\n class MultiRegex {\n constructor() {\n this.matchIndexes = {};\n // @ts-ignore\n this.regexes = [];\n this.matchAt = 1;\n this.position = 0;\n }\n\n // @ts-ignore\n addRule(re, opts) {\n opts.position = this.position++;\n // @ts-ignore\n this.matchIndexes[this.matchAt] = opts;\n this.regexes.push([opts, re]);\n this.matchAt += countMatchGroups(re) + 1;\n }\n\n compile() {\n if (this.regexes.length === 0) {\n // avoids the need to check length every time exec is called\n // @ts-ignore\n this.exec = () => null;\n }\n const terminators = this.regexes.map(el => el[1]);\n this.matcherRe = langRe(_rewriteBackreferences(terminators, { joinWith: '|' }), true);\n this.lastIndex = 0;\n }\n\n /** @param {string} s */\n exec(s) {\n this.matcherRe.lastIndex = this.lastIndex;\n const match = this.matcherRe.exec(s);\n if (!match) { return null; }\n\n // eslint-disable-next-line no-undefined\n const i = match.findIndex((el, i) => i > 0 && el !== undefined);\n // @ts-ignore\n const matchData = this.matchIndexes[i];\n // trim off any earlier non-relevant match groups (ie, the other regex\n // match groups that make up the multi-matcher)\n match.splice(0, i);\n\n return Object.assign(match, matchData);\n }\n }\n\n /*\n Created to solve the key deficiently with MultiRegex - there is no way to\n test for multiple matches at a single location. Why would we need to do\n that? In the future a more dynamic engine will allow certain matches to be\n ignored. An example: if we matched say the 3rd regex in a large group but\n decided to ignore it - we'd need to started testing again at the 4th\n regex... but MultiRegex itself gives us no real way to do that.\n\n So what this class creates MultiRegexs on the fly for whatever search\n position they are needed.\n\n NOTE: These additional MultiRegex objects are created dynamically. For most\n grammars most of the time we will never actually need anything more than the\n first MultiRegex - so this shouldn't have too much overhead.\n\n Say this is our search group, and we match regex3, but wish to ignore it.\n\n regex1 | regex2 | regex3 | regex4 | regex5 ' ie, startAt = 0\n\n What we need is a new MultiRegex that only includes the remaining\n possibilities:\n\n regex4 | regex5 ' ie, startAt = 3\n\n This class wraps all that complexity up in a simple API... `startAt` decides\n where in the array of expressions to start doing the matching. It\n auto-increments, so if a match is found at position 2, then startAt will be\n set to 3. If the end is reached startAt will return to 0.\n\n MOST of the time the parser will be setting startAt manually to 0.\n */\n class ResumableMultiRegex {\n constructor() {\n // @ts-ignore\n this.rules = [];\n // @ts-ignore\n this.multiRegexes = [];\n this.count = 0;\n\n this.lastIndex = 0;\n this.regexIndex = 0;\n }\n\n // @ts-ignore\n getMatcher(index) {\n if (this.multiRegexes[index]) return this.multiRegexes[index];\n\n const matcher = new MultiRegex();\n this.rules.slice(index).forEach(([re, opts]) => matcher.addRule(re, opts));\n matcher.compile();\n this.multiRegexes[index] = matcher;\n return matcher;\n }\n\n resumingScanAtSamePosition() {\n return this.regexIndex !== 0;\n }\n\n considerAll() {\n this.regexIndex = 0;\n }\n\n // @ts-ignore\n addRule(re, opts) {\n this.rules.push([re, opts]);\n if (opts.type === \"begin\") this.count++;\n }\n\n /** @param {string} s */\n exec(s) {\n const m = this.getMatcher(this.regexIndex);\n m.lastIndex = this.lastIndex;\n let result = m.exec(s);\n\n // The following is because we have no easy way to say \"resume scanning at the\n // existing position but also skip the current rule ONLY\". What happens is\n // all prior rules are also skipped which can result in matching the wrong\n // thing. Example of matching \"booger\":\n\n // our matcher is [string, \"booger\", number]\n //\n // ....booger....\n\n // if \"booger\" is ignored then we'd really need a regex to scan from the\n // SAME position for only: [string, number] but ignoring \"booger\" (if it\n // was the first match), a simple resume would scan ahead who knows how\n // far looking only for \"number\", ignoring potential string matches (or\n // future \"booger\" matches that might be valid.)\n\n // So what we do: We execute two matchers, one resuming at the same\n // position, but the second full matcher starting at the position after:\n\n // /--- resume first regex match here (for [number])\n // |/---- full match here for [string, \"booger\", number]\n // vv\n // ....booger....\n\n // Which ever results in a match first is then used. So this 3-4 step\n // process essentially allows us to say \"match at this position, excluding\n // a prior rule that was ignored\".\n //\n // 1. Match \"booger\" first, ignore. Also proves that [string] does non match.\n // 2. Resume matching for [number]\n // 3. Match at index + 1 for [string, \"booger\", number]\n // 4. If #2 and #3 result in matches, which came first?\n if (this.resumingScanAtSamePosition()) {\n if (result && result.index === this.lastIndex) ; else { // use the second matcher result\n const m2 = this.getMatcher(0);\n m2.lastIndex = this.lastIndex + 1;\n result = m2.exec(s);\n }\n }\n\n if (result) {\n this.regexIndex += result.position + 1;\n if (this.regexIndex === this.count) {\n // wrap-around to considering all matches again\n this.considerAll();\n }\n }\n\n return result;\n }\n }\n\n /**\n * Given a mode, builds a huge ResumableMultiRegex that can be used to walk\n * the content and find matches.\n *\n * @param {CompiledMode} mode\n * @returns {ResumableMultiRegex}\n */\n function buildModeRegex(mode) {\n const mm = new ResumableMultiRegex();\n\n mode.contains.forEach(term => mm.addRule(term.begin, { rule: term, type: \"begin\" }));\n\n if (mode.terminatorEnd) {\n mm.addRule(mode.terminatorEnd, { type: \"end\" });\n }\n if (mode.illegal) {\n mm.addRule(mode.illegal, { type: \"illegal\" });\n }\n\n return mm;\n }\n\n /** skip vs abort vs ignore\n *\n * @skip - The mode is still entered and exited normally (and contains rules apply),\n * but all content is held and added to the parent buffer rather than being\n * output when the mode ends. Mostly used with `sublanguage` to build up\n * a single large buffer than can be parsed by sublanguage.\n *\n * - The mode begin ands ends normally.\n * - Content matched is added to the parent mode buffer.\n * - The parser cursor is moved forward normally.\n *\n * @abort - A hack placeholder until we have ignore. Aborts the mode (as if it\n * never matched) but DOES NOT continue to match subsequent `contains`\n * modes. Abort is bad/suboptimal because it can result in modes\n * farther down not getting applied because an earlier rule eats the\n * content but then aborts.\n *\n * - The mode does not begin.\n * - Content matched by `begin` is added to the mode buffer.\n * - The parser cursor is moved forward accordingly.\n *\n * @ignore - Ignores the mode (as if it never matched) and continues to match any\n * subsequent `contains` modes. Ignore isn't technically possible with\n * the current parser implementation.\n *\n * - The mode does not begin.\n * - Content matched by `begin` is ignored.\n * - The parser cursor is not moved forward.\n */\n\n /**\n * Compiles an individual mode\n *\n * This can raise an error if the mode contains certain detectable known logic\n * issues.\n * @param {Mode} mode\n * @param {CompiledMode | null} [parent]\n * @returns {CompiledMode | never}\n */\n function compileMode(mode, parent) {\n const cmode = /** @type CompiledMode */ (mode);\n if (mode.isCompiled) return cmode;\n\n [\n scopeClassName,\n // do this early so compiler extensions generally don't have to worry about\n // the distinction between match/begin\n compileMatch,\n MultiClass,\n beforeMatchExt\n ].forEach(ext => ext(mode, parent));\n\n language.compilerExtensions.forEach(ext => ext(mode, parent));\n\n // __beforeBegin is considered private API, internal use only\n mode.__beforeBegin = null;\n\n [\n beginKeywords,\n // do this later so compiler extensions that come earlier have access to the\n // raw array if they wanted to perhaps manipulate it, etc.\n compileIllegal,\n // default to 1 relevance if not specified\n compileRelevance\n ].forEach(ext => ext(mode, parent));\n\n mode.isCompiled = true;\n\n let keywordPattern = null;\n if (typeof mode.keywords === \"object\" && mode.keywords.$pattern) {\n // we need a copy because keywords might be compiled multiple times\n // so we can't go deleting $pattern from the original on the first\n // pass\n mode.keywords = Object.assign({}, mode.keywords);\n keywordPattern = mode.keywords.$pattern;\n delete mode.keywords.$pattern;\n }\n keywordPattern = keywordPattern || /\\w+/;\n\n if (mode.keywords) {\n mode.keywords = compileKeywords(mode.keywords, language.case_insensitive);\n }\n\n cmode.keywordPatternRe = langRe(keywordPattern, true);\n\n if (parent) {\n if (!mode.begin) mode.begin = /\\B|\\b/;\n cmode.beginRe = langRe(cmode.begin);\n if (!mode.end && !mode.endsWithParent) mode.end = /\\B|\\b/;\n if (mode.end) cmode.endRe = langRe(cmode.end);\n cmode.terminatorEnd = source(cmode.end) || '';\n if (mode.endsWithParent && parent.terminatorEnd) {\n cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd;\n }\n }\n if (mode.illegal) cmode.illegalRe = langRe(/** @type {RegExp | string} */ (mode.illegal));\n if (!mode.contains) mode.contains = [];\n\n mode.contains = [].concat(...mode.contains.map(function(c) {\n return expandOrCloneMode(c === 'self' ? mode : c);\n }));\n mode.contains.forEach(function(c) { compileMode(/** @type Mode */ (c), cmode); });\n\n if (mode.starts) {\n compileMode(mode.starts, parent);\n }\n\n cmode.matcher = buildModeRegex(cmode);\n return cmode;\n }\n\n if (!language.compilerExtensions) language.compilerExtensions = [];\n\n // self is not valid at the top-level\n if (language.contains && language.contains.includes('self')) {\n throw new Error(\"ERR: contains `self` is not supported at the top-level of a language. See documentation.\");\n }\n\n // we need a null object, which inherit will guarantee\n language.classNameAliases = inherit$1(language.classNameAliases || {});\n\n return compileMode(/** @type Mode */ (language));\n}\n\n/**\n * Determines if a mode has a dependency on it's parent or not\n *\n * If a mode does have a parent dependency then often we need to clone it if\n * it's used in multiple places so that each copy points to the correct parent,\n * where-as modes without a parent can often safely be re-used at the bottom of\n * a mode chain.\n *\n * @param {Mode | null} mode\n * @returns {boolean} - is there a dependency on the parent?\n * */\nfunction dependencyOnParent(mode) {\n if (!mode) return false;\n\n return mode.endsWithParent || dependencyOnParent(mode.starts);\n}\n\n/**\n * Expands a mode or clones it if necessary\n *\n * This is necessary for modes with parental dependenceis (see notes on\n * `dependencyOnParent`) and for nodes that have `variants` - which must then be\n * exploded into their own individual modes at compile time.\n *\n * @param {Mode} mode\n * @returns {Mode | Mode[]}\n * */\nfunction expandOrCloneMode(mode) {\n if (mode.variants && !mode.cachedVariants) {\n mode.cachedVariants = mode.variants.map(function(variant) {\n return inherit$1(mode, { variants: null }, variant);\n });\n }\n\n // EXPAND\n // if we have variants then essentially \"replace\" the mode with the variants\n // this happens in compileMode, where this function is called from\n if (mode.cachedVariants) {\n return mode.cachedVariants;\n }\n\n // CLONE\n // if we have dependencies on parents then we need a unique\n // instance of ourselves, so we can be reused with many\n // different parents without issue\n if (dependencyOnParent(mode)) {\n return inherit$1(mode, { starts: mode.starts ? inherit$1(mode.starts) : null });\n }\n\n if (Object.isFrozen(mode)) {\n return inherit$1(mode);\n }\n\n // no special dependency issues, just return ourselves\n return mode;\n}\n\nvar version = \"11.11.1\";\n\nclass HTMLInjectionError extends Error {\n constructor(reason, html) {\n super(reason);\n this.name = \"HTMLInjectionError\";\n this.html = html;\n }\n}\n\n/*\nSyntax highlighting with language autodetection.\nhttps://highlightjs.org/\n*/\n\n\n\n/**\n@typedef {import('highlight.js').Mode} Mode\n@typedef {import('highlight.js').CompiledMode} CompiledMode\n@typedef {import('highlight.js').CompiledScope} CompiledScope\n@typedef {import('highlight.js').Language} Language\n@typedef {import('highlight.js').HLJSApi} HLJSApi\n@typedef {import('highlight.js').HLJSPlugin} HLJSPlugin\n@typedef {import('highlight.js').PluginEvent} PluginEvent\n@typedef {import('highlight.js').HLJSOptions} HLJSOptions\n@typedef {import('highlight.js').LanguageFn} LanguageFn\n@typedef {import('highlight.js').HighlightedHTMLElement} HighlightedHTMLElement\n@typedef {import('highlight.js').BeforeHighlightContext} BeforeHighlightContext\n@typedef {import('highlight.js/private').MatchType} MatchType\n@typedef {import('highlight.js/private').KeywordData} KeywordData\n@typedef {import('highlight.js/private').EnhancedMatch} EnhancedMatch\n@typedef {import('highlight.js/private').AnnotatedError} AnnotatedError\n@typedef {import('highlight.js').AutoHighlightResult} AutoHighlightResult\n@typedef {import('highlight.js').HighlightOptions} HighlightOptions\n@typedef {import('highlight.js').HighlightResult} HighlightResult\n*/\n\n\nconst escape = escapeHTML;\nconst inherit = inherit$1;\nconst NO_MATCH = Symbol(\"nomatch\");\nconst MAX_KEYWORD_HITS = 7;\n\n/**\n * @param {any} hljs - object that is extended (legacy)\n * @returns {HLJSApi}\n */\nconst HLJS = function(hljs) {\n // Global internal variables used within the highlight.js library.\n /** @type {Record} */\n const languages = Object.create(null);\n /** @type {Record} */\n const aliases = Object.create(null);\n /** @type {HLJSPlugin[]} */\n const plugins = [];\n\n // safe/production mode - swallows more errors, tries to keep running\n // even if a single syntax or parse hits a fatal error\n let SAFE_MODE = true;\n const LANGUAGE_NOT_FOUND = \"Could not find the language '{}', did you forget to load/include a language module?\";\n /** @type {Language} */\n const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] };\n\n // Global options used when within external APIs. This is modified when\n // calling the `hljs.configure` function.\n /** @type HLJSOptions */\n let options = {\n ignoreUnescapedHTML: false,\n throwUnescapedHTML: false,\n noHighlightRe: /^(no-?highlight)$/i,\n languageDetectRe: /\\blang(?:uage)?-([\\w-]+)\\b/i,\n classPrefix: 'hljs-',\n cssSelector: 'pre code',\n languages: null,\n // beta configuration options, subject to change, welcome to discuss\n // https://github.com/highlightjs/highlight.js/issues/1086\n __emitter: TokenTreeEmitter\n };\n\n /* Utility functions */\n\n /**\n * Tests a language name to see if highlighting should be skipped\n * @param {string} languageName\n */\n function shouldNotHighlight(languageName) {\n return options.noHighlightRe.test(languageName);\n }\n\n /**\n * @param {HighlightedHTMLElement} block - the HTML element to determine language for\n */\n function blockLanguage(block) {\n let classes = block.className + ' ';\n\n classes += block.parentNode ? block.parentNode.className : '';\n\n // language-* takes precedence over non-prefixed class names.\n const match = options.languageDetectRe.exec(classes);\n if (match) {\n const language = getLanguage(match[1]);\n if (!language) {\n warn(LANGUAGE_NOT_FOUND.replace(\"{}\", match[1]));\n warn(\"Falling back to no-highlight mode for this block.\", block);\n }\n return language ? match[1] : 'no-highlight';\n }\n\n return classes\n .split(/\\s+/)\n .find((_class) => shouldNotHighlight(_class) || getLanguage(_class));\n }\n\n /**\n * Core highlighting function.\n *\n * OLD API\n * highlight(lang, code, ignoreIllegals, continuation)\n *\n * NEW API\n * highlight(code, {lang, ignoreIllegals})\n *\n * @param {string} codeOrLanguageName - the language to use for highlighting\n * @param {string | HighlightOptions} optionsOrCode - the code to highlight\n * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail\n *\n * @returns {HighlightResult} Result - an object that represents the result\n * @property {string} language - the language name\n * @property {number} relevance - the relevance score\n * @property {string} value - the highlighted HTML code\n * @property {string} code - the original raw code\n * @property {CompiledMode} top - top of the current mode stack\n * @property {boolean} illegal - indicates whether any illegal matches were found\n */\n function highlight(codeOrLanguageName, optionsOrCode, ignoreIllegals) {\n let code = \"\";\n let languageName = \"\";\n if (typeof optionsOrCode === \"object\") {\n code = codeOrLanguageName;\n ignoreIllegals = optionsOrCode.ignoreIllegals;\n languageName = optionsOrCode.language;\n } else {\n // old API\n deprecated(\"10.7.0\", \"highlight(lang, code, ...args) has been deprecated.\");\n deprecated(\"10.7.0\", \"Please use highlight(code, options) instead.\\nhttps://github.com/highlightjs/highlight.js/issues/2277\");\n languageName = codeOrLanguageName;\n code = optionsOrCode;\n }\n\n // https://github.com/highlightjs/highlight.js/issues/3149\n // eslint-disable-next-line no-undefined\n if (ignoreIllegals === undefined) { ignoreIllegals = true; }\n\n /** @type {BeforeHighlightContext} */\n const context = {\n code,\n language: languageName\n };\n // the plugin can change the desired language or the code to be highlighted\n // just be changing the object it was passed\n fire(\"before:highlight\", context);\n\n // a before plugin can usurp the result completely by providing it's own\n // in which case we don't even need to call highlight\n const result = context.result\n ? context.result\n : _highlight(context.language, context.code, ignoreIllegals);\n\n result.code = context.code;\n // the plugin can change anything in result to suite it\n fire(\"after:highlight\", result);\n\n return result;\n }\n\n /**\n * private highlight that's used internally and does not fire callbacks\n *\n * @param {string} languageName - the language to use for highlighting\n * @param {string} codeToHighlight - the code to highlight\n * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail\n * @param {CompiledMode?} [continuation] - current continuation mode, if any\n * @returns {HighlightResult} - result of the highlight operation\n */\n function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) {\n const keywordHits = Object.create(null);\n\n /**\n * Return keyword data if a match is a keyword\n * @param {CompiledMode} mode - current mode\n * @param {string} matchText - the textual match\n * @returns {KeywordData | false}\n */\n function keywordData(mode, matchText) {\n return mode.keywords[matchText];\n }\n\n function processKeywords() {\n if (!top.keywords) {\n emitter.addText(modeBuffer);\n return;\n }\n\n let lastIndex = 0;\n top.keywordPatternRe.lastIndex = 0;\n let match = top.keywordPatternRe.exec(modeBuffer);\n let buf = \"\";\n\n while (match) {\n buf += modeBuffer.substring(lastIndex, match.index);\n const word = language.case_insensitive ? match[0].toLowerCase() : match[0];\n const data = keywordData(top, word);\n if (data) {\n const [kind, keywordRelevance] = data;\n emitter.addText(buf);\n buf = \"\";\n\n keywordHits[word] = (keywordHits[word] || 0) + 1;\n if (keywordHits[word] <= MAX_KEYWORD_HITS) relevance += keywordRelevance;\n if (kind.startsWith(\"_\")) {\n // _ implied for relevance only, do not highlight\n // by applying a class name\n buf += match[0];\n } else {\n const cssClass = language.classNameAliases[kind] || kind;\n emitKeyword(match[0], cssClass);\n }\n } else {\n buf += match[0];\n }\n lastIndex = top.keywordPatternRe.lastIndex;\n match = top.keywordPatternRe.exec(modeBuffer);\n }\n buf += modeBuffer.substring(lastIndex);\n emitter.addText(buf);\n }\n\n function processSubLanguage() {\n if (modeBuffer === \"\") return;\n /** @type HighlightResult */\n let result = null;\n\n if (typeof top.subLanguage === 'string') {\n if (!languages[top.subLanguage]) {\n emitter.addText(modeBuffer);\n return;\n }\n result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]);\n continuations[top.subLanguage] = /** @type {CompiledMode} */ (result._top);\n } else {\n result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null);\n }\n\n // Counting embedded language score towards the host language may be disabled\n // with zeroing the containing mode relevance. Use case in point is Markdown that\n // allows XML everywhere and makes every XML snippet to have a much larger Markdown\n // score.\n if (top.relevance > 0) {\n relevance += result.relevance;\n }\n emitter.__addSublanguage(result._emitter, result.language);\n }\n\n function processBuffer() {\n if (top.subLanguage != null) {\n processSubLanguage();\n } else {\n processKeywords();\n }\n modeBuffer = '';\n }\n\n /**\n * @param {string} text\n * @param {string} scope\n */\n function emitKeyword(keyword, scope) {\n if (keyword === \"\") return;\n\n emitter.startScope(scope);\n emitter.addText(keyword);\n emitter.endScope();\n }\n\n /**\n * @param {CompiledScope} scope\n * @param {RegExpMatchArray} match\n */\n function emitMultiClass(scope, match) {\n let i = 1;\n const max = match.length - 1;\n while (i <= max) {\n if (!scope._emit[i]) { i++; continue; }\n const klass = language.classNameAliases[scope[i]] || scope[i];\n const text = match[i];\n if (klass) {\n emitKeyword(text, klass);\n } else {\n modeBuffer = text;\n processKeywords();\n modeBuffer = \"\";\n }\n i++;\n }\n }\n\n /**\n * @param {CompiledMode} mode - new mode to start\n * @param {RegExpMatchArray} match\n */\n function startNewMode(mode, match) {\n if (mode.scope && typeof mode.scope === \"string\") {\n emitter.openNode(language.classNameAliases[mode.scope] || mode.scope);\n }\n if (mode.beginScope) {\n // beginScope just wraps the begin match itself in a scope\n if (mode.beginScope._wrap) {\n emitKeyword(modeBuffer, language.classNameAliases[mode.beginScope._wrap] || mode.beginScope._wrap);\n modeBuffer = \"\";\n } else if (mode.beginScope._multi) {\n // at this point modeBuffer should just be the match\n emitMultiClass(mode.beginScope, match);\n modeBuffer = \"\";\n }\n }\n\n top = Object.create(mode, { parent: { value: top } });\n return top;\n }\n\n /**\n * @param {CompiledMode } mode - the mode to potentially end\n * @param {RegExpMatchArray} match - the latest match\n * @param {string} matchPlusRemainder - match plus remainder of content\n * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode\n */\n function endOfMode(mode, match, matchPlusRemainder) {\n let matched = startsWith(mode.endRe, matchPlusRemainder);\n\n if (matched) {\n if (mode[\"on:end\"]) {\n const resp = new Response(mode);\n mode[\"on:end\"](match, resp);\n if (resp.isMatchIgnored) matched = false;\n }\n\n if (matched) {\n while (mode.endsParent && mode.parent) {\n mode = mode.parent;\n }\n return mode;\n }\n }\n // even if on:end fires an `ignore` it's still possible\n // that we might trigger the end node because of a parent mode\n if (mode.endsWithParent) {\n return endOfMode(mode.parent, match, matchPlusRemainder);\n }\n }\n\n /**\n * Handle matching but then ignoring a sequence of text\n *\n * @param {string} lexeme - string containing full match text\n */\n function doIgnore(lexeme) {\n if (top.matcher.regexIndex === 0) {\n // no more regexes to potentially match here, so we move the cursor forward one\n // space\n modeBuffer += lexeme[0];\n return 1;\n } else {\n // no need to move the cursor, we still have additional regexes to try and\n // match at this very spot\n resumeScanAtSamePosition = true;\n return 0;\n }\n }\n\n /**\n * Handle the start of a new potential mode match\n *\n * @param {EnhancedMatch} match - the current match\n * @returns {number} how far to advance the parse cursor\n */\n function doBeginMatch(match) {\n const lexeme = match[0];\n const newMode = match.rule;\n\n const resp = new Response(newMode);\n // first internal before callbacks, then the public ones\n const beforeCallbacks = [newMode.__beforeBegin, newMode[\"on:begin\"]];\n for (const cb of beforeCallbacks) {\n if (!cb) continue;\n cb(match, resp);\n if (resp.isMatchIgnored) return doIgnore(lexeme);\n }\n\n if (newMode.skip) {\n modeBuffer += lexeme;\n } else {\n if (newMode.excludeBegin) {\n modeBuffer += lexeme;\n }\n processBuffer();\n if (!newMode.returnBegin && !newMode.excludeBegin) {\n modeBuffer = lexeme;\n }\n }\n startNewMode(newMode, match);\n return newMode.returnBegin ? 0 : lexeme.length;\n }\n\n /**\n * Handle the potential end of mode\n *\n * @param {RegExpMatchArray} match - the current match\n */\n function doEndMatch(match) {\n const lexeme = match[0];\n const matchPlusRemainder = codeToHighlight.substring(match.index);\n\n const endMode = endOfMode(top, match, matchPlusRemainder);\n if (!endMode) { return NO_MATCH; }\n\n const origin = top;\n if (top.endScope && top.endScope._wrap) {\n processBuffer();\n emitKeyword(lexeme, top.endScope._wrap);\n } else if (top.endScope && top.endScope._multi) {\n processBuffer();\n emitMultiClass(top.endScope, match);\n } else if (origin.skip) {\n modeBuffer += lexeme;\n } else {\n if (!(origin.returnEnd || origin.excludeEnd)) {\n modeBuffer += lexeme;\n }\n processBuffer();\n if (origin.excludeEnd) {\n modeBuffer = lexeme;\n }\n }\n do {\n if (top.scope) {\n emitter.closeNode();\n }\n if (!top.skip && !top.subLanguage) {\n relevance += top.relevance;\n }\n top = top.parent;\n } while (top !== endMode.parent);\n if (endMode.starts) {\n startNewMode(endMode.starts, match);\n }\n return origin.returnEnd ? 0 : lexeme.length;\n }\n\n function processContinuations() {\n const list = [];\n for (let current = top; current !== language; current = current.parent) {\n if (current.scope) {\n list.unshift(current.scope);\n }\n }\n list.forEach(item => emitter.openNode(item));\n }\n\n /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */\n let lastMatch = {};\n\n /**\n * Process an individual match\n *\n * @param {string} textBeforeMatch - text preceding the match (since the last match)\n * @param {EnhancedMatch} [match] - the match itself\n */\n function processLexeme(textBeforeMatch, match) {\n const lexeme = match && match[0];\n\n // add non-matched text to the current mode buffer\n modeBuffer += textBeforeMatch;\n\n if (lexeme == null) {\n processBuffer();\n return 0;\n }\n\n // we've found a 0 width match and we're stuck, so we need to advance\n // this happens when we have badly behaved rules that have optional matchers to the degree that\n // sometimes they can end up matching nothing at all\n // Ref: https://github.com/highlightjs/highlight.js/issues/2140\n if (lastMatch.type === \"begin\" && match.type === \"end\" && lastMatch.index === match.index && lexeme === \"\") {\n // spit the \"skipped\" character that our regex choked on back into the output sequence\n modeBuffer += codeToHighlight.slice(match.index, match.index + 1);\n if (!SAFE_MODE) {\n /** @type {AnnotatedError} */\n const err = new Error(`0 width match regex (${languageName})`);\n err.languageName = languageName;\n err.badRule = lastMatch.rule;\n throw err;\n }\n return 1;\n }\n lastMatch = match;\n\n if (match.type === \"begin\") {\n return doBeginMatch(match);\n } else if (match.type === \"illegal\" && !ignoreIllegals) {\n // illegal match, we do not continue processing\n /** @type {AnnotatedError} */\n const err = new Error('Illegal lexeme \"' + lexeme + '\" for mode \"' + (top.scope || '') + '\"');\n err.mode = top;\n throw err;\n } else if (match.type === \"end\") {\n const processed = doEndMatch(match);\n if (processed !== NO_MATCH) {\n return processed;\n }\n }\n\n // edge case for when illegal matches $ (end of line) which is technically\n // a 0 width match but not a begin/end match so it's not caught by the\n // first handler (when ignoreIllegals is true)\n if (match.type === \"illegal\" && lexeme === \"\") {\n // advance so we aren't stuck in an infinite loop\n modeBuffer += \"\\n\";\n return 1;\n }\n\n // infinite loops are BAD, this is a last ditch catch all. if we have a\n // decent number of iterations yet our index (cursor position in our\n // parsing) still 3x behind our index then something is very wrong\n // so we bail\n if (iterations > 100000 && iterations > match.index * 3) {\n const err = new Error('potential infinite loop, way more iterations than matches');\n throw err;\n }\n\n /*\n Why might be find ourselves here? An potential end match that was\n triggered but could not be completed. IE, `doEndMatch` returned NO_MATCH.\n (this could be because a callback requests the match be ignored, etc)\n\n This causes no real harm other than stopping a few times too many.\n */\n\n modeBuffer += lexeme;\n return lexeme.length;\n }\n\n const language = getLanguage(languageName);\n if (!language) {\n error(LANGUAGE_NOT_FOUND.replace(\"{}\", languageName));\n throw new Error('Unknown language: \"' + languageName + '\"');\n }\n\n const md = compileLanguage(language);\n let result = '';\n /** @type {CompiledMode} */\n let top = continuation || md;\n /** @type Record */\n const continuations = {}; // keep continuations for sub-languages\n const emitter = new options.__emitter(options);\n processContinuations();\n let modeBuffer = '';\n let relevance = 0;\n let index = 0;\n let iterations = 0;\n let resumeScanAtSamePosition = false;\n\n try {\n if (!language.__emitTokens) {\n top.matcher.considerAll();\n\n for (;;) {\n iterations++;\n if (resumeScanAtSamePosition) {\n // only regexes not matched previously will now be\n // considered for a potential match\n resumeScanAtSamePosition = false;\n } else {\n top.matcher.considerAll();\n }\n top.matcher.lastIndex = index;\n\n const match = top.matcher.exec(codeToHighlight);\n // console.log(\"match\", match[0], match.rule && match.rule.begin)\n\n if (!match) break;\n\n const beforeMatch = codeToHighlight.substring(index, match.index);\n const processedCount = processLexeme(beforeMatch, match);\n index = match.index + processedCount;\n }\n processLexeme(codeToHighlight.substring(index));\n } else {\n language.__emitTokens(codeToHighlight, emitter);\n }\n\n emitter.finalize();\n result = emitter.toHTML();\n\n return {\n language: languageName,\n value: result,\n relevance,\n illegal: false,\n _emitter: emitter,\n _top: top\n };\n } catch (err) {\n if (err.message && err.message.includes('Illegal')) {\n return {\n language: languageName,\n value: escape(codeToHighlight),\n illegal: true,\n relevance: 0,\n _illegalBy: {\n message: err.message,\n index,\n context: codeToHighlight.slice(index - 100, index + 100),\n mode: err.mode,\n resultSoFar: result\n },\n _emitter: emitter\n };\n } else if (SAFE_MODE) {\n return {\n language: languageName,\n value: escape(codeToHighlight),\n illegal: false,\n relevance: 0,\n errorRaised: err,\n _emitter: emitter,\n _top: top\n };\n } else {\n throw err;\n }\n }\n }\n\n /**\n * returns a valid highlight result, without actually doing any actual work,\n * auto highlight starts with this and it's possible for small snippets that\n * auto-detection may not find a better match\n * @param {string} code\n * @returns {HighlightResult}\n */\n function justTextHighlightResult(code) {\n const result = {\n value: escape(code),\n illegal: false,\n relevance: 0,\n _top: PLAINTEXT_LANGUAGE,\n _emitter: new options.__emitter(options)\n };\n result._emitter.addText(code);\n return result;\n }\n\n /**\n Highlighting with language detection. Accepts a string with the code to\n highlight. Returns an object with the following properties:\n\n - language (detected language)\n - relevance (int)\n - value (an HTML string with highlighting markup)\n - secondBest (object with the same structure for second-best heuristically\n detected language, may be absent)\n\n @param {string} code\n @param {Array} [languageSubset]\n @returns {AutoHighlightResult}\n */\n function highlightAuto(code, languageSubset) {\n languageSubset = languageSubset || options.languages || Object.keys(languages);\n const plaintext = justTextHighlightResult(code);\n\n const results = languageSubset.filter(getLanguage).filter(autoDetection).map(name =>\n _highlight(name, code, false)\n );\n results.unshift(plaintext); // plaintext is always an option\n\n const sorted = results.sort((a, b) => {\n // sort base on relevance\n if (a.relevance !== b.relevance) return b.relevance - a.relevance;\n\n // always award the tie to the base language\n // ie if C++ and Arduino are tied, it's more likely to be C++\n if (a.language && b.language) {\n if (getLanguage(a.language).supersetOf === b.language) {\n return 1;\n } else if (getLanguage(b.language).supersetOf === a.language) {\n return -1;\n }\n }\n\n // otherwise say they are equal, which has the effect of sorting on\n // relevance while preserving the original ordering - which is how ties\n // have historically been settled, ie the language that comes first always\n // wins in the case of a tie\n return 0;\n });\n\n const [best, secondBest] = sorted;\n\n /** @type {AutoHighlightResult} */\n const result = best;\n result.secondBest = secondBest;\n\n return result;\n }\n\n /**\n * Builds new class name for block given the language name\n *\n * @param {HTMLElement} element\n * @param {string} [currentLang]\n * @param {string} [resultLang]\n */\n function updateClassName(element, currentLang, resultLang) {\n const language = (currentLang && aliases[currentLang]) || resultLang;\n\n element.classList.add(\"hljs\");\n element.classList.add(`language-${language}`);\n }\n\n /**\n * Applies highlighting to a DOM node containing code.\n *\n * @param {HighlightedHTMLElement} element - the HTML element to highlight\n */\n function highlightElement(element) {\n /** @type HTMLElement */\n let node = null;\n const language = blockLanguage(element);\n\n if (shouldNotHighlight(language)) return;\n\n fire(\"before:highlightElement\",\n { el: element, language });\n\n if (element.dataset.highlighted) {\n console.log(\"Element previously highlighted. To highlight again, first unset `dataset.highlighted`.\", element);\n return;\n }\n\n // we should be all text, no child nodes (unescaped HTML) - this is possibly\n // an HTML injection attack - it's likely too late if this is already in\n // production (the code has likely already done its damage by the time\n // we're seeing it)... but we yell loudly about this so that hopefully it's\n // more likely to be caught in development before making it to production\n if (element.children.length > 0) {\n if (!options.ignoreUnescapedHTML) {\n console.warn(\"One of your code blocks includes unescaped HTML. This is a potentially serious security risk.\");\n console.warn(\"https://github.com/highlightjs/highlight.js/wiki/security\");\n console.warn(\"The element with unescaped HTML:\");\n console.warn(element);\n }\n if (options.throwUnescapedHTML) {\n const err = new HTMLInjectionError(\n \"One of your code blocks includes unescaped HTML.\",\n element.innerHTML\n );\n throw err;\n }\n }\n\n node = element;\n const text = node.textContent;\n const result = language ? highlight(text, { language, ignoreIllegals: true }) : highlightAuto(text);\n\n element.innerHTML = result.value;\n element.dataset.highlighted = \"yes\";\n updateClassName(element, language, result.language);\n element.result = {\n language: result.language,\n // TODO: remove with version 11.0\n re: result.relevance,\n relevance: result.relevance\n };\n if (result.secondBest) {\n element.secondBest = {\n language: result.secondBest.language,\n relevance: result.secondBest.relevance\n };\n }\n\n fire(\"after:highlightElement\", { el: element, result, text });\n }\n\n /**\n * Updates highlight.js global options with the passed options\n *\n * @param {Partial} userOptions\n */\n function configure(userOptions) {\n options = inherit(options, userOptions);\n }\n\n // TODO: remove v12, deprecated\n const initHighlighting = () => {\n highlightAll();\n deprecated(\"10.6.0\", \"initHighlighting() deprecated. Use highlightAll() now.\");\n };\n\n // TODO: remove v12, deprecated\n function initHighlightingOnLoad() {\n highlightAll();\n deprecated(\"10.6.0\", \"initHighlightingOnLoad() deprecated. Use highlightAll() now.\");\n }\n\n let wantsHighlight = false;\n\n /**\n * auto-highlights all pre>code elements on the page\n */\n function highlightAll() {\n function boot() {\n // if a highlight was requested before DOM was loaded, do now\n highlightAll();\n }\n\n // if we are called too early in the loading process\n if (document.readyState === \"loading\") {\n // make sure the event listener is only added once\n if (!wantsHighlight) {\n window.addEventListener('DOMContentLoaded', boot, false);\n }\n wantsHighlight = true;\n return;\n }\n\n const blocks = document.querySelectorAll(options.cssSelector);\n blocks.forEach(highlightElement);\n }\n\n /**\n * Register a language grammar module\n *\n * @param {string} languageName\n * @param {LanguageFn} languageDefinition\n */\n function registerLanguage(languageName, languageDefinition) {\n let lang = null;\n try {\n lang = languageDefinition(hljs);\n } catch (error$1) {\n error(\"Language definition for '{}' could not be registered.\".replace(\"{}\", languageName));\n // hard or soft error\n if (!SAFE_MODE) { throw error$1; } else { error(error$1); }\n // languages that have serious errors are replaced with essentially a\n // \"plaintext\" stand-in so that the code blocks will still get normal\n // css classes applied to them - and one bad language won't break the\n // entire highlighter\n lang = PLAINTEXT_LANGUAGE;\n }\n // give it a temporary name if it doesn't have one in the meta-data\n if (!lang.name) lang.name = languageName;\n languages[languageName] = lang;\n lang.rawDefinition = languageDefinition.bind(null, hljs);\n\n if (lang.aliases) {\n registerAliases(lang.aliases, { languageName });\n }\n }\n\n /**\n * Remove a language grammar module\n *\n * @param {string} languageName\n */\n function unregisterLanguage(languageName) {\n delete languages[languageName];\n for (const alias of Object.keys(aliases)) {\n if (aliases[alias] === languageName) {\n delete aliases[alias];\n }\n }\n }\n\n /**\n * @returns {string[]} List of language internal names\n */\n function listLanguages() {\n return Object.keys(languages);\n }\n\n /**\n * @param {string} name - name of the language to retrieve\n * @returns {Language | undefined}\n */\n function getLanguage(name) {\n name = (name || '').toLowerCase();\n return languages[name] || languages[aliases[name]];\n }\n\n /**\n *\n * @param {string|string[]} aliasList - single alias or list of aliases\n * @param {{languageName: string}} opts\n */\n function registerAliases(aliasList, { languageName }) {\n if (typeof aliasList === 'string') {\n aliasList = [aliasList];\n }\n aliasList.forEach(alias => { aliases[alias.toLowerCase()] = languageName; });\n }\n\n /**\n * Determines if a given language has auto-detection enabled\n * @param {string} name - name of the language\n */\n function autoDetection(name) {\n const lang = getLanguage(name);\n return lang && !lang.disableAutodetect;\n }\n\n /**\n * Upgrades the old highlightBlock plugins to the new\n * highlightElement API\n * @param {HLJSPlugin} plugin\n */\n function upgradePluginAPI(plugin) {\n // TODO: remove with v12\n if (plugin[\"before:highlightBlock\"] && !plugin[\"before:highlightElement\"]) {\n plugin[\"before:highlightElement\"] = (data) => {\n plugin[\"before:highlightBlock\"](\n Object.assign({ block: data.el }, data)\n );\n };\n }\n if (plugin[\"after:highlightBlock\"] && !plugin[\"after:highlightElement\"]) {\n plugin[\"after:highlightElement\"] = (data) => {\n plugin[\"after:highlightBlock\"](\n Object.assign({ block: data.el }, data)\n );\n };\n }\n }\n\n /**\n * @param {HLJSPlugin} plugin\n */\n function addPlugin(plugin) {\n upgradePluginAPI(plugin);\n plugins.push(plugin);\n }\n\n /**\n * @param {HLJSPlugin} plugin\n */\n function removePlugin(plugin) {\n const index = plugins.indexOf(plugin);\n if (index !== -1) {\n plugins.splice(index, 1);\n }\n }\n\n /**\n *\n * @param {PluginEvent} event\n * @param {any} args\n */\n function fire(event, args) {\n const cb = event;\n plugins.forEach(function(plugin) {\n if (plugin[cb]) {\n plugin[cb](args);\n }\n });\n }\n\n /**\n * DEPRECATED\n * @param {HighlightedHTMLElement} el\n */\n function deprecateHighlightBlock(el) {\n deprecated(\"10.7.0\", \"highlightBlock will be removed entirely in v12.0\");\n deprecated(\"10.7.0\", \"Please use highlightElement now.\");\n\n return highlightElement(el);\n }\n\n /* Interface definition */\n Object.assign(hljs, {\n highlight,\n highlightAuto,\n highlightAll,\n highlightElement,\n // TODO: Remove with v12 API\n highlightBlock: deprecateHighlightBlock,\n configure,\n initHighlighting,\n initHighlightingOnLoad,\n registerLanguage,\n unregisterLanguage,\n listLanguages,\n getLanguage,\n registerAliases,\n autoDetection,\n inherit,\n addPlugin,\n removePlugin\n });\n\n hljs.debugMode = function() { SAFE_MODE = false; };\n hljs.safeMode = function() { SAFE_MODE = true; };\n hljs.versionString = version;\n\n hljs.regex = {\n concat: concat,\n lookahead: lookahead,\n either: either,\n optional: optional,\n anyNumberOfTimes: anyNumberOfTimes\n };\n\n for (const key in MODES) {\n // @ts-ignore\n if (typeof MODES[key] === \"object\") {\n // @ts-ignore\n deepFreeze(MODES[key]);\n }\n }\n\n // merge all the modes/regexes into our main object\n Object.assign(hljs, MODES);\n\n return hljs;\n};\n\n// Other names for the variable may break build script\nconst highlight = HLJS({});\n\n// returns a new instance of the highlighter to be used for extensions\n// check https://github.com/wooorm/lowlight/issues/47\nhighlight.newInstance = () => HLJS({});\n\nmodule.exports = highlight;\nhighlight.HighlightJS = highlight;\nhighlight.default = highlight;\n","const IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';\nconst KEYWORDS = [\n \"as\", // for exports\n \"in\",\n \"of\",\n \"if\",\n \"for\",\n \"while\",\n \"finally\",\n \"var\",\n \"new\",\n \"function\",\n \"do\",\n \"return\",\n \"void\",\n \"else\",\n \"break\",\n \"catch\",\n \"instanceof\",\n \"with\",\n \"throw\",\n \"case\",\n \"default\",\n \"try\",\n \"switch\",\n \"continue\",\n \"typeof\",\n \"delete\",\n \"let\",\n \"yield\",\n \"const\",\n \"class\",\n // JS handles these with a special rule\n // \"get\",\n // \"set\",\n \"debugger\",\n \"async\",\n \"await\",\n \"static\",\n \"import\",\n \"from\",\n \"export\",\n \"extends\",\n // It's reached stage 3, which is \"recommended for implementation\":\n \"using\"\n];\nconst LITERALS = [\n \"true\",\n \"false\",\n \"null\",\n \"undefined\",\n \"NaN\",\n \"Infinity\"\n];\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects\nconst TYPES = [\n // Fundamental objects\n \"Object\",\n \"Function\",\n \"Boolean\",\n \"Symbol\",\n // numbers and dates\n \"Math\",\n \"Date\",\n \"Number\",\n \"BigInt\",\n // text\n \"String\",\n \"RegExp\",\n // Indexed collections\n \"Array\",\n \"Float32Array\",\n \"Float64Array\",\n \"Int8Array\",\n \"Uint8Array\",\n \"Uint8ClampedArray\",\n \"Int16Array\",\n \"Int32Array\",\n \"Uint16Array\",\n \"Uint32Array\",\n \"BigInt64Array\",\n \"BigUint64Array\",\n // Keyed collections\n \"Set\",\n \"Map\",\n \"WeakSet\",\n \"WeakMap\",\n // Structured data\n \"ArrayBuffer\",\n \"SharedArrayBuffer\",\n \"Atomics\",\n \"DataView\",\n \"JSON\",\n // Control abstraction objects\n \"Promise\",\n \"Generator\",\n \"GeneratorFunction\",\n \"AsyncFunction\",\n // Reflection\n \"Reflect\",\n \"Proxy\",\n // Internationalization\n \"Intl\",\n // WebAssembly\n \"WebAssembly\"\n];\n\nconst ERROR_TYPES = [\n \"Error\",\n \"EvalError\",\n \"InternalError\",\n \"RangeError\",\n \"ReferenceError\",\n \"SyntaxError\",\n \"TypeError\",\n \"URIError\"\n];\n\nconst BUILT_IN_GLOBALS = [\n \"setInterval\",\n \"setTimeout\",\n \"clearInterval\",\n \"clearTimeout\",\n\n \"require\",\n \"exports\",\n\n \"eval\",\n \"isFinite\",\n \"isNaN\",\n \"parseFloat\",\n \"parseInt\",\n \"decodeURI\",\n \"decodeURIComponent\",\n \"encodeURI\",\n \"encodeURIComponent\",\n \"escape\",\n \"unescape\"\n];\n\nconst BUILT_IN_VARIABLES = [\n \"arguments\",\n \"this\",\n \"super\",\n \"console\",\n \"window\",\n \"document\",\n \"localStorage\",\n \"sessionStorage\",\n \"module\",\n \"global\" // Node.js\n];\n\nconst BUILT_INS = [].concat(\n BUILT_IN_GLOBALS,\n TYPES,\n ERROR_TYPES\n);\n\n/*\nLanguage: JavaScript\nDescription: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.\nCategory: common, scripting, web\nWebsite: https://developer.mozilla.org/en-US/docs/Web/JavaScript\n*/\n\n\n/** @type LanguageFn */\nfunction javascript(hljs) {\n const regex = hljs.regex;\n /**\n * Takes a string like \" {\n const tag = \"',\n end: ''\n };\n // to avoid some special cases inside isTrulyOpeningTag\n const XML_SELF_CLOSING = /<[A-Za-z0-9\\\\._:-]+\\s*\\/>/;\n const XML_TAG = {\n begin: /<[A-Za-z0-9\\\\._:-]+/,\n end: /\\/[A-Za-z0-9\\\\._:-]+>|\\/>/,\n /**\n * @param {RegExpMatchArray} match\n * @param {CallbackResponse} response\n */\n isTrulyOpeningTag: (match, response) => {\n const afterMatchIndex = match[0].length + match.index;\n const nextChar = match.input[afterMatchIndex];\n if (\n // HTML should not include another raw `<` inside a tag\n // nested type?\n // `>`, etc.\n nextChar === \"<\" ||\n // the , gives away that this is not HTML\n // ``\n nextChar === \",\"\n ) {\n response.ignoreMatch();\n return;\n }\n\n // ``\n // Quite possibly a tag, lets look for a matching closing tag...\n if (nextChar === \">\") {\n // if we cannot find a matching closing tag, then we\n // will ignore it\n if (!hasClosingTag(match, { after: afterMatchIndex })) {\n response.ignoreMatch();\n }\n }\n\n // `` (self-closing)\n // handled by simpleSelfClosing rule\n\n let m;\n const afterMatch = match.input.substring(afterMatchIndex);\n\n // some more template typing stuff\n // (key?: string) => Modify<\n if ((m = afterMatch.match(/^\\s*=/))) {\n response.ignoreMatch();\n return;\n }\n\n // ``\n // technically this could be HTML, but it smells like a type\n // NOTE: This is ugh, but added specifically for https://github.com/highlightjs/highlight.js/issues/3276\n if ((m = afterMatch.match(/^\\s+extends\\s+/))) {\n if (m.index === 0) {\n response.ignoreMatch();\n // eslint-disable-next-line no-useless-return\n return;\n }\n }\n }\n };\n const KEYWORDS$1 = {\n $pattern: IDENT_RE,\n keyword: KEYWORDS,\n literal: LITERALS,\n built_in: BUILT_INS,\n \"variable.language\": BUILT_IN_VARIABLES\n };\n\n // https://tc39.es/ecma262/#sec-literals-numeric-literals\n const decimalDigits = '[0-9](_?[0-9])*';\n const frac = `\\\\.(${decimalDigits})`;\n // DecimalIntegerLiteral, including Annex B NonOctalDecimalIntegerLiteral\n // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals\n const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`;\n const NUMBER = {\n className: 'number',\n variants: [\n // DecimalLiteral\n { begin: `(\\\\b(${decimalInteger})((${frac})|\\\\.)?|(${frac}))` +\n `[eE][+-]?(${decimalDigits})\\\\b` },\n { begin: `\\\\b(${decimalInteger})\\\\b((${frac})\\\\b|\\\\.)?|(${frac})\\\\b` },\n\n // DecimalBigIntegerLiteral\n { begin: `\\\\b(0|[1-9](_?[0-9])*)n\\\\b` },\n\n // NonDecimalIntegerLiteral\n { begin: \"\\\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\\\b\" },\n { begin: \"\\\\b0[bB][0-1](_?[0-1])*n?\\\\b\" },\n { begin: \"\\\\b0[oO][0-7](_?[0-7])*n?\\\\b\" },\n\n // LegacyOctalIntegerLiteral (does not include underscore separators)\n // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals\n { begin: \"\\\\b0[0-7]+n?\\\\b\" },\n ],\n relevance: 0\n };\n\n const SUBST = {\n className: 'subst',\n begin: '\\\\$\\\\{',\n end: '\\\\}',\n keywords: KEYWORDS$1,\n contains: [] // defined later\n };\n const HTML_TEMPLATE = {\n begin: '\\.?html`',\n end: '',\n starts: {\n end: '`',\n returnEnd: false,\n contains: [\n hljs.BACKSLASH_ESCAPE,\n SUBST\n ],\n subLanguage: 'xml'\n }\n };\n const CSS_TEMPLATE = {\n begin: '\\.?css`',\n end: '',\n starts: {\n end: '`',\n returnEnd: false,\n contains: [\n hljs.BACKSLASH_ESCAPE,\n SUBST\n ],\n subLanguage: 'css'\n }\n };\n const GRAPHQL_TEMPLATE = {\n begin: '\\.?gql`',\n end: '',\n starts: {\n end: '`',\n returnEnd: false,\n contains: [\n hljs.BACKSLASH_ESCAPE,\n SUBST\n ],\n subLanguage: 'graphql'\n }\n };\n const TEMPLATE_STRING = {\n className: 'string',\n begin: '`',\n end: '`',\n contains: [\n hljs.BACKSLASH_ESCAPE,\n SUBST\n ]\n };\n const JSDOC_COMMENT = hljs.COMMENT(\n /\\/\\*\\*(?!\\/)/,\n '\\\\*/',\n {\n relevance: 0,\n contains: [\n {\n begin: '(?=@[A-Za-z]+)',\n relevance: 0,\n contains: [\n {\n className: 'doctag',\n begin: '@[A-Za-z]+'\n },\n {\n className: 'type',\n begin: '\\\\{',\n end: '\\\\}',\n excludeEnd: true,\n excludeBegin: true,\n relevance: 0\n },\n {\n className: 'variable',\n begin: IDENT_RE$1 + '(?=\\\\s*(-)|$)',\n endsParent: true,\n relevance: 0\n },\n // eat spaces (not newlines) so we can find\n // types or variables\n {\n begin: /(?=[^\\n])\\s/,\n relevance: 0\n }\n ]\n }\n ]\n }\n );\n const COMMENT = {\n className: \"comment\",\n variants: [\n JSDOC_COMMENT,\n hljs.C_BLOCK_COMMENT_MODE,\n hljs.C_LINE_COMMENT_MODE\n ]\n };\n const SUBST_INTERNALS = [\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE,\n HTML_TEMPLATE,\n CSS_TEMPLATE,\n GRAPHQL_TEMPLATE,\n TEMPLATE_STRING,\n // Skip numbers when they are part of a variable name\n { match: /\\$\\d+/ },\n NUMBER,\n // This is intentional:\n // See https://github.com/highlightjs/highlight.js/issues/3288\n // hljs.REGEXP_MODE\n ];\n SUBST.contains = SUBST_INTERNALS\n .concat({\n // we need to pair up {} inside our subst to prevent\n // it from ending too early by matching another }\n begin: /\\{/,\n end: /\\}/,\n keywords: KEYWORDS$1,\n contains: [\n \"self\"\n ].concat(SUBST_INTERNALS)\n });\n const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains);\n const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([\n // eat recursive parens in sub expressions\n {\n begin: /(\\s*)\\(/,\n end: /\\)/,\n keywords: KEYWORDS$1,\n contains: [\"self\"].concat(SUBST_AND_COMMENTS)\n }\n ]);\n const PARAMS = {\n className: 'params',\n // convert this to negative lookbehind in v12\n begin: /(\\s*)\\(/, // to match the parms with\n end: /\\)/,\n excludeBegin: true,\n excludeEnd: true,\n keywords: KEYWORDS$1,\n contains: PARAMS_CONTAINS\n };\n\n // ES6 classes\n const CLASS_OR_EXTENDS = {\n variants: [\n // class Car extends vehicle\n {\n match: [\n /class/,\n /\\s+/,\n IDENT_RE$1,\n /\\s+/,\n /extends/,\n /\\s+/,\n regex.concat(IDENT_RE$1, \"(\", regex.concat(/\\./, IDENT_RE$1), \")*\")\n ],\n scope: {\n 1: \"keyword\",\n 3: \"title.class\",\n 5: \"keyword\",\n 7: \"title.class.inherited\"\n }\n },\n // class Car\n {\n match: [\n /class/,\n /\\s+/,\n IDENT_RE$1\n ],\n scope: {\n 1: \"keyword\",\n 3: \"title.class\"\n }\n },\n\n ]\n };\n\n const CLASS_REFERENCE = {\n relevance: 0,\n match:\n regex.either(\n // Hard coded exceptions\n /\\bJSON/,\n // Float32Array, OutT\n /\\b[A-Z][a-z]+([A-Z][a-z]*|\\d)*/,\n // CSSFactory, CSSFactoryT\n /\\b[A-Z]{2,}([A-Z][a-z]+|\\d)+([A-Z][a-z]*)*/,\n // FPs, FPsT\n /\\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\\d)*([A-Z][a-z]*)*/,\n // P\n // single letters are not highlighted\n // BLAH\n // this will be flagged as a UPPER_CASE_CONSTANT instead\n ),\n className: \"title.class\",\n keywords: {\n _: [\n // se we still get relevance credit for JS library classes\n ...TYPES,\n ...ERROR_TYPES\n ]\n }\n };\n\n const USE_STRICT = {\n label: \"use_strict\",\n className: 'meta',\n relevance: 10,\n begin: /^\\s*['\"]use (strict|asm)['\"]/\n };\n\n const FUNCTION_DEFINITION = {\n variants: [\n {\n match: [\n /function/,\n /\\s+/,\n IDENT_RE$1,\n /(?=\\s*\\()/\n ]\n },\n // anonymous function\n {\n match: [\n /function/,\n /\\s*(?=\\()/\n ]\n }\n ],\n className: {\n 1: \"keyword\",\n 3: \"title.function\"\n },\n label: \"func.def\",\n contains: [ PARAMS ],\n illegal: /%/\n };\n\n const UPPER_CASE_CONSTANT = {\n relevance: 0,\n match: /\\b[A-Z][A-Z_0-9]+\\b/,\n className: \"variable.constant\"\n };\n\n function noneOf(list) {\n return regex.concat(\"(?!\", list.join(\"|\"), \")\");\n }\n\n const FUNCTION_CALL = {\n match: regex.concat(\n /\\b/,\n noneOf([\n ...BUILT_IN_GLOBALS,\n \"super\",\n \"import\"\n ].map(x => `${x}\\\\s*\\\\(`)),\n IDENT_RE$1, regex.lookahead(/\\s*\\(/)),\n className: \"title.function\",\n relevance: 0\n };\n\n const PROPERTY_ACCESS = {\n begin: regex.concat(/\\./, regex.lookahead(\n regex.concat(IDENT_RE$1, /(?![0-9A-Za-z$_(])/)\n )),\n end: IDENT_RE$1,\n excludeBegin: true,\n keywords: \"prototype\",\n className: \"property\",\n relevance: 0\n };\n\n const GETTER_OR_SETTER = {\n match: [\n /get|set/,\n /\\s+/,\n IDENT_RE$1,\n /(?=\\()/\n ],\n className: {\n 1: \"keyword\",\n 3: \"title.function\"\n },\n contains: [\n { // eat to avoid empty params\n begin: /\\(\\)/\n },\n PARAMS\n ]\n };\n\n const FUNC_LEAD_IN_RE = '(\\\\(' +\n '[^()]*(\\\\(' +\n '[^()]*(\\\\(' +\n '[^()]*' +\n '\\\\)[^()]*)*' +\n '\\\\)[^()]*)*' +\n '\\\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\\\s*=>';\n\n const FUNCTION_VARIABLE = {\n match: [\n /const|var|let/, /\\s+/,\n IDENT_RE$1, /\\s*/,\n /=\\s*/,\n /(async\\s*)?/, // async is optional\n regex.lookahead(FUNC_LEAD_IN_RE)\n ],\n keywords: \"async\",\n className: {\n 1: \"keyword\",\n 3: \"title.function\"\n },\n contains: [\n PARAMS\n ]\n };\n\n return {\n name: 'JavaScript',\n aliases: ['js', 'jsx', 'mjs', 'cjs'],\n keywords: KEYWORDS$1,\n // this will be extended by TypeScript\n exports: { PARAMS_CONTAINS, CLASS_REFERENCE },\n illegal: /#(?![$_A-z])/,\n contains: [\n hljs.SHEBANG({\n label: \"shebang\",\n binary: \"node\",\n relevance: 5\n }),\n USE_STRICT,\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE,\n HTML_TEMPLATE,\n CSS_TEMPLATE,\n GRAPHQL_TEMPLATE,\n TEMPLATE_STRING,\n COMMENT,\n // Skip numbers when they are part of a variable name\n { match: /\\$\\d+/ },\n NUMBER,\n CLASS_REFERENCE,\n {\n scope: 'attr',\n match: IDENT_RE$1 + regex.lookahead(':'),\n relevance: 0\n },\n FUNCTION_VARIABLE,\n { // \"value\" container\n begin: '(' + hljs.RE_STARTERS_RE + '|\\\\b(case|return|throw)\\\\b)\\\\s*',\n keywords: 'return throw case',\n relevance: 0,\n contains: [\n COMMENT,\n hljs.REGEXP_MODE,\n {\n className: 'function',\n // we have to count the parens to make sure we actually have the\n // correct bounding ( ) before the =>. There could be any number of\n // sub-expressions inside also surrounded by parens.\n begin: FUNC_LEAD_IN_RE,\n returnBegin: true,\n end: '\\\\s*=>',\n contains: [\n {\n className: 'params',\n variants: [\n {\n begin: hljs.UNDERSCORE_IDENT_RE,\n relevance: 0\n },\n {\n className: null,\n begin: /\\(\\s*\\)/,\n skip: true\n },\n {\n begin: /(\\s*)\\(/,\n end: /\\)/,\n excludeBegin: true,\n excludeEnd: true,\n keywords: KEYWORDS$1,\n contains: PARAMS_CONTAINS\n }\n ]\n }\n ]\n },\n { // could be a comma delimited list of params to a function call\n begin: /,/,\n relevance: 0\n },\n {\n match: /\\s+/,\n relevance: 0\n },\n { // JSX\n variants: [\n { begin: FRAGMENT.begin, end: FRAGMENT.end },\n { match: XML_SELF_CLOSING },\n {\n begin: XML_TAG.begin,\n // we carefully check the opening tag to see if it truly\n // is a tag and not a false positive\n 'on:begin': XML_TAG.isTrulyOpeningTag,\n end: XML_TAG.end\n }\n ],\n subLanguage: 'xml',\n contains: [\n {\n begin: XML_TAG.begin,\n end: XML_TAG.end,\n skip: true,\n contains: ['self']\n }\n ]\n }\n ],\n },\n FUNCTION_DEFINITION,\n {\n // prevent this from getting swallowed up by function\n // since they appear \"function like\"\n beginKeywords: \"while if switch catch for\"\n },\n {\n // we have to count the parens to make sure we actually have the correct\n // bounding ( ). There could be any number of sub-expressions inside\n // also surrounded by parens.\n begin: '\\\\b(?!function)' + hljs.UNDERSCORE_IDENT_RE +\n '\\\\(' + // first parens\n '[^()]*(\\\\(' +\n '[^()]*(\\\\(' +\n '[^()]*' +\n '\\\\)[^()]*)*' +\n '\\\\)[^()]*)*' +\n '\\\\)\\\\s*\\\\{', // end parens\n returnBegin:true,\n label: \"func.def\",\n contains: [\n PARAMS,\n hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$1, className: \"title.function\" })\n ]\n },\n // catch ... so it won't trigger the property rule below\n {\n match: /\\.\\.\\./,\n relevance: 0\n },\n PROPERTY_ACCESS,\n // hack: prevents detection of keywords in some circumstances\n // .keyword()\n // $keyword = x\n {\n match: '\\\\$' + IDENT_RE$1,\n relevance: 0\n },\n {\n match: [ /\\bconstructor(?=\\s*\\()/ ],\n className: { 1: \"title.function\" },\n contains: [ PARAMS ]\n },\n FUNCTION_CALL,\n UPPER_CASE_CONSTANT,\n CLASS_OR_EXTENDS,\n GETTER_OR_SETTER,\n {\n match: /\\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`\n }\n ]\n };\n}\n\nexport { javascript as default };\n","const MODES = (hljs) => {\n return {\n IMPORTANT: {\n scope: 'meta',\n begin: '!important'\n },\n BLOCK_COMMENT: hljs.C_BLOCK_COMMENT_MODE,\n HEXCOLOR: {\n scope: 'number',\n begin: /#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\\b/\n },\n FUNCTION_DISPATCH: {\n className: \"built_in\",\n begin: /[\\w-]+(?=\\()/\n },\n ATTRIBUTE_SELECTOR_MODE: {\n scope: 'selector-attr',\n begin: /\\[/,\n end: /\\]/,\n illegal: '$',\n contains: [\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE\n ]\n },\n CSS_NUMBER_MODE: {\n scope: 'number',\n begin: hljs.NUMBER_RE + '(' +\n '%|em|ex|ch|rem' +\n '|vw|vh|vmin|vmax' +\n '|cm|mm|in|pt|pc|px' +\n '|deg|grad|rad|turn' +\n '|s|ms' +\n '|Hz|kHz' +\n '|dpi|dpcm|dppx' +\n ')?',\n relevance: 0\n },\n CSS_VARIABLE: {\n className: \"attr\",\n begin: /--[A-Za-z_][A-Za-z0-9_-]*/\n }\n };\n};\n\nconst HTML_TAGS = [\n 'a',\n 'abbr',\n 'address',\n 'article',\n 'aside',\n 'audio',\n 'b',\n 'blockquote',\n 'body',\n 'button',\n 'canvas',\n 'caption',\n 'cite',\n 'code',\n 'dd',\n 'del',\n 'details',\n 'dfn',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'header',\n 'hgroup',\n 'html',\n 'i',\n 'iframe',\n 'img',\n 'input',\n 'ins',\n 'kbd',\n 'label',\n 'legend',\n 'li',\n 'main',\n 'mark',\n 'menu',\n 'nav',\n 'object',\n 'ol',\n 'optgroup',\n 'option',\n 'p',\n 'picture',\n 'q',\n 'quote',\n 'samp',\n 'section',\n 'select',\n 'source',\n 'span',\n 'strong',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'textarea',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'ul',\n 'var',\n 'video'\n];\n\nconst SVG_TAGS = [\n 'defs',\n 'g',\n 'marker',\n 'mask',\n 'pattern',\n 'svg',\n 'switch',\n 'symbol',\n 'feBlend',\n 'feColorMatrix',\n 'feComponentTransfer',\n 'feComposite',\n 'feConvolveMatrix',\n 'feDiffuseLighting',\n 'feDisplacementMap',\n 'feFlood',\n 'feGaussianBlur',\n 'feImage',\n 'feMerge',\n 'feMorphology',\n 'feOffset',\n 'feSpecularLighting',\n 'feTile',\n 'feTurbulence',\n 'linearGradient',\n 'radialGradient',\n 'stop',\n 'circle',\n 'ellipse',\n 'image',\n 'line',\n 'path',\n 'polygon',\n 'polyline',\n 'rect',\n 'text',\n 'use',\n 'textPath',\n 'tspan',\n 'foreignObject',\n 'clipPath'\n];\n\nconst TAGS = [\n ...HTML_TAGS,\n ...SVG_TAGS,\n];\n\n// Sorting, then reversing makes sure longer attributes/elements like\n// `font-weight` are matched fully instead of getting false positives on say `font`\n\nconst MEDIA_FEATURES = [\n 'any-hover',\n 'any-pointer',\n 'aspect-ratio',\n 'color',\n 'color-gamut',\n 'color-index',\n 'device-aspect-ratio',\n 'device-height',\n 'device-width',\n 'display-mode',\n 'forced-colors',\n 'grid',\n 'height',\n 'hover',\n 'inverted-colors',\n 'monochrome',\n 'orientation',\n 'overflow-block',\n 'overflow-inline',\n 'pointer',\n 'prefers-color-scheme',\n 'prefers-contrast',\n 'prefers-reduced-motion',\n 'prefers-reduced-transparency',\n 'resolution',\n 'scan',\n 'scripting',\n 'update',\n 'width',\n // TODO: find a better solution?\n 'min-width',\n 'max-width',\n 'min-height',\n 'max-height'\n].sort().reverse();\n\n// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes\nconst PSEUDO_CLASSES = [\n 'active',\n 'any-link',\n 'blank',\n 'checked',\n 'current',\n 'default',\n 'defined',\n 'dir', // dir()\n 'disabled',\n 'drop',\n 'empty',\n 'enabled',\n 'first',\n 'first-child',\n 'first-of-type',\n 'fullscreen',\n 'future',\n 'focus',\n 'focus-visible',\n 'focus-within',\n 'has', // has()\n 'host', // host or host()\n 'host-context', // host-context()\n 'hover',\n 'indeterminate',\n 'in-range',\n 'invalid',\n 'is', // is()\n 'lang', // lang()\n 'last-child',\n 'last-of-type',\n 'left',\n 'link',\n 'local-link',\n 'not', // not()\n 'nth-child', // nth-child()\n 'nth-col', // nth-col()\n 'nth-last-child', // nth-last-child()\n 'nth-last-col', // nth-last-col()\n 'nth-last-of-type', //nth-last-of-type()\n 'nth-of-type', //nth-of-type()\n 'only-child',\n 'only-of-type',\n 'optional',\n 'out-of-range',\n 'past',\n 'placeholder-shown',\n 'read-only',\n 'read-write',\n 'required',\n 'right',\n 'root',\n 'scope',\n 'target',\n 'target-within',\n 'user-invalid',\n 'valid',\n 'visited',\n 'where' // where()\n].sort().reverse();\n\n// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements\nconst PSEUDO_ELEMENTS = [\n 'after',\n 'backdrop',\n 'before',\n 'cue',\n 'cue-region',\n 'first-letter',\n 'first-line',\n 'grammar-error',\n 'marker',\n 'part',\n 'placeholder',\n 'selection',\n 'slotted',\n 'spelling-error'\n].sort().reverse();\n\nconst ATTRIBUTES = [\n 'accent-color',\n 'align-content',\n 'align-items',\n 'align-self',\n 'alignment-baseline',\n 'all',\n 'anchor-name',\n 'animation',\n 'animation-composition',\n 'animation-delay',\n 'animation-direction',\n 'animation-duration',\n 'animation-fill-mode',\n 'animation-iteration-count',\n 'animation-name',\n 'animation-play-state',\n 'animation-range',\n 'animation-range-end',\n 'animation-range-start',\n 'animation-timeline',\n 'animation-timing-function',\n 'appearance',\n 'aspect-ratio',\n 'backdrop-filter',\n 'backface-visibility',\n 'background',\n 'background-attachment',\n 'background-blend-mode',\n 'background-clip',\n 'background-color',\n 'background-image',\n 'background-origin',\n 'background-position',\n 'background-position-x',\n 'background-position-y',\n 'background-repeat',\n 'background-size',\n 'baseline-shift',\n 'block-size',\n 'border',\n 'border-block',\n 'border-block-color',\n 'border-block-end',\n 'border-block-end-color',\n 'border-block-end-style',\n 'border-block-end-width',\n 'border-block-start',\n 'border-block-start-color',\n 'border-block-start-style',\n 'border-block-start-width',\n 'border-block-style',\n 'border-block-width',\n 'border-bottom',\n 'border-bottom-color',\n 'border-bottom-left-radius',\n 'border-bottom-right-radius',\n 'border-bottom-style',\n 'border-bottom-width',\n 'border-collapse',\n 'border-color',\n 'border-end-end-radius',\n 'border-end-start-radius',\n 'border-image',\n 'border-image-outset',\n 'border-image-repeat',\n 'border-image-slice',\n 'border-image-source',\n 'border-image-width',\n 'border-inline',\n 'border-inline-color',\n 'border-inline-end',\n 'border-inline-end-color',\n 'border-inline-end-style',\n 'border-inline-end-width',\n 'border-inline-start',\n 'border-inline-start-color',\n 'border-inline-start-style',\n 'border-inline-start-width',\n 'border-inline-style',\n 'border-inline-width',\n 'border-left',\n 'border-left-color',\n 'border-left-style',\n 'border-left-width',\n 'border-radius',\n 'border-right',\n 'border-right-color',\n 'border-right-style',\n 'border-right-width',\n 'border-spacing',\n 'border-start-end-radius',\n 'border-start-start-radius',\n 'border-style',\n 'border-top',\n 'border-top-color',\n 'border-top-left-radius',\n 'border-top-right-radius',\n 'border-top-style',\n 'border-top-width',\n 'border-width',\n 'bottom',\n 'box-align',\n 'box-decoration-break',\n 'box-direction',\n 'box-flex',\n 'box-flex-group',\n 'box-lines',\n 'box-ordinal-group',\n 'box-orient',\n 'box-pack',\n 'box-shadow',\n 'box-sizing',\n 'break-after',\n 'break-before',\n 'break-inside',\n 'caption-side',\n 'caret-color',\n 'clear',\n 'clip',\n 'clip-path',\n 'clip-rule',\n 'color',\n 'color-interpolation',\n 'color-interpolation-filters',\n 'color-profile',\n 'color-rendering',\n 'color-scheme',\n 'column-count',\n 'column-fill',\n 'column-gap',\n 'column-rule',\n 'column-rule-color',\n 'column-rule-style',\n 'column-rule-width',\n 'column-span',\n 'column-width',\n 'columns',\n 'contain',\n 'contain-intrinsic-block-size',\n 'contain-intrinsic-height',\n 'contain-intrinsic-inline-size',\n 'contain-intrinsic-size',\n 'contain-intrinsic-width',\n 'container',\n 'container-name',\n 'container-type',\n 'content',\n 'content-visibility',\n 'counter-increment',\n 'counter-reset',\n 'counter-set',\n 'cue',\n 'cue-after',\n 'cue-before',\n 'cursor',\n 'cx',\n 'cy',\n 'direction',\n 'display',\n 'dominant-baseline',\n 'empty-cells',\n 'enable-background',\n 'field-sizing',\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'filter',\n 'flex',\n 'flex-basis',\n 'flex-direction',\n 'flex-flow',\n 'flex-grow',\n 'flex-shrink',\n 'flex-wrap',\n 'float',\n 'flood-color',\n 'flood-opacity',\n 'flow',\n 'font',\n 'font-display',\n 'font-family',\n 'font-feature-settings',\n 'font-kerning',\n 'font-language-override',\n 'font-optical-sizing',\n 'font-palette',\n 'font-size',\n 'font-size-adjust',\n 'font-smooth',\n 'font-smoothing',\n 'font-stretch',\n 'font-style',\n 'font-synthesis',\n 'font-synthesis-position',\n 'font-synthesis-small-caps',\n 'font-synthesis-style',\n 'font-synthesis-weight',\n 'font-variant',\n 'font-variant-alternates',\n 'font-variant-caps',\n 'font-variant-east-asian',\n 'font-variant-emoji',\n 'font-variant-ligatures',\n 'font-variant-numeric',\n 'font-variant-position',\n 'font-variation-settings',\n 'font-weight',\n 'forced-color-adjust',\n 'gap',\n 'glyph-orientation-horizontal',\n 'glyph-orientation-vertical',\n 'grid',\n 'grid-area',\n 'grid-auto-columns',\n 'grid-auto-flow',\n 'grid-auto-rows',\n 'grid-column',\n 'grid-column-end',\n 'grid-column-start',\n 'grid-gap',\n 'grid-row',\n 'grid-row-end',\n 'grid-row-start',\n 'grid-template',\n 'grid-template-areas',\n 'grid-template-columns',\n 'grid-template-rows',\n 'hanging-punctuation',\n 'height',\n 'hyphenate-character',\n 'hyphenate-limit-chars',\n 'hyphens',\n 'icon',\n 'image-orientation',\n 'image-rendering',\n 'image-resolution',\n 'ime-mode',\n 'initial-letter',\n 'initial-letter-align',\n 'inline-size',\n 'inset',\n 'inset-area',\n 'inset-block',\n 'inset-block-end',\n 'inset-block-start',\n 'inset-inline',\n 'inset-inline-end',\n 'inset-inline-start',\n 'isolation',\n 'justify-content',\n 'justify-items',\n 'justify-self',\n 'kerning',\n 'left',\n 'letter-spacing',\n 'lighting-color',\n 'line-break',\n 'line-height',\n 'line-height-step',\n 'list-style',\n 'list-style-image',\n 'list-style-position',\n 'list-style-type',\n 'margin',\n 'margin-block',\n 'margin-block-end',\n 'margin-block-start',\n 'margin-bottom',\n 'margin-inline',\n 'margin-inline-end',\n 'margin-inline-start',\n 'margin-left',\n 'margin-right',\n 'margin-top',\n 'margin-trim',\n 'marker',\n 'marker-end',\n 'marker-mid',\n 'marker-start',\n 'marks',\n 'mask',\n 'mask-border',\n 'mask-border-mode',\n 'mask-border-outset',\n 'mask-border-repeat',\n 'mask-border-slice',\n 'mask-border-source',\n 'mask-border-width',\n 'mask-clip',\n 'mask-composite',\n 'mask-image',\n 'mask-mode',\n 'mask-origin',\n 'mask-position',\n 'mask-repeat',\n 'mask-size',\n 'mask-type',\n 'masonry-auto-flow',\n 'math-depth',\n 'math-shift',\n 'math-style',\n 'max-block-size',\n 'max-height',\n 'max-inline-size',\n 'max-width',\n 'min-block-size',\n 'min-height',\n 'min-inline-size',\n 'min-width',\n 'mix-blend-mode',\n 'nav-down',\n 'nav-index',\n 'nav-left',\n 'nav-right',\n 'nav-up',\n 'none',\n 'normal',\n 'object-fit',\n 'object-position',\n 'offset',\n 'offset-anchor',\n 'offset-distance',\n 'offset-path',\n 'offset-position',\n 'offset-rotate',\n 'opacity',\n 'order',\n 'orphans',\n 'outline',\n 'outline-color',\n 'outline-offset',\n 'outline-style',\n 'outline-width',\n 'overflow',\n 'overflow-anchor',\n 'overflow-block',\n 'overflow-clip-margin',\n 'overflow-inline',\n 'overflow-wrap',\n 'overflow-x',\n 'overflow-y',\n 'overlay',\n 'overscroll-behavior',\n 'overscroll-behavior-block',\n 'overscroll-behavior-inline',\n 'overscroll-behavior-x',\n 'overscroll-behavior-y',\n 'padding',\n 'padding-block',\n 'padding-block-end',\n 'padding-block-start',\n 'padding-bottom',\n 'padding-inline',\n 'padding-inline-end',\n 'padding-inline-start',\n 'padding-left',\n 'padding-right',\n 'padding-top',\n 'page',\n 'page-break-after',\n 'page-break-before',\n 'page-break-inside',\n 'paint-order',\n 'pause',\n 'pause-after',\n 'pause-before',\n 'perspective',\n 'perspective-origin',\n 'place-content',\n 'place-items',\n 'place-self',\n 'pointer-events',\n 'position',\n 'position-anchor',\n 'position-visibility',\n 'print-color-adjust',\n 'quotes',\n 'r',\n 'resize',\n 'rest',\n 'rest-after',\n 'rest-before',\n 'right',\n 'rotate',\n 'row-gap',\n 'ruby-align',\n 'ruby-position',\n 'scale',\n 'scroll-behavior',\n 'scroll-margin',\n 'scroll-margin-block',\n 'scroll-margin-block-end',\n 'scroll-margin-block-start',\n 'scroll-margin-bottom',\n 'scroll-margin-inline',\n 'scroll-margin-inline-end',\n 'scroll-margin-inline-start',\n 'scroll-margin-left',\n 'scroll-margin-right',\n 'scroll-margin-top',\n 'scroll-padding',\n 'scroll-padding-block',\n 'scroll-padding-block-end',\n 'scroll-padding-block-start',\n 'scroll-padding-bottom',\n 'scroll-padding-inline',\n 'scroll-padding-inline-end',\n 'scroll-padding-inline-start',\n 'scroll-padding-left',\n 'scroll-padding-right',\n 'scroll-padding-top',\n 'scroll-snap-align',\n 'scroll-snap-stop',\n 'scroll-snap-type',\n 'scroll-timeline',\n 'scroll-timeline-axis',\n 'scroll-timeline-name',\n 'scrollbar-color',\n 'scrollbar-gutter',\n 'scrollbar-width',\n 'shape-image-threshold',\n 'shape-margin',\n 'shape-outside',\n 'shape-rendering',\n 'speak',\n 'speak-as',\n 'src', // @font-face\n 'stop-color',\n 'stop-opacity',\n 'stroke',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-opacity',\n 'stroke-width',\n 'tab-size',\n 'table-layout',\n 'text-align',\n 'text-align-all',\n 'text-align-last',\n 'text-anchor',\n 'text-combine-upright',\n 'text-decoration',\n 'text-decoration-color',\n 'text-decoration-line',\n 'text-decoration-skip',\n 'text-decoration-skip-ink',\n 'text-decoration-style',\n 'text-decoration-thickness',\n 'text-emphasis',\n 'text-emphasis-color',\n 'text-emphasis-position',\n 'text-emphasis-style',\n 'text-indent',\n 'text-justify',\n 'text-orientation',\n 'text-overflow',\n 'text-rendering',\n 'text-shadow',\n 'text-size-adjust',\n 'text-transform',\n 'text-underline-offset',\n 'text-underline-position',\n 'text-wrap',\n 'text-wrap-mode',\n 'text-wrap-style',\n 'timeline-scope',\n 'top',\n 'touch-action',\n 'transform',\n 'transform-box',\n 'transform-origin',\n 'transform-style',\n 'transition',\n 'transition-behavior',\n 'transition-delay',\n 'transition-duration',\n 'transition-property',\n 'transition-timing-function',\n 'translate',\n 'unicode-bidi',\n 'user-modify',\n 'user-select',\n 'vector-effect',\n 'vertical-align',\n 'view-timeline',\n 'view-timeline-axis',\n 'view-timeline-inset',\n 'view-timeline-name',\n 'view-transition-name',\n 'visibility',\n 'voice-balance',\n 'voice-duration',\n 'voice-family',\n 'voice-pitch',\n 'voice-range',\n 'voice-rate',\n 'voice-stress',\n 'voice-volume',\n 'white-space',\n 'white-space-collapse',\n 'widows',\n 'width',\n 'will-change',\n 'word-break',\n 'word-spacing',\n 'word-wrap',\n 'writing-mode',\n 'x',\n 'y',\n 'z-index',\n 'zoom'\n].sort().reverse();\n\n/*\nLanguage: CSS\nCategory: common, css, web\nWebsite: https://developer.mozilla.org/en-US/docs/Web/CSS\n*/\n\n\n/** @type LanguageFn */\nfunction css(hljs) {\n const regex = hljs.regex;\n const modes = MODES(hljs);\n const VENDOR_PREFIX = { begin: /-(webkit|moz|ms|o)-(?=[a-z])/ };\n const AT_MODIFIERS = \"and or not only\";\n const AT_PROPERTY_RE = /@-?\\w[\\w]*(-\\w+)*/; // @-webkit-keyframes\n const IDENT_RE = '[a-zA-Z-][a-zA-Z0-9_-]*';\n const STRINGS = [\n hljs.APOS_STRING_MODE,\n hljs.QUOTE_STRING_MODE\n ];\n\n return {\n name: 'CSS',\n case_insensitive: true,\n illegal: /[=|'\\$]/,\n keywords: { keyframePosition: \"from to\" },\n classNameAliases: {\n // for visual continuity with `tag {}` and because we\n // don't have a great class for this?\n keyframePosition: \"selector-tag\" },\n contains: [\n modes.BLOCK_COMMENT,\n VENDOR_PREFIX,\n // to recognize keyframe 40% etc which are outside the scope of our\n // attribute value mode\n modes.CSS_NUMBER_MODE,\n {\n className: 'selector-id',\n begin: /#[A-Za-z0-9_-]+/,\n relevance: 0\n },\n {\n className: 'selector-class',\n begin: '\\\\.' + IDENT_RE,\n relevance: 0\n },\n modes.ATTRIBUTE_SELECTOR_MODE,\n {\n className: 'selector-pseudo',\n variants: [\n { begin: ':(' + PSEUDO_CLASSES.join('|') + ')' },\n { begin: ':(:)?(' + PSEUDO_ELEMENTS.join('|') + ')' }\n ]\n },\n // we may actually need this (12/2020)\n // { // pseudo-selector params\n // begin: /\\(/,\n // end: /\\)/,\n // contains: [ hljs.CSS_NUMBER_MODE ]\n // },\n modes.CSS_VARIABLE,\n {\n className: 'attribute',\n begin: '\\\\b(' + ATTRIBUTES.join('|') + ')\\\\b'\n },\n // attribute values\n {\n begin: /:/,\n end: /[;}{]/,\n contains: [\n modes.BLOCK_COMMENT,\n modes.HEXCOLOR,\n modes.IMPORTANT,\n modes.CSS_NUMBER_MODE,\n ...STRINGS,\n // needed to highlight these as strings and to avoid issues with\n // illegal characters that might be inside urls that would tigger the\n // languages illegal stack\n {\n begin: /(url|data-uri)\\(/,\n end: /\\)/,\n relevance: 0, // from keywords\n keywords: { built_in: \"url data-uri\" },\n contains: [\n ...STRINGS,\n {\n className: \"string\",\n // any character other than `)` as in `url()` will be the start\n // of a string, which ends with `)` (from the parent mode)\n begin: /[^)]/,\n endsWithParent: true,\n excludeEnd: true\n }\n ]\n },\n modes.FUNCTION_DISPATCH\n ]\n },\n {\n begin: regex.lookahead(/@/),\n end: '[{;]',\n relevance: 0,\n illegal: /:/, // break on Less variables @var: ...\n contains: [\n {\n className: 'keyword',\n begin: AT_PROPERTY_RE\n },\n {\n begin: /\\s/,\n endsWithParent: true,\n excludeEnd: true,\n relevance: 0,\n keywords: {\n $pattern: /[a-z-]+/,\n keyword: AT_MODIFIERS,\n attribute: MEDIA_FEATURES.join(\" \")\n },\n contains: [\n {\n begin: /[a-z-]+(?=:)/,\n className: \"attribute\"\n },\n ...STRINGS,\n modes.CSS_NUMBER_MODE\n ]\n }\n ]\n },\n {\n className: 'selector-tag',\n begin: '\\\\b(' + TAGS.join('|') + ')\\\\b'\n }\n ]\n };\n}\n\nexport { css as default };\n","/*\nLanguage: Elm\nAuthor: Janis Voigtlaender \nWebsite: https://elm-lang.org\nCategory: functional\n*/\n\n/** @type LanguageFn */\nfunction elm(hljs) {\n const COMMENT = { variants: [\n hljs.COMMENT('--', '$'),\n hljs.COMMENT(\n /\\{-/,\n /-\\}/,\n { contains: [ 'self' ] }\n )\n ] };\n\n const CONSTRUCTOR = {\n className: 'type',\n begin: '\\\\b[A-Z][\\\\w\\']*', // TODO: other constructors (built-in, infix).\n relevance: 0\n };\n\n const LIST = {\n begin: '\\\\(',\n end: '\\\\)',\n illegal: '\"',\n contains: [\n {\n className: 'type',\n begin: '\\\\b[A-Z][\\\\w]*(\\\\((\\\\.\\\\.|,|\\\\w+)\\\\))?'\n },\n COMMENT\n ]\n };\n\n const RECORD = {\n begin: /\\{/,\n end: /\\}/,\n contains: LIST.contains\n };\n\n const CHARACTER = {\n className: 'string',\n begin: '\\'\\\\\\\\?.',\n end: '\\'',\n illegal: '.'\n };\n\n const KEYWORDS = [\n \"let\",\n \"in\",\n \"if\",\n \"then\",\n \"else\",\n \"case\",\n \"of\",\n \"where\",\n \"module\",\n \"import\",\n \"exposing\",\n \"type\",\n \"alias\",\n \"as\",\n \"infix\",\n \"infixl\",\n \"infixr\",\n \"port\",\n \"effect\",\n \"command\",\n \"subscription\"\n ];\n\n return {\n name: 'Elm',\n keywords: KEYWORDS,\n contains: [\n\n // Top-level constructions.\n\n {\n beginKeywords: 'port effect module',\n end: 'exposing',\n keywords: 'port effect module where command subscription exposing',\n contains: [\n LIST,\n COMMENT\n ],\n illegal: '\\\\W\\\\.|;'\n },\n {\n begin: 'import',\n end: '$',\n keywords: 'import as exposing',\n contains: [\n LIST,\n COMMENT\n ],\n illegal: '\\\\W\\\\.|;'\n },\n {\n begin: 'type',\n end: '$',\n keywords: 'type alias',\n contains: [\n CONSTRUCTOR,\n LIST,\n RECORD,\n COMMENT\n ]\n },\n {\n beginKeywords: 'infix infixl infixr',\n end: '$',\n contains: [\n hljs.C_NUMBER_MODE,\n COMMENT\n ]\n },\n {\n begin: 'port',\n end: '$',\n keywords: 'port',\n contains: [ COMMENT ]\n },\n\n // Literals and names.\n CHARACTER,\n hljs.QUOTE_STRING_MODE,\n hljs.C_NUMBER_MODE,\n CONSTRUCTOR,\n hljs.inherit(hljs.TITLE_MODE, { begin: '^[_a-z][\\\\w\\']*' }),\n COMMENT,\n\n { // No markup, relevance booster\n begin: '->|<-' }\n ],\n illegal: /;/\n };\n}\n\nexport { elm as default };\n","/*\nLanguage: HTML, XML\nWebsite: https://www.w3.org/XML/\nCategory: common, web\nAudit: 2020\n*/\n\n/** @type LanguageFn */\nfunction xml(hljs) {\n const regex = hljs.regex;\n // XML names can have the following additional letters: https://www.w3.org/TR/xml/#NT-NameChar\n // OTHER_NAME_CHARS = /[:\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]/;\n // Element names start with NAME_START_CHAR followed by optional other Unicode letters, ASCII digits, hyphens, underscores, and periods\n // const TAG_NAME_RE = regex.concat(/[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]/, regex.optional(/[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*:/), /[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*/);;\n // const XML_IDENT_RE = /[A-Z_a-z:\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]+/;\n // const TAG_NAME_RE = regex.concat(/[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]/, regex.optional(/[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*:/), /[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*/);\n // however, to cater for performance and more Unicode support rely simply on the Unicode letter class\n const TAG_NAME_RE = regex.concat(/[\\p{L}_]/u, regex.optional(/[\\p{L}0-9_.-]*:/u), /[\\p{L}0-9_.-]*/u);\n const XML_IDENT_RE = /[\\p{L}0-9._:-]+/u;\n const XML_ENTITIES = {\n className: 'symbol',\n begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/\n };\n const XML_META_KEYWORDS = {\n begin: /\\s/,\n contains: [\n {\n className: 'keyword',\n begin: /#?[a-z_][a-z1-9_-]+/,\n illegal: /\\n/\n }\n ]\n };\n const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, {\n begin: /\\(/,\n end: /\\)/\n });\n const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { className: 'string' });\n const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' });\n const TAG_INTERNALS = {\n endsWithParent: true,\n illegal: /`]+/ }\n ]\n }\n ]\n }\n ]\n };\n return {\n name: 'HTML, XML',\n aliases: [\n 'html',\n 'xhtml',\n 'rss',\n 'atom',\n 'xjb',\n 'xsd',\n 'xsl',\n 'plist',\n 'wsf',\n 'svg'\n ],\n case_insensitive: true,\n unicodeRegex: true,\n contains: [\n {\n className: 'meta',\n begin: //,\n relevance: 10,\n contains: [\n XML_META_KEYWORDS,\n QUOTE_META_STRING_MODE,\n APOS_META_STRING_MODE,\n XML_META_PAR_KEYWORDS,\n {\n begin: /\\[/,\n end: /\\]/,\n contains: [\n {\n className: 'meta',\n begin: //,\n contains: [\n XML_META_KEYWORDS,\n XML_META_PAR_KEYWORDS,\n QUOTE_META_STRING_MODE,\n APOS_META_STRING_MODE\n ]\n }\n ]\n }\n ]\n },\n hljs.COMMENT(\n //,\n { relevance: 10 }\n ),\n {\n begin: //,\n relevance: 10\n },\n XML_ENTITIES,\n // xml processing instructions\n {\n className: 'meta',\n end: /\\?>/,\n variants: [\n {\n begin: /<\\?xml/,\n relevance: 10,\n contains: [\n QUOTE_META_STRING_MODE\n ]\n },\n {\n begin: /<\\?[a-z][a-z0-9]+/,\n }\n ]\n\n },\n {\n className: 'tag',\n /*\n The lookahead pattern (?=...) ensures that 'begin' only matches\n ')/,\n end: />/,\n keywords: { name: 'style' },\n contains: [ TAG_INTERNALS ],\n starts: {\n end: /<\\/style>/,\n returnEnd: true,\n subLanguage: [\n 'css',\n 'xml'\n ]\n }\n },\n {\n className: 'tag',\n // See the comment in the + + +

SimplyCountdown.js - ${type} Test

+
+ +
+ ${content} + +`; +}; + +// Test ES Module natif +const esTest = commonHtml( + "ES Module", + ` + +` +); + +// Test UMD Global +const umdGlobalTest = commonHtml( + "UMD (Global)", + ` + + +` +); + +// Test UMD avec AMD (RequireJS) +const umdAmdTest = commonHtml( + "UMD (AMD/RequireJS)", + ` + + +` +); + +// Test CommonJS (Node.js) +const commonJsTest = commonHtml( + "CommonJS (Node.js-like)", + ` + + + +` +); + +// Test Dynamic Loading +const dynamicLoadingTest = commonHtml( + "Dynamic Loading", + ` + +` +); + +// GΓ©nΓ©rer tous les fichiers de test +const tests = { + "index.es.html": esTest, + "index.umd-global.html": umdGlobalTest, + "index.umd-amd.html": umdAmdTest, + "index.umd-commonjs.html": commonJsTest, + "index.umd-dynamic.html": dynamicLoadingTest, +}; + +// CrΓ©er le dossier dist_tests s'il n'existe pas +const testDir = path.join(__dirname, "..", "dist_tests"); +if (!fs.existsSync(testDir)) { + fs.mkdirSync(testDir); +} + +// GΓ©nΓ©rer tous les fichiers +Object.entries(tests).forEach(([filename, content]) => { + fs.writeFileSync(path.join(testDir, filename), content); +}); diff --git a/scripts/server.test.js b/scripts/server.test.js new file mode 100644 index 0000000..83121ba --- /dev/null +++ b/scripts/server.test.js @@ -0,0 +1,34 @@ +import { createServer } from "http"; +import handler from "serve-handler"; +import { spawn } from "child_process"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const PORT = 5173; + +const server = createServer((request, response) => { + return handler(request, response, { + public: path.join(__dirname, ".."), + cleanUrls: false, + }); +}); + +server.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}/dist_tests/index.es.html`); + + const openCommand = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open"; + + spawn(openCommand, [`http://localhost:${PORT}/dist_tests/index.es.html`], { + stdio: "ignore", + detached: true, + }).unref(); +}); + +// Handle CTRL+C to stop server +process.on("SIGINT", () => { + server.close(); + process.exit(); +}); diff --git a/src/core/dom.ts b/src/core/dom.ts new file mode 100644 index 0000000..8cbab7c --- /dev/null +++ b/src/core/dom.ts @@ -0,0 +1,93 @@ +/** + * Creates a countdown section element + */ +export const createCountdownSection = ( + sectionClass: string, + amountClass: string, + wordClass: string, + amount: number, + word: string, + params: { + sectionClass: string; + amountClass: string; + wordClass: string; + } +): HTMLElement => { + const section = document.createElement("div"); + section.className = `${sectionClass} ${params.sectionClass}`; + + const wrap = document.createElement("div"); + const amount_elem = document.createElement("span"); + const word_elem = document.createElement("span"); + + amount_elem.className = `${amountClass} ${params.amountClass}`; + word_elem.className = `${wordClass} ${params.wordClass}`; + + amount_elem.textContent = String(amount); + word_elem.textContent = word; + + wrap.appendChild(amount_elem); + wrap.appendChild(word_elem); + section.appendChild(wrap); + + return section; +}; + +/** + * Retrieves a countdown section element from a container + */ +export const getCountdownSection = (sectionClass: string, container: HTMLElement): HTMLElement | null => { + return container.querySelector(`.simply-section.${sectionClass}`); +}; + +/** + * Updates a countdown section element + */ +export const updateCountdownSection = (section: HTMLElement, amount: number | string, word: string): void => { + const amountElement = section.querySelector(".simply-amount"); + const wordElement = section.querySelector(".simply-word"); + + if (amountElement) { + amountElement.textContent = String(amount); + } + if (wordElement) { + wordElement.textContent = word; + } +}; + +/** + * Creates all countdown elements + */ +export const createCountdown = ( + container: HTMLElement, + params: { + sectionClass: string; + amountClass: string; + wordClass: string; + } +): { + days: HTMLElement; + hours: HTMLElement; + minutes: HTMLElement; + seconds: HTMLElement; +} => { + const amountCls = "simply-amount"; + const wordCls = "simply-word"; + + const days = createCountdownSection("simply-section simply-days-section", amountCls, wordCls, 0, "day", params); + const hours = createCountdownSection("simply-section simply-hours-section", amountCls, wordCls, 0, "hour", params); + const minutes = createCountdownSection("simply-section simply-minutes-section", amountCls, wordCls, 0, "minute", params); + const seconds = createCountdownSection("simply-section simply-seconds-section", amountCls, wordCls, 0, "second", params); + + container.appendChild(days); + container.appendChild(hours); + container.appendChild(minutes); + container.appendChild(seconds); + + return { + days, + hours, + minutes, + seconds, + }; +}; diff --git a/src/core/simplyCountdown.ts b/src/core/simplyCountdown.ts new file mode 100644 index 0000000..33d778a --- /dev/null +++ b/src/core/simplyCountdown.ts @@ -0,0 +1,391 @@ +/*! + * Project : simplyCountdown.js + * Date : 2024-12-27 + * License : MIT + * Version : 3.0.0 + * Author : Vincent Loy-Serre + * Contributors : + * - Justin Beasley + * - Nathan Smith + * - Mehdi Rezaei + * - mira01 + */ + +import type { CountdownParameters, CountdownSelector, CountdownState, CountdownController, CountdownControllerArray } from "../types"; +import { createCountdown, updateCountdownSection } from "./dom"; + +const defaultParams: CountdownParameters = { + year: 2024, + month: 12, + day: 25, + hours: 0, + minutes: 0, + seconds: 0, + words: { + days: { lambda: (root, n) => (n > 1 ? root + "s" : root), root: "day" }, + hours: { lambda: (root, n) => (n > 1 ? root + "s" : root), root: "hour" }, + minutes: { lambda: (root, n) => (n > 1 ? root + "s" : root), root: "minute" }, + seconds: { lambda: (root, n) => (n > 1 ? root + "s" : root), root: "second" }, + }, + plural: true, + inline: false, + inlineSeparator: ", ", + enableUtc: false, + onEnd: () => {}, + refresh: 1000, + inlineClass: "simply-countdown-inline", + sectionClass: "simply-section", + amountClass: "simply-amount", + wordClass: "simply-word", + zeroPad: false, + countUp: false, + removeZeroUnits: false, + onStop: () => {}, + onResume: () => {}, + onUpdate: () => {}, +}; + +const isNodeList = (element: CountdownSelector): element is NodeListOf => { + return element instanceof NodeList; +}; + +interface TimeUnit { + value: number; + word: keyof CountdownParameters["words"]; + element?: HTMLElement; +} + +/** + * Formats a time unit with optional zero padding and pluralization + * @param unit - The time unit object containing value and word properties + * @param params - The countdown parameters containing formatting options and word definitions + * @returns A formatted string containing the value and pluralized word for the time unit + * @example + * // With zeroPad: true + * formatTimeUnit({value: 5, word: 'days'}, params) // returns "05 days" + * // With zeroPad: false + * formatTimeUnit({value: 5, word: 'days'}, params) // returns "5 days" + */ +function formatTimeUnit(unit: TimeUnit, params: CountdownParameters): string { + const value = params.zeroPad ? String(unit.value).padStart(2, "0") : unit.value; + return `${value} ${params.words[unit.word].lambda(params.words[unit.word].root, unit.value)}`; +} + +/** + * Determines whether a time unit should be displayed based on its value and the values of previous units + * @param unit - The current time unit to evaluate + * @param previousUnits - Array of time units that come before the current unit + * @param params - Configuration parameters for the countdown + * @returns True if the unit should be displayed, false otherwise + * + * If removeZeroUnits is false in params, always returns true. + * Otherwise, returns true if either: + * - The current unit value is not zero + * - Any previous unit has a non-zero value + */ +function shouldDisplay(unit: TimeUnit, previousUnits: TimeUnit[], params: CountdownParameters): boolean { + if (!params.removeZeroUnits) return true; + return unit.value !== 0 || previousUnits.some((u) => u.value !== 0); +} + +/** + * Displays the countdown timer inline within the specified HTML element. + * + * @param timeUnits - Array of time units containing values and labels for display + * @param params - Configuration parameters for the countdown display + * @param element - The HTML element where the countdown will be rendered + * + * @remarks + * The function filters and formats time units based on display rules, then joins them with + * the specified separator from params.inlineSeparator before setting the element's innerHTML. + */ +function displayInline(timeUnits: TimeUnit[], params: CountdownParameters, element: HTMLElement): void { + const displayStr = timeUnits + .filter((unit, index) => shouldDisplay(unit, timeUnits.slice(0, index), params)) + .map((unit) => formatTimeUnit(unit as { value: number; word: keyof typeof params.words }, params)) + .join(params.inlineSeparator); + + element.innerHTML = displayStr; +} + +/** + * Updates the display of time units in the countdown based on their values and display conditions + * @param timeUnits - Array of TimeUnit objects containing the time values and their corresponding words + * @param params - Configuration parameters for the countdown display + * @param countdown - DOM elements representing the countdown display sections + * @returns void + * + * @remarks + * This function iterates through each time unit and determines whether it should be shown based on: + * - If it's the seconds unit (always shown) + * - If it meets display criteria based on previous units + * + * For units that should be shown, it: + * - Updates the display value (with optional zero padding) + * - Updates the word label using the configured lambda function + * - Shows the unit's DOM element + * + * For units that shouldn't be shown, it hides their DOM elements + */ +function displayBlocks(timeUnits: TimeUnit[], params: CountdownParameters, countdown: any): void { + timeUnits.forEach((unit, index) => { + const shouldShow = unit.word === "seconds" || shouldDisplay(unit, timeUnits.slice(0, index), params); + + if (shouldShow) { + updateCountdownSection( + countdown[unit.word], + params.zeroPad ? String(unit.value).padStart(2, "0") : unit.value, + params.words[unit.word].lambda(params.words[unit.word].root, unit.value) + ); + countdown[unit.word].style.display = ""; + } else { + countdown[unit.word].style.display = "none"; + } + }); +} + +/** + * Creates a countdown instance that manages the countdown timer functionality. + * + * @param targetElement - The HTML element where the countdown will be rendered + * @param parameters - Configuration parameters for the countdown + * + * @returns A controller object with methods to control the countdown: + * - stopCountdown: Pauses the countdown and triggers onStop callback + * - resumeCountdown: Resumes a paused countdown and triggers onResume callback + * - updateCountdown: Updates countdown parameters and triggers onUpdate callback + * - getState: Returns current state of the countdown + */ +const createCountdownInstance = (targetElement: HTMLElement, parameters: CountdownParameters): CountdownController => { + let state: CountdownState = { + isPaused: false, + interval: null, + targetDate: new Date(), + }; + + const getTargetDate = (params: CountdownParameters): Date => { + return params.enableUtc + ? new Date(Date.UTC(params.year, params.month - 1, params.day, params.hours, params.minutes, params.seconds)) + : new Date(params.year, params.month - 1, params.day, params.hours, params.minutes, params.seconds); + }; + + state.targetDate = getTargetDate(parameters); + + // Create span element for inline mode + let inlineElement: HTMLElement | null = null; + if (parameters.inline) { + inlineElement = document.createElement("span"); + inlineElement.className = parameters.inlineClass; + targetElement.appendChild(inlineElement); + } + + const countdown = parameters.inline + ? null + : createCountdown(targetElement, { + sectionClass: parameters.sectionClass, + amountClass: parameters.amountClass, + wordClass: parameters.wordClass, + }); + + const refresh = () => { + // Fix UTC current date handling + const currentDate = parameters.enableUtc + ? new Date( + Date.UTC( + new Date().getUTCFullYear(), + new Date().getUTCMonth(), + new Date().getUTCDate(), + new Date().getUTCHours(), + new Date().getUTCMinutes(), + new Date().getUTCSeconds() + ) + ) + : new Date(); + + let diff = parameters.countUp ? currentDate.getTime() - state.targetDate.getTime() : state.targetDate.getTime() - currentDate.getTime(); + + if (diff <= 0 && !parameters.countUp) { + diff = 0; + // Clear interval before calling onEnd to prevent multiple calls + if (state.interval !== null) { + clearInterval(state.interval); + } + + if (parameters.onEnd) { + parameters.onEnd(); + } + } + + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + diff -= days * 1000 * 60 * 60 * 24; + + const hours = Math.floor(diff / (1000 * 60 * 60)); + diff -= hours * 1000 * 60 * 60; + + const minutes = Math.floor(diff / (1000 * 60)); + diff -= minutes * 1000 * 60; + + const seconds = Math.floor(diff / 1000); + + if (parameters.inline && inlineElement) { + const timeUnits: TimeUnit[] = [ + { value: days, word: "days" as keyof CountdownParameters["words"] }, + { + value: hours, + word: "hours" as keyof CountdownParameters["words"], + }, + { + value: minutes, + word: "minutes" as keyof CountdownParameters["words"], + }, + { + value: seconds, + word: "seconds" as keyof CountdownParameters["words"], + }, + ]; + displayInline(timeUnits, parameters, inlineElement); + } else if (countdown) { + const timeUnits: TimeUnit[] = [ + { value: days, word: "days" as keyof CountdownParameters["words"] }, + { + value: hours, + word: "hours" as keyof CountdownParameters["words"], + }, + { + value: minutes, + word: "minutes" as keyof CountdownParameters["words"], + }, + { + value: seconds, + word: "seconds" as keyof CountdownParameters["words"], + }, + ]; + displayBlocks(timeUnits, parameters, countdown); + } + }; + + const startInterval = () => { + state.interval = setInterval(refresh, parameters.refresh); + refresh(); + }; + + const stopCountdown = () => { + if (state.interval !== null) { + clearInterval(state.interval); + state.interval = null; + } + state.isPaused = true; + parameters.onStop?.(); + }; + + const resumeCountdown = () => { + if (state.isPaused) { + startInterval(); + state.isPaused = false; + parameters.onResume?.(); + } + }; + + const updateCountdown = (newParams: Partial) => { + Object.assign(parameters, newParams); + if ( + newParams.year !== undefined || + newParams.month !== undefined || + newParams.day !== undefined || + newParams.hours !== undefined || + newParams.minutes !== undefined || + newParams.seconds !== undefined + ) { + state.targetDate = getTargetDate(parameters); + } + + parameters.onUpdate?.(newParams); + + if (!state.isPaused) { + if (state.interval) { + clearInterval(state.interval); + } + startInterval(); + } + }; + + const getState = () => ({ ...state }); + + // Start the countdown + startInterval(); + + // Cleanup on element removal + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + mutation.removedNodes.forEach((node) => { + if (node === targetElement) { + if (state.interval !== null) { + clearInterval(state.interval); + } + observer.disconnect(); + } + }); + }); + }); + + if (targetElement.parentNode) { + observer.observe(targetElement.parentNode, { childList: true }); + } + + // Return controller object + return { + stopCountdown, + resumeCountdown, + updateCountdown, + getState, + }; +}; + +/** + * Creates an enhanced array of countdown controllers with additional control methods. + * + * @param controllers - Array of individual countdown controllers to be combined + * @returns An array of controllers enhanced with collective control methods: + * - `stopCountdown()`: Stops all countdowns in the array + * - `resumeCountdown()`: Resumes all countdowns in the array + * - `updateCountdown(newParams)`: Updates all countdowns with new parameters + * - `getState()`: Returns an array of states from all countdowns + */ +const createControllerArray = (controllers: CountdownController[]): CountdownControllerArray => { + const array = controllers as CountdownControllerArray; + + array.stopCountdown = () => controllers.forEach((c) => c.stopCountdown()); + array.resumeCountdown = () => controllers.forEach((c) => c.resumeCountdown()); + array.updateCountdown = (newParams) => controllers.forEach((c) => c.updateCountdown(newParams)); + array.getState = () => controllers.map((c) => c.getState()); + + return array; +}; + +/** + * Creates a countdown timer on specified HTML elements + * @param element - A CSS selector string, HTMLElement, or NodeList targeting the countdown container(s) + * @param args - Optional configuration parameters for the countdown + * @returns A CountdownController for single element or CountdownControllerArray for multiple elements + */ +const simplyCountdown = ( + element: CountdownSelector, + args: Partial = defaultParams +): CountdownController | CountdownControllerArray => { + const parameters: CountdownParameters = { ...defaultParams, ...args }; + + if (typeof element === "string") { + const elements = document.querySelectorAll(element); + const controllers = Array.from(elements).map((el) => createCountdownInstance(el, parameters)); + return controllers.length === 1 ? controllers[0] : createControllerArray(controllers); + } + + if (isNodeList(element)) { + const controllers = Array.from(element).map((el) => createCountdownInstance(el, parameters)); + return controllers.length === 1 ? controllers[0] : createControllerArray(controllers); + } + + return createCountdownInstance(element, parameters); +}; + +export default simplyCountdown; diff --git a/src/core/simplyCountdown.umd.ts b/src/core/simplyCountdown.umd.ts new file mode 100644 index 0000000..8270923 --- /dev/null +++ b/src/core/simplyCountdown.umd.ts @@ -0,0 +1,22 @@ +import simplyCountdownCore from "./simplyCountdown"; + +declare const define: { + (factory: () => any): void; + amd: boolean; +}; + +if (typeof define === "function" && define.amd) { + // AMD + define(function () { + return simplyCountdownCore; + }); +} else if (typeof module === "object" && module.exports) { + // Node + module.exports = simplyCountdownCore; +} else { + // Browser + (window as any).simplyCountdown = simplyCountdownCore; +} + +// Export for Vite/Rollup +export default simplyCountdownCore; diff --git a/src/themes/circle.css b/src/themes/circle.css new file mode 100644 index 0000000..c43dcfc --- /dev/null +++ b/src/themes/circle.css @@ -0,0 +1,73 @@ +.simply-countdown-circle { + --sc-circle-primary: #6366f1; + --sc-circle-secondary: #818cf8; + --sc-circle-bg: #1e1b4b; + --sc-circle-text: #fff; + + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.5rem; + font-family: "Inter", sans-serif; +} + +.simply-countdown-circle > .simply-section { + position: relative; + width: 100px; + height: 100px; + padding: 1rem; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + border-radius: 50%; + background: linear-gradient(45deg, var(--sc-circle-primary), var(--sc-circle-secondary)); + box-shadow: 0 0 25px -5px var(--sc-circle-primary); + animation: pulse-circle 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.simply-countdown-circle > .simply-section::before { + content: ""; + position: absolute; + inset: 6px; + border-radius: 50%; + background: var(--sc-circle-bg); + z-index: 0; +} + +.simply-countdown-circle > .simply-section > div { + position: relative; + z-index: 1; + color: var(--sc-circle-text); + text-align: center; +} + +.simply-countdown-circle .simply-amount { + display: block; + font-size: 1.75rem; + font-weight: 700; + line-height: 1; + background: linear-gradient(to right, var(--sc-circle-primary), var(--sc-circle-secondary)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.simply-countdown-circle .simply-word { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + opacity: 0.8; +} + +@keyframes pulse-circle { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(0.98); + opacity: 0.9; + } +} diff --git a/src/themes/cyber.css b/src/themes/cyber.css new file mode 100644 index 0000000..9b733d0 --- /dev/null +++ b/src/themes/cyber.css @@ -0,0 +1,155 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.cyberpunk +* Author : Vincent Loy +* Theme : Modern Cyberpunk +*/ + +.simply-countdown-cyber { + overflow: visible; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.75rem; + font-family: "Inter", system-ui, -apple-system, sans-serif; + perspective: 1000px; +} + +.simply-countdown-cyber > .simply-section { + width: 70px; + height: 70px; + padding: 1.5rem; + position: relative; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba(23, 25, 35, 0.9), rgba(15, 17, 25, 0.95)); + border-radius: 0.5rem; + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + backdrop-filter: blur(12px); + transform-style: preserve-3d; +} + +.simply-countdown-cyber > .simply-section::before { + content: ""; + position: absolute; + inset: -1px; + background: linear-gradient(135deg, rgba(120, 240, 255, 0.2), rgba(255, 90, 220, 0.2)); + border-radius: 0.5rem; + z-index: -1; + opacity: 0; + transition: opacity 0.3s ease; +} + +.simply-countdown-cyber > .simply-section::after { + content: ""; + position: absolute; + inset: -2px; + background: linear-gradient(135deg, #78f0ff, #ff5adc); + border-radius: 0.5rem; + z-index: -2; + opacity: 0.15; + filter: blur(4px); + animation: pulse 4s ease-in-out infinite; +} + +.simply-countdown-cyber > .simply-section .glass-overlay { + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); + border-radius: 0.5rem; +} + +.simply-countdown-cyber > .simply-section:hover { + transform: translateY(-4px) translateZ(10px) rotateX(5deg); + box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.5), 0 0 20px rgba(120, 240, 255, 0.2), 0 0 0 1px rgba(120, 240, 255, 0.1); +} + +.simply-countdown-cyber > .simply-section:hover::before { + opacity: 1; +} + +.simply-countdown-cyber > .simply-section > div { + display: flex; + flex-direction: column; + gap: 0.4rem; + align-items: center; + transform-style: preserve-3d; +} + +.simply-countdown-cyber > .simply-section .simply-amount { + font-size: 1.75rem; + font-weight: 700; + background: linear-gradient(to bottom right, #78f0ff, #ff5adc); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + text-shadow: 0 0 20px rgba(120, 240, 255, 0.3), 0 0 40px rgba(120, 240, 255, 0.2); + letter-spacing: -0.02em; + transform: translateZ(10px); +} + +.simply-countdown-cyber > .simply-section .simply-word { + font-size: 0.6rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.2em; + color: rgba(255, 255, 255, 0.7); + transform: translateZ(5px); + position: relative; +} + +.simply-countdown-cyber > .simply-section .simply-word::after { + content: ""; + position: absolute; + left: -10%; + bottom: -4px; + width: 120%; + height: 1px; + background: linear-gradient(to right, rgba(120, 240, 255, 0), rgba(120, 240, 255, 0.5), rgba(255, 90, 220, 0.5), rgba(255, 90, 220, 0)); +} + +@media (min-width: 640px) { + .simply-countdown-cyber > .simply-section { + width: 80px; + height: 80px; + padding: 1.75rem; + } + + .simply-countdown-cyber > .simply-section .simply-amount { + font-size: 2rem; + } + + .simply-countdown-cyber > .simply-section .simply-word { + font-size: 0.75rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown-cyber > .simply-section { + width: 100px; + height: 100px; + padding: 2rem; + } + + .simply-countdown-cyber > .simply-section .simply-amount { + font-size: 2.5rem; + } + + .simply-countdown-cyber > .simply-section .simply-word { + font-size: 0.8rem; + } +} + +/* Add subtle animation for extra futuristic feel */ +@keyframes pulse { + 0%, + 100% { + opacity: 0.15; + transform: scale(1); + } + 50% { + opacity: 0.25; + transform: scale(1.05); + } +} diff --git a/src/themes/dark.css b/src/themes/dark.css new file mode 100644 index 0000000..386f246 --- /dev/null +++ b/src/themes/dark.css @@ -0,0 +1,85 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.dark +* Author : Vincent Loy +* Theme : Dark Modern +*/ + +.simply-countdown-dark { + overflow: hidden; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.25rem; + font-family: "Inter", system-ui, -apple-system, sans-serif; +} + +.simply-countdown-dark > .simply-section { + width: 65px; + height: 65px; + padding: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + background: rgba(15, 23, 42, 0.75); + border: 1px solid rgba(51, 65, 85, 0.6); + border-radius: 1rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + backdrop-filter: blur(10px); +} + +.simply-countdown-dark > .simply-section > div { + display: flex; + flex-direction: column; + line-height: 1; + align-items: center; +} + +.simply-countdown-dark > .simply-section .simply-amount { + font-size: 1.5rem; + font-weight: 700; + color: #f1f5f9; + line-height: 1.2; + letter-spacing: -0.025em; +} + +.simply-countdown-dark > .simply-section .simply-word { + font-size: 0.6rem; + font-weight: 500; + color: #94a3b8; + text-transform: uppercase; + letter-spacing: 0.1em; +} + +@media (min-width: 640px) { + .simply-countdown-dark > .simply-section { + width: 75px; + height: 75px; + padding: 1.75rem; + } + + .simply-countdown-dark > .simply-section .simply-amount { + font-size: 1.75rem; + } + + .simply-countdown-dark > .simply-section .simply-word { + font-size: 0.75rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown-dark > .simply-section { + width: 90px; + height: 90px; + padding: 2rem; + } + + .simply-countdown-dark > .simply-section .simply-amount { + font-size: 2rem; + } + + .simply-countdown-dark > .simply-section .simply-word { + font-size: 0.8rem; + } +} diff --git a/src/themes/default.css b/src/themes/default.css new file mode 100644 index 0000000..80e1514 --- /dev/null +++ b/src/themes/default.css @@ -0,0 +1,85 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.default +* Author : Vincent Loy +* Theme : Light Modern +*/ + +.simply-countdown { + overflow: hidden; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.25rem; + font-family: "Inter", system-ui, -apple-system, sans-serif; +} + +.simply-countdown > .simply-section { + width: 65px; + height: 65px; + padding: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(226, 232, 240, 0.8); + border-radius: 1rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.02); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + backdrop-filter: blur(10px); +} + +.simply-countdown > .simply-section > div { + display: flex; + flex-direction: column; + line-height: 1; + align-items: center; +} + +.simply-countdown > .simply-section .simply-amount { + font-size: 1.5rem; + font-weight: 700; + color: #1e293b; + line-height: 1.2; + letter-spacing: -0.025em; +} + +.simply-countdown > .simply-section .simply-word { + font-size: 0.6rem; + font-weight: 500; + color: #64748b; + text-transform: uppercase; + letter-spacing: 0.1em; +} + +@media (min-width: 640px) { + .simply-countdown > .simply-section { + width: 75px; + height: 75px; + padding: 1.75rem; + } + + .simply-countdown > .simply-section .simply-amount { + font-size: 1.75rem; + } + + .simply-countdown > .simply-section .simply-word { + font-size: 0.75rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown > .simply-section { + width: 90px; + height: 90px; + padding: 2rem; + } + + .simply-countdown > .simply-section .simply-amount { + font-size: 2rem; + } + + .simply-countdown > .simply-section .simply-word { + font-size: 0.8rem; + } +} diff --git a/src/themes/losange.css b/src/themes/losange.css new file mode 100644 index 0000000..198873b --- /dev/null +++ b/src/themes/losange.css @@ -0,0 +1,83 @@ +/** +* Project : simply-countdown +* File : simplyCountdown.theme.losange +* Author : Vincent Loy +*/ + +.simply-countdown-losange { + overflow: visible; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 3rem; + font-family: "Inter", sans-serif; +} + +.simply-countdown-losange > .simply-section { + width: 70px; + height: 70px; + display: flex; + justify-content: center; + align-items: center; + transform: rotate(-45deg); + background: linear-gradient(135deg, #4f46e5, #7c3aed); + border-radius: 0.5rem; + transition: all 0.2s ease-in-out; +} + +.simply-countdown-losange > .simply-section > div { + transform: rotate(45deg); + display: flex; + flex-direction: column; + line-height: 1.2; +} + +.simply-countdown-losange > .simply-section .simply-amount, +.simply-countdown-losange > .simply-section .simply-word { + display: block; + text-align: center; +} + +.simply-countdown-losange > .simply-section .simply-amount { + font-size: 1.25rem; + font-weight: 700; + color: #fff; +} + +.simply-countdown-losange > .simply-section .simply-word { + font-size: 0.65rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +@media (min-width: 640px) { + .simply-countdown-losange > .simply-section { + width: 80px; + height: 80px; + } + + .simply-countdown-losange > .simply-section .simply-amount { + font-size: 1.5rem; + } + + .simply-countdown-losange > .simply-section .simply-word { + font-size: 0.7rem; + } +} + +@media (min-width: 1024px) { + .simply-countdown-losange > .simply-section { + width: 90px; + height: 90px; + } + + .simply-countdown-losange > .simply-section .simply-amount { + font-size: 1.75rem; + } + + .simply-countdown-losange > .simply-section .simply-word { + font-size: 0.75rem; + } +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 0000000..4c2f9e6 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,72 @@ +export interface WordConfig { + lambda: (root: string, n: number) => string; + root: string; +} + +export interface Words { + days: WordConfig; + hours: WordConfig; + minutes: WordConfig; + seconds: WordConfig; +} + +export interface CountdownParameters { + year: number; + month: number; + day: number; + hours: number; + minutes: number; + seconds: number; + words: Words; + plural: boolean; + inline: boolean; + inlineSeparator: string; + enableUtc: boolean; + onEnd: () => void; + refresh: number; + inlineClass: string; + sectionClass: string; + amountClass: string; + wordClass: string; + zeroPad: boolean; + removeZeroUnits: boolean; + countUp: boolean; + onStop?: () => void; + onResume?: () => void; + onUpdate?: (newParams: Partial) => void; +} + +export type CountdownState = { + isPaused: boolean; + interval: ReturnType | null; + targetDate: Date; +}; + +export interface CountdownElements { + days: HTMLElement; + hours: HTMLElement; + minutes: HTMLElement; + seconds: HTMLElement; +} + +export interface CountdownSection { + full: HTMLElement; + amount: HTMLElement; + word: HTMLElement; +} + +export type CountdownSelector = string | HTMLElement | NodeListOf; + +export interface CountdownController { + stopCountdown: () => void; + resumeCountdown: () => void; + updateCountdown: (newParams: Partial) => void; + getState: () => CountdownState; +} + +export interface CountdownControllerArray extends Array { + stopCountdown: () => void; + resumeCountdown: () => void; + updateCountdown: (newParams: Partial) => void; + getState: () => CountdownState[]; +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..2e89a06 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,20 @@ +import { addDynamicIconSelectors } from "@iconify/tailwind"; + +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./docs/src/**/*.{html,js,css}"], + theme: { + extend: { + colors: { + primary: { + DEFAULT: "#4f46e5", + dark: "#4338ca", + }, + }, + fontFamily: { + sans: ["Montserrat", "Arial", "sans-serif"], + }, + }, + }, + plugins: [require("@tailwindcss/typography"), addDynamicIconSelectors()], +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bd9f2f3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "declaration": true, + "declarationDir": "dist/types", + "outDir": "dist", + "sourceMap": true, + "emitDeclarationOnly": true + }, + "include": [ + "src/**/*.ts", + "src/**/*.d.ts" + ], + "exclude": [ + "node_modules", + "dist", + "docs", + "**/*.test.ts", + "tests" + ] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..db54678 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,100 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; + +// Configuration commune +const commonConfig = { + sourcemap: true, +}; + +// Configuration pour le build ES +const esConfig = defineConfig({ + build: { + outDir: "dist", + emptyOutDir: true, + minify: true, + ...commonConfig, + lib: { + entry: resolve(__dirname, "src/core/simplyCountdown.ts"), + formats: ["es"], + fileName: () => "simplyCountdown.js", + }, + rollupOptions: { + output: { + assetFileNames: (assetInfo) => { + if (assetInfo.name === "style.css") { + return "themes/[name][extname]"; + } + return "[name][extname]"; + }, + }, + }, + }, +}); + +// Configuration pour le build UMD +const umdConfig = defineConfig({ + build: { + outDir: "dist", + emptyOutDir: false, + minify: "terser", + terserOptions: { + compress: { + drop_console: true, + }, + }, + ...commonConfig, + lib: { + entry: resolve(__dirname, "src/core/simplyCountdown.umd.ts"), + name: "simplyCountdown", + formats: ["umd"], + fileName: () => "simplyCountdown.umd.js", + }, + rollupOptions: { + output: { + assetFileNames: (assetInfo) => { + if (assetInfo.name === "style.css") { + return "themes/[name][extname]"; + } + return "[name][extname]"; + }, + format: "umd", + name: "simplyCountdown", + amd: { + id: "simplyCountdown", + }, + }, + }, + }, +}); + +// Configuration pour la documentation +const docsConfig = defineConfig({ + root: "docs/src", + publicDir: resolve(__dirname, "docs/src/public"), + build: { + outDir: "../dist", + emptyOutDir: true, + sourcemap: true, + rollupOptions: { + input: { + main: resolve(__dirname, "docs/src/index.html"), + }, + output: { + entryFileNames: "assets/[name].min.js", + chunkFileNames: "assets/[name].min.js", + assetFileNames: "assets/[name].min.[ext]", + }, + }, + }, +}); + +// Export la configuration en fonction du mode +export default ({ mode }: { mode?: string }) => { + if (mode === "docs") { + return docsConfig; + } + if (mode === "umd") { + return umdConfig; + } + return esConfig; +};