This commit is contained in:
i.j 2025-03-01 13:06:28 +00:00
parent ec1055e8ec
commit 72751a25cb
13 changed files with 780 additions and 749 deletions

View File

@ -1,3 +1,12 @@
v4.5.0 - 01/03/2025
- Allow to filter posts by language
- Update documentation
- Improve documentation content/structure
- Update Rollup module bundler
- Change dateLocale and dateOptions param names
- JS refactoring
- Improve performance
v4.4.2 - 04/04/2024 v4.4.2 - 04/04/2024
- Fix render emojos in warning/spoiler text - Fix render emojos in warning/spoiler text
- Sanitize post content before rendering - Sanitize post content before rendering

338
README.md
View File

@ -1,340 +1,46 @@
# 🐘 Mastodon embed timeline # 🐘 Mastodon embed timeline
![Mastodon timeline widget screenshot](screenshot-light-dark.jpg "Mastodon timeline widget screenshot") ![Mastodon timeline widget screenshot](./docs/screenshot-light-dark.jpg "Mastodon timeline widget screenshot")
Embed a Mastodon timeline in your page, only with a CSS and JS file. Embed a Mastodon timeline in your page with just a CSS and JS file.
Demo running: Demo:
<https://codepen.io/ipuntoj/pen/MWppNGL> <https://codepen.io/ipuntoj/pen/MWppNGL>
## 📋 Table of contents ## 📋 Table of contents
- [Installation](#installation) - [Installation](#-installation)
- [Download](#download) - [Setup](#-setup)
- [CDN](#cdn) - [API](#-api)
- [Package manager](#package-manager) - [Examples](#-examples)
- [Setup](#setup) - [Browser support](#-browser-support)
- [Initialize](#initialize) - [Improve me](#-improve-me)
- [Local timeline](#local-timeline) - [License](#-license)
- [Profile timeline](#profile-timeline) - [FAQ](#-faq)
- [Hashtag timeline](#hashtag-timeline) - [Alternatives](#alternatives)
- [Customize](#customize)
- [API](#api)
- [Examples](#examples)
## Installation ## 🛠️ Installation
You have three different ways to install it in your project, choose the one that best suits your needs: Steps for installing the timeline in three different ways. [Click here](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/docs/INSTALLATION.md)
### Download ## ⚙️ Setup
Download into your project the following compiled and minified files: Steps to configure and customize your timeline. [Click here](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/docs/SETUP.md)
- `dist/mastodon-timeline.min.css` ## 🔌 API
- `dist/mastodon-timeline.umd.js`
Now call the CSS and JS files in your HTML page using the `<link>` and `<script>` tags as follows in this example: The current version has the following functions that can be performed:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Your site title</title>
<!-- CSS -->
<link href="path/to/mastodon-timeline.min.css" rel="stylesheet" />
</head>
<body>
<!-- Your HTML content -->
<!-- JavaScript -->
<script src="path/to/mastodon-timeline.umd.js"></script>
<script>
// You can initialize the script here
</script>
</body>
</html>
```
### CDN
This option allows you to start without the need to upload any files on your server.
Copy the following CSS and JS links to include them in your project:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.4.2/dist/mastodon-timeline.min.css" integrity="sha256-1UGgxsonaMCfOEnVOL89aMKSo3GEAmaRP0ISbsWa6lU=" crossorigin="anonymous">
```
```html
<script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.4.2/dist/mastodon-timeline.umd.js" integrity="sha256-E6WPG6iq+qQIzvu3HPJJxoAeRdum5siq13x4ITjyxu8=" crossorigin="anonymous"></script>
```
### Package manager
A quick way to get it installed using **npm** or **yarn**:
```terminal
npm install @idotj/mastodon-embed-timeline
```
or
```terminal
yarn add @idotj/mastodon-embed-timeline
```
After installation, you can import it as follows:
```js
import * as MastodonTimeline from "@idotj/mastodon-embed-timeline";
```
Make sure to import also the file `mastodon-timeline.min.css` into your project.
## Setup
### Initialize
To get your timeline up add the following HTML structure in your page:
```html
<div id="mt-container" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
```
Now you can then initialize the script running:
```js
const myTimeline = new MastodonTimeline.Init();
```
By default it will show a timeline with 20 posts from the instance [mastodon.social](https://mastodon.social/public/local)
If you are trying to initialize the script before `mastodon-timeline.umd.js` is loaded, you will probably get such an error in the console:
"_MastodonTimeline is not defined_".
To fix that try to initialize the script as follow:
```js
window.addEventListener("load", () => {
const myTimeline = new MastodonTimeline.Init();
});
```
The next step is to configure the options/values of your timeline according to the type you prefer. There are three types, **Local**, **Profile** and **Hashtag**. Here you have an example of each one to see how it works:
#### Local timeline
To show a timeline with posts from the instance [mastodon.online](https://mastodon.online/public/local) add the following option/value when initializing the timeline:
```js
const myTimeline = new MastodonTimeline.Init({
instanceUrl: "https://mastodon.online",
});
```
#### Profile timeline
To show a timeline with posts from my Mastodon profile [@idotj](https://mastodon.online/@idotj) add the following options/values when initializing the timeline:
```js
const myTimeline = new MastodonTimeline.Init({
instanceUrl: "https://mastodon.online",
timelineType: "profile",
userId: "000180745",
profileName: "@idotj",
});
```
If you don't know your `userId` you have two ways to get it:
- Copy the url below and paste it in a new tab. Remember to replace the words `INSTANCE` and `USERNAME` with your current values in the url:
<https://INSTANCE/api/v1/accounts/lookup?acct=USERNAME>
The first value you see in the list is your `id` number.
- Click on the link below and put your `@USERNAME` and `@INSTANCE` in the input field:
[https://prouser123.me/mastodon-userid-lookup/](https://prouser123.me/mastodon-userid-lookup/)
#### Hashtag timeline
To show a timeline with posts containing the hashtag [#fediverse](https://mastodon.online/tags/fediverse) add the following options/values when initializing the timeline:
```js
const myTimeline = new MastodonTimeline.Init({
instanceUrl: "https://mastodon.online",
timelineType: "hashtag",
hashtagName: "fediverse",
});
```
### Customize
In the `examples/` folder there is an HTML file `local-timeline-customized.html` where you can see how to customize your timeline by overwriting the CSS styles and using several JS options when initializing the timeline.
If you need to change something in the core files (`src/` folder), I recommend you to read the document [CONTRIBUTING.md](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/CONTRIBUTING.md#testing) to see how to compile and test your changes.
Here you have all the options available to quickly setup and customize your timeline:
```js
// Id of the <div> containing the timeline
// Default: "mt-container"
mtContainerId: "mt-container",
// Mastodon instance Url including https://
// Default: "https://mastodon.social"
instanceUrl: "https://mastodon.social",
// Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag'
// Default: "local"
timelineType: "local",
// Your user ID number on Mastodon instance
// Leave it empty if you didn't choose 'profile' as type of timeline
// Default: ""
userId: "",
// Your user name on Mastodon instance (including the @ symbol at the beginning)
// Leave it empty if you didn't choose 'profile' as type of timeline
// Default: ""
profileName: "",
// The name of the hashtag (not including the # symbol)
// Leave it empty if you didn't choose 'hashtag' as type of timeline
// Default: ""
hashtagName: "",
// Class name for the loading spinner (also used in CSS file)
// Default: "mt-loading-spinner"
spinnerClass: "mt-loading-spinner",
// Preferred color theme: "light", "dark" or "auto"
// Default: "auto"
defaultTheme: "auto",
// Maximum number of posts to request to the server
// Default: "20"
maxNbPostFetch: "20",
// Maximum number of posts to show in the timeline
// Default: "20"
maxNbPostShow: "20",
// Specifies the format of the date according to the chosen language/country
// Default: "en-GB" (British English: day-month-year order)
dateLocale: "en-GB",
// Customize the date format using the options for day, month and year
// Default: day: "2-digit", month: "short", year: "numeric" (DD MMM YYYY)
dateOptions: {
day: "2-digit",
month: "short",
year: "numeric",
},
// Hide unlisted posts
// Default: false (don't hide)
hideUnlisted: false,
// Hide boosted posts
// Default: false (don't hide)
hideReblog: false,
// Hide replies posts
// Default: false (don't hide)
hideReplies: false,
// Hide pinned posts from the profile timeline
// Default: false (don't hide)
hidePinnedPosts: false,
// Hide the user account under the user name
// Default: false (don't hide)
hideUserAccount: false,
// Limit the text content to a maximum number of lines
// Use "0" to show no text
// Default: "" (unlimited)
txtMaxLines: "",
// Customize the text of the button used for showing/hiding sensitive/spoiler text
btnShowMore: "SHOW MORE",
btnShowLess: "SHOW LESS",
// Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag
// Default: false (don't apply)
markdownBlockquote: false,
// Hide custom emojis available on the server
// Default: false (don't hide)
hideEmojos: false,
// Customize the text of the button used for showing a sensitive/spoiler media content
btnShowContent: "SHOW CONTENT",
// Hide video image preview and load the video player instead
// Default: false (don't hide)
hideVideoPreview: false,
// Customize the text of the button used for the image preview to play the video
btnPlayVideoTxt: "Load and play video",
// Hide preview card if post contains a link, photo or video from a Url
// Default: false (don't hide)
hidePreviewLink: false,
// Limit the preview text description to a maximum number of lines
// Use "0" to show no text
// Default: "" (unlimited)
previewMaxLines: "",
// Hide replies, boosts and favourites posts counter
// Default: false (don't hide)
hideCounterBar: false,
// Disable a carousel/lightbox when the user clicks on a picture in a post
// Default: false (not disabled)
disableCarousel: false,
// Customize the text of the buttons used for the carousel/lightbox
carouselCloseTxt: "Close carousel",
carouselPrevTxt: "Previous media item",
carouselNextTxt: "Next media item",
// Customize the text of the button pointing to the Mastodon page placed at the end of the timeline
// Leave the value empty to hide it
btnSeeMore: "See more posts at Mastodon",
// Customize the text of the button reloading the list of posts placed at the end of the timeline
// Leave the value empty to hide it
btnReload: "Refresh",
// Keep searching for the main <div> container before building the timeline. Useful in some cases where extra time is needed to render the page
// Default: false (don't apply)
insistSearchContainer: false,
// Defines the maximum time to continue searching for the main <div> container
// Default: "3000" (3 seconds)
insistSearchContainerTime: "3000",
```
## API
| Function | Description | | Function | Description |
| ------------------------- | ------------------------------------------------------------------------------- | | ------------------------- | ------------------------------------------------------------------------------- |
| `mtColorTheme(themeType)` | Apply a theme color. `themeType` accepts only two values: `'light'` or `'dark'` | | `mtColorTheme(themeType)` | Apply a theme color. `themeType` accepts only two values: `'light'` or `'dark'` |
| `mtUpdate()` | Reload the timeline by fetching the lastest posts | | `mtUpdate()` | Reload the timeline by fetching the lastest posts |
## Examples ## 📚 Examples
The folder `examples/` contains several demos in HTML to play with. Download the full project and open each HTML file in your favorite browser. The folder `examples/` contains different demos of timeline types to play with. Download the full project and open each HTML file in your favorite browser.
Also, you have other alternatives to run these examples locally. Consult the document [CONTRIBUTING.md](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/CONTRIBUTING.md#testing) to use options such as Docker or Http-server. If you have any problems loading the examples in your browser (e.g. CORS error), check the documentation [CONTRIBUTING.md](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/CONTRIBUTING.md#testing) to run the examples using alternatives such as Docker or Http-server.
## 🌐 Browser support ## 🌐 Browser support
@ -350,7 +56,7 @@ Mastodon embed timeline is supported on the latest versions of the following bro
## 🚀 Improve me ## 🚀 Improve me
Feel free to add your features and improvements. Feel free to add your features and improvements.
Take a look at the [CONTRIBUTING.md](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/CONTRIBUTING.md) document to learn more about how to build and collaborate on the project. The [CONTRIBUTING.md](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/CONTRIBUTING.md) document to learn more about how to modify, build and collaborate on the project.
## ⚖️ License ## ⚖️ License

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

75
docs/INSTALLATION.md Normal file
View File

@ -0,0 +1,75 @@
# 🐘 Mastodon embed timeline - 🛠️ Installation
You have three different ways to install it in your project:
- [Download](#download)
- [CDN](#cdn)
- [Package manager](#package-manager)
## Download
Download and copy these two files into your project folder:
- `dist/mastodon-timeline.min.css`
- `dist/mastodon-timeline.umd.js`
Load the CSS and JS files in your HTML page using the `<link>` and `<script>` tags as follows in this example:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Example of site title</title>
<!-- Your CSS -->
<link href="your/path/to/file/mastodon-timeline.min.css" rel="stylesheet" />
</head>
<body>
<!-- HTML content -->
...
<!-- Your JavaScript -->
<script src="your/path/to/file/mastodon-timeline.umd.js"></script>
<script>
// You can initialize the script here
</script>
</body>
</html>
```
## CDN
This option allows you to start without the need to upload any files on your server.
Copy the following CSS and JS links to include them in your project:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.5.0/dist/mastodon-timeline.min.css" crossorigin="anonymous">
```
```html
<script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.5.0/dist/mastodon-timeline.umd.js" crossorigin="anonymous"></script>
```
## Package manager
A quick way to get it installed using **npm** or **yarn**:
```terminal
npm install @idotj/mastodon-embed-timeline
```
or
```terminal
yarn add @idotj/mastodon-embed-timeline
```
After installation, you can import it as follows:
```js
import * as MastodonTimeline from "@idotj/mastodon-embed-timeline";
```
Make sure to import also the file `mastodon-timeline.min.css` into your project.

239
docs/SETUP.md Normal file
View File

@ -0,0 +1,239 @@
# 🐘 Mastodon embed timeline - ⚙️ Setup
## Initialize
Add the following HTML structure in your page:
```html
<div id="mt-container" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
```
Now you can then initialize the script running:
```js
const myTimeline = new MastodonTimeline.Init();
```
By default it will show a timeline with 20 posts from the instance [mastodon.social](https://mastodon.social/public/local)
If you are trying to initialize the script before `mastodon-timeline.umd.js` is loaded, you will probably get such an error in the console:
"_MastodonTimeline is not defined_".
To fix that try to initialize the script as follow:
```js
window.addEventListener("load", () => {
const myTimeline = new MastodonTimeline.Init();
});
```
## Timeline type
There are three types, **Local**, **Profile** and **Hashtag**. Here you have an example of each one to see how it works:
### Local timeline
To show a timeline with posts from a local instance (e.g. [mastodon.online](https://mastodon.online/public/local)) add the following option/value when initializing the timeline:
```js
const myTimeline = new MastodonTimeline.Init({
instanceUrl: "https://mastodon.online",
});
```
### Profile timeline
To show a timeline with posts from a Mastodon profile (e.g. [@idotj](https://mastodon.online/@idotj)) add the following options/values when initializing the timeline:
```js
const myTimeline = new MastodonTimeline.Init({
instanceUrl: "https://mastodon.online",
timelineType: "profile",
userId: "000180745",
profileName: "@idotj",
});
```
If you don't know your `userId` you have two ways to get it:
- Copy the url below and paste it in a new tab. Remember to replace the words `INSTANCE` and `USERNAME` with your current values in the url:
<https://INSTANCE/api/v1/accounts/lookup?acct=USERNAME>
The first value you see in the list is your `id` number.
- Click on the link below and put your `@USERNAME` and `@INSTANCE` in the input field:
[https://mastodon-userid-lookup.jcxldn.net/](https://mastodon-userid-lookup.jcxldn.net/)
### Hashtag timeline
To show a timeline with posts containing a hashtag (e.g. [#fediverse](https://mastodon.online/tags/fediverse)) add the following options/values when initializing the timeline:
```js
const myTimeline = new MastodonTimeline.Init({
instanceUrl: "https://mastodon.online",
timelineType: "hashtag",
hashtagName: "fediverse",
});
```
## Customize
There is a long list of parameters that allows you to quickly customize your timeline:
```js
// Id of the <div> containing the timeline
// Default: "mt-container"
mtContainerId: "mt-container",
// Mastodon instance Url including https://
// Default: "https://mastodon.social"
instanceUrl: "https://mastodon.social",
// Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag'
// Default: "local"
timelineType: "local",
// Your user ID number on Mastodon instance
// Leave it empty if you didn't choose 'profile' as type of timeline
// Default: ""
userId: "",
// Your user name on Mastodon instance (including the @ symbol at the beginning)
// Leave it empty if you didn't choose 'profile' as type of timeline
// Default: ""
profileName: "",
// The name of the hashtag (without the # symbol)
// Leave it empty if you didn't choose 'hashtag' as type of timeline
// Default: ""
hashtagName: "",
// Class name for the loading spinner (also used in CSS file)
// Default: "mt-loading-spinner"
spinnerClass: "mt-loading-spinner",
// Preferred color theme: "light", "dark" or "auto"
// Default: "auto"
defaultTheme: "auto",
// Maximum number of posts to request to the server
// Default: "20"
maxNbPostFetch: "20",
// Maximum number of posts to show in the timeline
// Default: "20"
maxNbPostShow: "20",
// Set the format of the date according to the chosen language/country
// Default: "en-GB" (British English: day-month-year order)
dateFormatLocale: "en-GB",
// Customize the date format using the options for day, month and year
// Default: day: "2-digit", month: "short", year: "numeric" (DD MMM YYYY)
dateFormatOptions: {
day: "2-digit",
month: "short",
year: "numeric",
},
// Hide unlisted posts
// Default: false (don't hide)
hideUnlisted: false,
// Hide boosted posts
// Default: false (don't hide)
hideReblog: false,
// Hide replies posts
// Default: false (don't hide)
hideReplies: false,
// Hide pinned posts from the profile timeline
// Default: false (don't hide)
hidePinnedPosts: false,
// Hide the user account under the user name
// Default: false (don't hide)
hideUserAccount: false,
// Show only posts with the selected language (ISO 639-1)
// Use "en" to show only posts in English
// Default: "" (don't filter by language)
filterByLanguage: "",
// Limit the text content to a maximum number of lines
// Use "0" to show no text
// Default: "" (unlimited)
txtMaxLines: "",
// Customize the text of the button used for showing/hiding sensitive/spoiler text
btnShowMore: "SHOW MORE",
btnShowLess: "SHOW LESS",
// Convert Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag
// Default: false (don't apply)
markdownBlockquote: false,
// Hide custom emojis available on the server
// Default: false (don't hide)
hideEmojos: false,
// Customize the text of the button used for showing a sensitive/spoiler media content
btnShowContent: "SHOW CONTENT",
// Hide video image preview and load the video player instead
// Default: false (don't hide)
hideVideoPreview: false,
// Customize the text of the button used for the image preview to play the video
btnPlayVideoTxt: "Load and play video",
// Hide preview card if post contains a link, photo or video from a Url
// Default: false (don't hide)
hidePreviewLink: false,
// Limit the preview text description to a maximum number of lines
// Use "0" to show no text
// Default: "" (unlimited)
previewMaxLines: "",
// Hide replies, boosts and favourites posts counter
// Default: false (don't hide)
hideCounterBar: false,
// Disable a carousel/lightbox when the user clicks on a picture in a post
// Default: false (not disabled)
disableCarousel: false,
// Customize the text of the buttons used for the carousel/lightbox
// Default:
carouselCloseTxt: "Close carousel",
carouselPrevTxt: "Previous media item",
carouselNextTxt: "Next media item",
// Customize the text of the button pointing to the Mastodon page placed at the end of the timeline
// Leave the value empty to hide it
// Default:
btnSeeMore: "See more posts at Mastodon",
// Customize the text of the button reloading the list of posts placed at the end of the timeline
// Leave the value empty to hide it
// Default:
btnReload: "Refresh",
// Keep searching for the main <div> container before building the timeline. Useful in some cases where extra time is needed to render the page
// Default: false (don't apply)
insistSearchContainer: false,
// Define the maximum time to continue searching for the main <div> container
// Default: "3000" (3 seconds)
insistSearchContainerTime: "3000",
```
In the `examples/` folder there is an HTML file `local-timeline-customized.html` that you can use as a referente to see how to customize your timeline by overwriting some CSS styles and using several parameters when initializing the timeline.
Also, if you need to change something in the core files (`src/` folder), I recommend you to read the document [CONTRIBUTING.md](https://gitlab.com/idotj/mastodon-embed-timeline/-/blob/master/CONTRIBUTING.md#testing) to see how to compile and test your changes.

View File

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -187,8 +187,8 @@
defaultTheme: "light", defaultTheme: "light",
maxNbPostFetch: "42", maxNbPostFetch: "42",
maxNbPostShow: "42", maxNbPostShow: "42",
dateLocale: "en-CA", dateFormatLocale: "en-CA",
dateOptions: { dateFormatOptions: {
day: "2-digit", day: "2-digit",
month: "2-digit", month: "2-digit",
year: "numeric", year: "numeric",
@ -225,8 +225,8 @@
defaultTheme: "light", defaultTheme: "light",
maxNbPostFetch: "42", maxNbPostFetch: "42",
maxNbPostShow: "42", maxNbPostShow: "42",
dateLocale: "en-CA", dateFormatLocale: "en-CA",
dateOptions: { dateFormatOptions: {
day: "2-digit", day: "2-digit",
month: "2-digit", month: "2-digit",
year: "numeric", year: "numeric",

239
package-lock.json generated
View File

@ -1,17 +1,17 @@
{ {
"name": "@idotj/mastodon-embed-timeline", "name": "@idotj/mastodon-embed-timeline",
"version": "4.4.2", "version": "4.5.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@idotj/mastodon-embed-timeline", "name": "@idotj/mastodon-embed-timeline",
"version": "4.4.2", "version": "4.5.0",
"license": "GNU", "license": "GNU",
"devDependencies": { "devDependencies": {
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"clean-css-cli": "^5.6.3", "clean-css-cli": "^5.6.3",
"rollup": "^4.14.0" "rollup": "^4.34.8"
} }
}, },
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
@ -95,205 +95,277 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz",
"integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz",
"integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz",
"integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz",
"integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz",
"integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz",
"integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz",
"integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz",
"integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz",
"integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz",
"integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz",
"integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz",
"integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==",
"cpu": [ "cpu": [
"ppc64le" "ppc64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz",
"integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz",
"integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz",
"integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz",
"integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz",
"integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz",
"integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz",
"integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true, "dev": true,
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
] ]
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.11.3", "version": "8.11.3",
@ -346,12 +418,13 @@
} }
}, },
"node_modules/braces": { "node_modules/braces": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"fill-range": "^7.0.1" "fill-range": "^7.1.1"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -433,10 +506,11 @@
"dev": true "dev": true
}, },
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@ -550,6 +624,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@ -627,12 +702,13 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.14.0", "version": "4.34.8",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz",
"integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.5" "@types/estree": "1.0.6"
}, },
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
@ -642,21 +718,25 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.14.0", "@rollup/rollup-android-arm-eabi": "4.34.8",
"@rollup/rollup-android-arm64": "4.14.0", "@rollup/rollup-android-arm64": "4.34.8",
"@rollup/rollup-darwin-arm64": "4.14.0", "@rollup/rollup-darwin-arm64": "4.34.8",
"@rollup/rollup-darwin-x64": "4.14.0", "@rollup/rollup-darwin-x64": "4.34.8",
"@rollup/rollup-linux-arm-gnueabihf": "4.14.0", "@rollup/rollup-freebsd-arm64": "4.34.8",
"@rollup/rollup-linux-arm64-gnu": "4.14.0", "@rollup/rollup-freebsd-x64": "4.34.8",
"@rollup/rollup-linux-arm64-musl": "4.14.0", "@rollup/rollup-linux-arm-gnueabihf": "4.34.8",
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", "@rollup/rollup-linux-arm-musleabihf": "4.34.8",
"@rollup/rollup-linux-riscv64-gnu": "4.14.0", "@rollup/rollup-linux-arm64-gnu": "4.34.8",
"@rollup/rollup-linux-s390x-gnu": "4.14.0", "@rollup/rollup-linux-arm64-musl": "4.34.8",
"@rollup/rollup-linux-x64-gnu": "4.14.0", "@rollup/rollup-linux-loongarch64-gnu": "4.34.8",
"@rollup/rollup-linux-x64-musl": "4.14.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8",
"@rollup/rollup-win32-arm64-msvc": "4.14.0", "@rollup/rollup-linux-riscv64-gnu": "4.34.8",
"@rollup/rollup-win32-ia32-msvc": "4.14.0", "@rollup/rollup-linux-s390x-gnu": "4.34.8",
"@rollup/rollup-win32-x64-msvc": "4.14.0", "@rollup/rollup-linux-x64-gnu": "4.34.8",
"@rollup/rollup-linux-x64-musl": "4.34.8",
"@rollup/rollup-win32-arm64-msvc": "4.34.8",
"@rollup/rollup-win32-ia32-msvc": "4.34.8",
"@rollup/rollup-win32-x64-msvc": "4.34.8",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@ -743,6 +823,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "@idotj/mastodon-embed-timeline", "name": "@idotj/mastodon-embed-timeline",
"version": "4.4.2", "version": "4.5.0",
"description": "Displays Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.", "description": "Displays a Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.",
"license": "GNU", "license": "GNU",
"author": { "author": {
"name": "idotj", "name": "idotj",
@ -34,7 +34,7 @@
}, },
"devDependencies": { "devDependencies": {
"clean-css-cli": "^5.6.3", "clean-css-cli": "^5.6.3",
"rollup": "^4.14.0", "rollup": "^4.34.8",
"@rollup/plugin-terser": "^0.4.4" "@rollup/plugin-terser": "^0.4.4"
}, },
"files": [ "files": [

View File

@ -1,4 +1,4 @@
/* Mastodon embed timeline v4.4.2 */ /* Mastodon embed timeline v4.5.0 */
/* More info at: */ /* More info at: */
/* https://gitlab.com/idotj/mastodon-embed-timeline */ /* https://gitlab.com/idotj/mastodon-embed-timeline */
@ -476,6 +476,7 @@ body:has(dialog.mt-dialog[open]) {
gap: 0.25rem; gap: 0.25rem;
align-items: center; align-items: center;
opacity: 0.5; opacity: 0.5;
cursor: default;
} }
.mt-post-counter-bar-replies > svg, .mt-post-counter-bar-replies > svg,
.mt-post-counter-bar-reblog > svg, .mt-post-counter-bar-reblog > svg,

View File

@ -1,7 +1,7 @@
/** /**
* Mastodon embed timeline * Mastodon embed timeline
* @author idotj * @author idotj
* @version 4.4.2 * @version 4.5.0
* @url https://gitlab.com/idotj/mastodon-embed-timeline * @url https://gitlab.com/idotj/mastodon-embed-timeline
* @license GNU AGPLv3 * @license GNU AGPLv3
*/ */
@ -20,8 +20,8 @@ export class Init {
defaultTheme: "auto", defaultTheme: "auto",
maxNbPostFetch: "20", maxNbPostFetch: "20",
maxNbPostShow: "20", maxNbPostShow: "20",
dateLocale: "en-GB", dateFormatLocale: "en-GB",
dateOptions: { dateFormatOptions: {
day: "2-digit", day: "2-digit",
month: "short", month: "short",
year: "numeric", year: "numeric",
@ -32,6 +32,7 @@ export class Init {
hidePinnedPosts: false, hidePinnedPosts: false,
hideUserAccount: false, hideUserAccount: false,
txtMaxLines: "", txtMaxLines: "",
filterByLanguage: "",
btnShowMore: "SHOW MORE", btnShowMore: "SHOW MORE",
btnShowLess: "SHOW LESS", btnShowLess: "SHOW LESS",
markdownBlockquote: false, markdownBlockquote: false,
@ -100,7 +101,10 @@ export class Init {
* Find main container in DOM before building the timeline * Find main container in DOM before building the timeline
*/ */
#getContainerNode() { #getContainerNode() {
// console.log("Initializing Mastodon timeline with settings: ", this.mtSettings); // console.log(
// "Initializing Mastodon timeline with settings: ",
// this.mtSettings
// );
const assignContainerNode = () => { const assignContainerNode = () => {
this.mtContainerNode = document.getElementById( this.mtContainerNode = document.getElementById(
@ -261,39 +265,55 @@ export class Init {
* @returns {Object} * @returns {Object}
*/ */
#setUrls(i) { #setUrls(i) {
let urls = {}; const {
timelineType,
userId,
hashtagName,
maxNbPostFetch,
hidePinnedPosts,
hideEmojos,
} = this.mtSettings;
if (this.mtSettings.timelineType === "profile") { const urls = {};
if (this.mtSettings.userId) {
urls.timeline = `${i}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`; switch (timelineType) {
if (!this.mtSettings.hidePinnedPosts) { case "profile":
urls.pinned = `${i}accounts/${this.mtSettings.userId}/statuses?pinned=true`; if (!userId) {
this.#showError(
"Please check your <strong>userId</strong> value",
"⚠️"
);
break;
} }
} else { urls.timeline = `${i}accounts/${userId}/statuses?limit=${maxNbPostFetch}`;
if (!hidePinnedPosts) {
urls.pinned = `${i}accounts/${userId}/statuses?pinned=true`;
}
break;
case "hashtag":
if (!hashtagName) {
this.#showError(
"Please check your <strong>hashtagName</strong> value",
"⚠️"
);
break;
}
urls.timeline = `${i}timelines/tag/${hashtagName}?limit=${maxNbPostFetch}`;
break;
case "local":
urls.timeline = `${i}timelines/public?local=true&limit=${maxNbPostFetch}`;
break;
default:
this.#showError( this.#showError(
"Please check your <strong>userId</strong> value", "Please check your <strong>timelineType</strong> value",
"⚠️" "⚠️"
); );
}
} else if (this.mtSettings.timelineType === "hashtag") {
if (this.mtSettings.hashtagName) {
urls.timeline = `${i}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`;
} else {
this.#showError(
"Please check your <strong>hashtagName</strong> value",
"⚠️"
);
}
} else if (this.mtSettings.timelineType === "local") {
urls.timeline = `${i}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`;
} else {
this.#showError(
"Please check your <strong>timelineType</strong> value",
"⚠️"
);
} }
if (!this.mtSettings.hideEmojos) { if (!hideEmojos) {
urls.emojos = `${i}custom_emojis`; urls.emojos = `${i}custom_emojis`;
} }
@ -375,27 +395,39 @@ export class Init {
// console.log("Mastodon timeline data fetched: ", this.fetchedData); // console.log("Mastodon timeline data fetched: ", this.fetchedData);
const {
hideUnlisted,
hideReblog,
hideReplies,
maxNbPostShow,
filterByLanguage,
} = this.mtSettings;
const posts = this.fetchedData.timeline; const posts = this.fetchedData.timeline;
let nbPostToShow = 0; let nbPostToShow = 0;
this.mtBodyNode.replaceChildren(); this.mtBodyNode.replaceChildren();
posts.forEach((post) => { const filteredPosts = posts.filter((post) => {
const isPublicOrUnlisted = const isPublicOrUnlisted =
post.visibility === "public" || post.visibility === "public" ||
(!this.mtSettings.hideUnlisted && post.visibility === "unlisted"); (!hideUnlisted && post.visibility === "unlisted");
const shouldHideReblog = this.mtSettings.hideReblog && post.reblog; const shouldHideReblog = hideReblog && post.reblog;
const shouldHideReplies = const shouldHideReplies = hideReplies && post.in_reply_to_id;
this.mtSettings.hideReplies && post.in_reply_to_id; const postLanguage =
post.language || (post.reblog ? post.reblog.language : null);
const matchesLanguage =
filterByLanguage === "" || postLanguage === filterByLanguage;
// Filter by (Public / Unlisted) return (
if (isPublicOrUnlisted && !shouldHideReblog && !shouldHideReplies) { isPublicOrUnlisted &&
if (nbPostToShow < this.mtSettings.maxNbPostShow) { !shouldHideReblog &&
this.#appendPost(post, nbPostToShow); !shouldHideReplies &&
nbPostToShow++; matchesLanguage
} else { );
// Reached the limit of maximum number of posts to show });
}
filteredPosts.forEach((post, index) => {
if (index < maxNbPostShow) {
this.#appendPost(post, index);
} }
}); });
@ -472,312 +504,187 @@ export class Init {
* @param {Number} i Index of post * @param {Number} i Index of post
*/ */
#assamblePost(c, i) { #assamblePost(c, i) {
let avatar, const isReblog = Boolean(c.reblog);
user, const post = isReblog ? c.reblog : c;
userName, const {
accountName,
url, url,
date, created_at: date,
formattedDate, replies_count,
favoritesCount, reblogs_count,
reblogCount, favourites_count,
repliesCount; } = post;
const {
avatar,
url: accountUrl,
username,
display_name,
emojis,
} = post.account;
if (c.reblog) { // Avatar
// BOOSTED post const avatarHTML =
// Post url '<a href="' +
url = c.reblog.url; accountUrl +
'" class="mt-post-avatar" rel="nofollow noopener noreferrer" target="_blank">' +
'<div class="mt-post-avatar-' +
(isReblog ? "boosted" : "standard") +
'">' +
'<div class="mt-post-avatar-image-big mt-loading-spinner">' +
'<img src="' +
avatar +
'" alt="' +
this.#escapeHTML(username) +
' avatar" loading="lazy" />' +
"</div>" +
(isReblog
? '<div class="mt-post-avatar-image-small">' +
'<img src="' +
c.account.avatar +
'" alt="' +
this.#escapeHTML(c.account.username) +
' avatar" loading="lazy" />' +
"</div>"
: "") +
"</div>" +
"</a>";
// Boosted avatar // User
avatar = const userNameFull =
'<a href="' + !this.mtSettings.hideEmojos && display_name
c.reblog.account.url + ? this.#shortcode2Emojos(display_name, emojis)
'" class="mt-post-avatar" rel="nofollow noopener noreferrer" target="_blank">' + : display_name || username;
'<div class="mt-post-avatar-boosted">' +
'<div class="mt-post-avatar-image-big mt-loading-spinner">' +
'<img src="' +
c.reblog.account.avatar +
'" alt="' +
this.#escapeHTML(c.reblog.account.username) +
' avatar" loading="lazy" />' +
"</div>" +
'<div class="mt-post-avatar-image-small">' +
'<img src="' +
c.account.avatar +
'" alt="' +
this.#escapeHTML(c.account.username) +
' avatar" loading="lazy" />' +
"</div>" +
"</div>" +
"</a>";
// User name and url const accountName = this.mtSettings.hideUserAccount
if (!this.mtSettings.hideEmojos && c.reblog.account.display_name) { ? ""
userName = this.#shortcode2Emojos( : '<br /><span class="mt-post-header-user-account">@' +
c.reblog.account.display_name, username +
c.reblog.account.emojis "@" +
); new URL(accountUrl).hostname +
} else { "</span>";
userName = c.reblog.account.display_name
? c.reblog.account.display_name
: c.reblog.account.username;
}
if (!this.mtSettings.hideUserAccount) { const userHTML =
accountName = '<div class="mt-post-header-user">' +
'<br /><span class="mt-post-header-user-account">@' + '<a href="' +
c.reblog.account.username + accountUrl +
"@" + '" rel="nofollow noopener noreferrer" target="_blank">' +
new URL(c.reblog.account.url).hostname + '<bdi class="mt-post-header-user-name">' +
"</span>"; userNameFull +
} else { "</bdi>" +
accountName = ""; accountName +
} "</a>" +
"</div>";
user =
'<div class="mt-post-header-user">' +
'<a href="' +
c.reblog.account.url +
'" rel="nofollow noopener noreferrer" target="_blank"><bdi class="mt-post-header-user-name">' +
userName +
"</bdi>" +
accountName +
"</a>" +
"</div>";
// Date
date = c.reblog.created_at;
// Counter bar
repliesCount = c.reblog.replies_count;
reblogCount = c.reblog.reblogs_count;
favoritesCount = c.reblog.favourites_count;
} else {
// STANDARD post
// Post url
url = c.url;
// Avatar
avatar =
'<a href="' +
c.account.url +
'" class="mt-post-avatar" rel="nofollow noopener noreferrer" target="_blank">' +
'<div class="mt-post-avatar-standard">' +
'<div class="mt-post-avatar-image-big mt-loading-spinner">' +
'<img src="' +
c.account.avatar +
'" alt="' +
this.#escapeHTML(c.account.username) +
' avatar" loading="lazy" />' +
"</div>" +
"</div>" +
"</a>";
// User name and url
if (!this.mtSettings.hideEmojos && c.account.display_name) {
userName = this.#shortcode2Emojos(
c.account.display_name,
c.account.emojis
);
} else {
userName = c.account.display_name
? c.account.display_name
: c.account.username;
}
if (!this.mtSettings.hideUserAccount) {
accountName =
'<br /><span class="mt-post-header-user-account">@' +
c.account.username +
"@" +
new URL(c.account.url).hostname +
"</span>";
} else {
accountName = "";
}
user =
'<div class="mt-post-header-user">' +
'<a href="' +
c.account.url +
'" rel="nofollow noopener noreferrer" target="_blank"><bdi class="mt-post-header-user-name">' +
userName +
"</bdi>" +
accountName +
"</a>" +
"</div>";
// Date
date = c.created_at;
// Counter bar
repliesCount = c.replies_count;
reblogCount = c.reblogs_count;
favoritesCount = c.favourites_count;
}
// Date // Date
formattedDate = this.#formatDate(date); const formattedDate = this.#formatDate(date);
const timestamp = ` const dateHTML =
<div class="mt-post-header-date"> '<div class="mt-post-header-date">' +
${ (c.pinned ? "<svg>...</svg>" : "") +
c.pinned '<a href="' +
? '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" class="mt-post-pinned" aria-hidden="true"><path d="m640-480 80 80v80H520v240l-40 40-40-40v-240H240v-80l80-80v-280h-40v-80h400v80h-40v280Zm-286 80h252l-46-46v-314H400v314l-46 46Zm126 0Z"></path></svg>' url +
: "" '" rel="nofollow noopener noreferrer" target="_blank">' +
} '<time datetime="' +
<a href="${url}" rel="nofollow noopener noreferrer" target="_blank"> date +
<time datetime="${date}"> '">' +
${formattedDate} formattedDate +
</time> "</time>" +
${c.edited_at ? " *" : ""} (c.edited_at ? " *" : "") +
</a> "</a>" +
</div>`; "</div>";
// Main text // Post text
let content = ""; const txtTruncateCss =
if (this.mtSettings.txtMaxLines !== "0") { this.mtSettings.txtMaxLines !== "0" ? " truncate" : "";
const txtCss = let postTxt = "";
this.mtSettings.txtMaxLines.length !== 0 ? " truncate" : ""; const textSource = post.spoiler_text ? post.spoiler_text : post.content;
if (c.spoiler_text !== "") { if (textSource) {
content = postTxt =
'<div class="mt-post-txt">' + '<div class="mt-post-txt' +
this.#formatPostText(c.spoiler_text) + txtTruncateCss +
' <button type="button" class="mt-btn-dark mt-btn-spoiler-txt" aria-expanded="false">' + '">' +
this.mtSettings.btnShowMore + '<div class="mt-post-txt-wrapper">' +
"</button>" + this.#formatPostText(textSource) +
'<div class="spoiler-txt-hidden">' + "</div>" +
this.#formatPostText(c.content) + "</div>";
"</div>" +
"</div>";
} else if (
c.reblog &&
c.reblog.content !== "" &&
c.reblog.spoiler_text !== ""
) {
content =
'<div class="mt-post-txt">' +
this.#formatPostText(c.reblog.spoiler_text) +
' <button type="button" class="mt-btn-dark mt-btn-spoiler-txt" aria-expanded="false">' +
this.mtSettings.btnShowMore +
"</button>" +
'<div class="spoiler-txt-hidden">' +
this.#formatPostText(c.reblog.content) +
"</div>" +
"</div>";
} else if (
c.reblog &&
c.reblog.content !== "" &&
c.reblog.spoiler_text === ""
) {
content =
'<div class="mt-post-txt' +
txtCss +
'">' +
'<div class="mt-post-txt-wrapper">' +
this.#formatPostText(c.reblog.content) +
"</div>" +
"</div>";
} else {
content =
'<div class="mt-post-txt' +
txtCss +
'">' +
'<div class="mt-post-txt-wrapper">' +
this.#formatPostText(c.content) +
"</div>" +
"</div>";
}
} }
// Media attachments // Media
let media = []; const media = [
if (c.media_attachments.length > 0) { ...c.media_attachments,
for (let i in c.media_attachments) { ...(c.reblog?.media_attachments || []),
media.push(this.#createMedia(c.media_attachments[i], c.sensitive)); ]
} .map((attachment) => this.#createMedia(attachment, post.sensitive))
} .join("");
if (c.reblog && c.reblog.media_attachments.length > 0) {
for (let i in c.reblog.media_attachments) { const mediaHTML = media
media.push( ? `<div class="mt-post-media-wrapper">${media}</div>`
this.#createMedia(c.reblog.media_attachments[i], c.reblog.sensitive) : "";
);
}
}
media = `<div class="mt-post-media-wrapper">${media.join("")}</div>`;
// Preview link // Preview link
let previewLink = ""; const previewLinkHTML =
if (!this.mtSettings.hidePreviewLink && c.card) { !this.mtSettings.hidePreviewLink && c.card
previewLink = this.#createPreviewLink(c.card); ? this.#createPreviewLink(c.card)
} : "";
// Poll // Poll
let poll = ""; const pollHTML = c.poll
if (c.poll) { ? '<div class="mt-post-poll ' +
let pollOption = "";
for (let i in c.poll.options) {
pollOption += "<li>" + c.poll.options[i].title + "</li>";
}
poll =
'<div class="mt-post-poll ' +
(c.poll.expired ? "mt-post-poll-expired" : "") + (c.poll.expired ? "mt-post-poll-expired" : "") +
'">' + '">' +
"<ul>" + "<ul>" +
pollOption + c.poll.options
.map(function (opt) {
return "<li>" + opt.title + "</li>";
})
.join("") +
"</ul>" + "</ul>" +
"</div>"; "</div>"
} : "";
// Counter bar // Counter bar
let counterBar = ""; const counterBarHTML = !this.mtSettings.hideCounterBar
if (!this.mtSettings.hideCounterBar) { ? '<div class="mt-post-counter-bar">' +
const repliesTag = this.#counteBarItem("replies", replies_count) +
'<div class="mt-post-counter-bar-replies">' + this.#counteBarItem("reblog", reblogs_count) +
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960" aria-hidden="true"><path d="M774.913-185.869V-356q0-56.609-35.891-92.5-35.892-35.891-92.5-35.891H258.045L411.435-331l-56 56.566L105.869-524l249.566-249.566 56 56.566-153.39 153.391h388.477q88.957 0 148.566 59.609 59.608 59.609 59.608 148v170.131h-79.783Z"></path></svg>' + this.#counteBarItem("favorites", favourites_count) +
repliesCount + "</div>"
"</div>"; : "";
const reblogTag = return (
'<div class="mt-post-counter-bar-reblog">' +
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960" aria-hidden="true"><path d="M276.043-65.304 105.869-236.043l170.174-170.175 52.74 54.175-78.652 78.652h449.304v-160h75.261v235.261H250.131l78.652 78.087-52.74 54.74Zm-90.174-457.348v-235.261h524.565L631.782-836l52.74-54.74L854.696-720 684.522-549.26 631.782-604l78.652-78.652H261.13v160h-75.261Z"></path></svg>' +
reblogCount +
"</div>";
const favoritesTag =
'<div class="mt-post-counter-bar-favorites">' +
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960" aria-hidden="true"><path d="m330.955-216.328 149.066-89 149.066 90.023-40.305-168.391 131.217-114.347-172.956-14.87L480-671.869l-67.043 158.521-172.956 14.305 131.427 113.796-40.473 168.919ZM212.086-50.608l70.652-305.305L45.52-561.305l312.645-26.579L480-876.176l121.835 288.292 312.645 26.579-237.218 205.392 71.217 305.306L480-213.173 212.086-50.607ZM480-433.87Z"></path></svg>' +
favoritesCount +
"</div>";
counterBar =
'<div class="mt-post-counter-bar">' +
repliesTag +
reblogTag +
favoritesTag +
"</div>";
}
// Put all elements together in the post container
const post =
'<article class="mt-post" aria-posinset="' + '<article class="mt-post" aria-posinset="' +
(i + 1) + (i + 1) +
'" data-location="' + '" data-location="' +
url + url +
'" tabindex="0">' + '" tabindex="0">' +
'<div class="mt-post-header">' + '<div class="mt-post-header">' +
avatar + avatarHTML +
user + userHTML +
timestamp + dateHTML +
"</div>" + "</div>" +
content + postTxt +
media + mediaHTML +
previewLink + previewLinkHTML +
poll + pollHTML +
counterBar + counterBarHTML +
"</article>"; "</article>"
);
}
return post; /**
* Build counter bar items
* @param {string} t Type of icon
* @param {Number} i Counter
*/
#counteBarItem(t, c) {
const icons = {
replies:
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960" aria-hidden="true"><path d="M774.913-185.869V-356q0-56.609-35.891-92.5-35.892-35.891-92.5-35.891H258.045L411.435-331l-56 56.566L105.869-524l249.566-249.566 56 56.566-153.39 153.391h388.477q88.957 0 148.566 59.609 59.608 59.609 59.608 148v170.131h-79.783Z"></path></svg>',
reblog:
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960" aria-hidden="true"><path d="M276.043-65.304 105.869-236.043l170.174-170.175 52.74 54.175-78.652 78.652h449.304v-160h75.261v235.261H250.131l78.652 78.087-52.74 54.74Zm-90.174-457.348v-235.261h524.565L631.782-836l52.74-54.74L854.696-720 684.522-549.26 631.782-604l78.652-78.652H261.13v160h-75.261Z"></path></svg>',
favorites:
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -960 960 960" aria-hidden="true"><path d="m330.955-216.328 149.066-89 149.066 90.023-40.305-168.391 131.217-114.347-172.956-14.87L480-671.869l-67.043 158.521-172.956 14.305 131.427 113.796-40.473 168.919ZM212.086-50.608l70.652-305.305L45.52-561.305l312.645-26.579L480-876.176l121.835 288.292 312.645 26.579-237.218 205.392 71.217 305.306L480-213.173 212.086-50.607ZM480-433.87Z"></path></svg>',
};
return `<div class="mt-post-counter-bar-${t}">${icons[t]}${c}</div>`;
} }
/** /**
@ -983,8 +890,8 @@ export class Init {
const originalDate = new Date(d); const originalDate = new Date(d);
const formattedDate = new Intl.DateTimeFormat( const formattedDate = new Intl.DateTimeFormat(
this.mtSettings.dateLocale, this.mtSettings.dateFormatLocale,
this.mtSettings.dateOptions this.mtSettings.dateFormatOptions
).format(originalDate); ).format(originalDate);
return formattedDate; return formattedDate;
@ -1408,12 +1315,12 @@ export class Init {
#createPreviewLink(c) { #createPreviewLink(c) {
let previewDescription = ""; let previewDescription = "";
if (this.mtSettings.previewMaxLines !== "0" && c.description) { if (this.mtSettings.previewMaxLines !== "0" && c.description) {
const txtCss = const txtTruncateCss =
this.mtSettings.previewMaxLines.length !== 0 ? " truncate" : ""; this.mtSettings.previewMaxLines.length !== 0 ? " truncate" : "";
previewDescription = previewDescription =
'<span class="mt-post-preview-description' + '<span class="mt-post-preview-description' +
txtCss + txtTruncateCss +
'">' + '">' +
this.#parseHTMLstring(c.description) + this.#parseHTMLstring(c.description) +
"</span>"; "</span>";
@ -1591,24 +1498,37 @@ export class Init {
* @param {Event} e User interaction trigger * @param {Event} e User interaction trigger
*/ */
#openPostUrl(e) { #openPostUrl(e) {
const urlPost = e.target.closest(".mt-post").dataset.location; const urlPost = e.target.closest(".mt-post")?.dataset.location;
if (!urlPost) return;
const tagName = e.target.localName;
if ( if (
e.target.localName !== "a" && tagName === "a" ||
e.target.localName !== "span" && tagName === "span" ||
e.target.localName !== "button" && tagName === "button" ||
e.target.localName !== "bdi" && tagName === "bdi" ||
e.target.localName !== "time" && tagName === "time"
!e.target.classList.contains("mt-post-media-spoiler") && )
e.target.className !== "mt-post-preview-noImage" && return;
e.target.parentNode.className !== "mt-post-avatar-image-big" &&
e.target.parentNode.className !== "mt-post-avatar-image-small" && const targetClass = e.target.className;
e.target.parentNode.className !== "mt-post-header-user-name" && if (
e.target.parentNode.className !== "mt-post-preview-image" && targetClass === "mt-post-media-spoiler" ||
e.target.parentNode.className !== "mt-post-preview" && targetClass === "mt-post-preview-noImage"
urlPost )
) { return;
window.open(urlPost, "_blank", "noopener");
} const parentClass = e.target.parentNode?.className;
if (
parentClass === "mt-post-avatar-image-big" ||
parentClass === "mt-post-avatar-image-small" ||
parentClass === "mt-post-header-user-name" ||
parentClass === "mt-post-preview-image" ||
parentClass === "mt-post-preview"
)
return;
window.open(urlPost, "_blank", "noopener");
} }
/** /**