From 72751a25cb66f5b81d31f28e6b6e6686af172bcb Mon Sep 17 00:00:00 2001 From: "i.j" Date: Sat, 1 Mar 2025 13:06:28 +0000 Subject: [PATCH] V4.5.0 --- CHANGELOG | 9 + README.md | 338 +--------- dist/mastodon-timeline.esm.js | 4 +- dist/mastodon-timeline.min.css | 2 +- dist/mastodon-timeline.umd.js | 4 +- docs/INSTALLATION.md | 75 +++ docs/SETUP.md | 239 +++++++ .../screenshot-light-dark.jpg | Bin examples/local-timeline-customized.html | 8 +- package-lock.json | 239 ++++--- package.json | 6 +- src/mastodon-timeline.css | 3 +- src/mastodon-timeline.js | 602 ++++++++---------- 13 files changed, 780 insertions(+), 749 deletions(-) create mode 100644 docs/INSTALLATION.md create mode 100644 docs/SETUP.md rename screenshot-light-dark.jpg => docs/screenshot-light-dark.jpg (100%) diff --git a/CHANGELOG b/CHANGELOG index b201496..bca0f0b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 - Fix render emojos in warning/spoiler text - Sanitize post content before rendering diff --git a/README.md b/README.md index 92274e4..9dc5c29 100644 --- a/README.md +++ b/README.md @@ -1,340 +1,46 @@ # 🐘 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: ## 📋 Table of contents -- [Installation](#installation) - - [Download](#download) - - [CDN](#cdn) - - [Package manager](#package-manager) -- [Setup](#setup) - - [Initialize](#initialize) - - [Local timeline](#local-timeline) - - [Profile timeline](#profile-timeline) - - [Hashtag timeline](#hashtag-timeline) - - [Customize](#customize) -- [API](#api) -- [Examples](#examples) +- [Installation](#-installation) +- [Setup](#-setup) +- [API](#-api) +- [Examples](#-examples) +- [Browser support](#-browser-support) +- [Improve me](#-improve-me) +- [License](#ī¸-license) +- [FAQ](#-faq) +- [Alternatives](#alternatives) -## 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` -- `dist/mastodon-timeline.umd.js` +## 🔌 API -Now call the CSS and JS files in your HTML page using the `` and ` - - - -``` - -### 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 - -``` - -```html - -``` - -### 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 -
-
-
-
-
-``` - -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: - - 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
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
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
container - // Default: "3000" (3 seconds) - insistSearchContainerTime: "3000", - -``` - -## API +The current version has the following functions that can be performed: | Function | Description | | ------------------------- | ------------------------------------------------------------------------------- | | `mtColorTheme(themeType)` | Apply a theme color. `themeType` accepts only two values: `'light'` or `'dark'` | | `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 @@ -350,7 +56,7 @@ Mastodon embed timeline is supported on the latest versions of the following bro ## 🚀 Improve me 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 diff --git a/dist/mastodon-timeline.esm.js b/dist/mastodon-timeline.esm.js index 2a41636..5bf03a7 100644 --- a/dist/mastodon-timeline.esm.js +++ b/dist/mastodon-timeline.esm.js @@ -1,8 +1,8 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.4.2 + * @version 4.5.0 * @url https://gitlab.com/idotj/mastodon-embed-timeline * @license GNU AGPLv3 */ -class t{constructor(t={}){this.defaultSettings={mtContainerId:"mt-container",instanceUrl:"https://mastodon.social",timelineType:"local",userId:"",profileName:"",hashtagName:"",spinnerClass:"mt-loading-spinner",defaultTheme:"auto",maxNbPostFetch:"20",maxNbPostShow:"20",dateLocale:"en-GB",dateOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,txtMaxLines:"",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",markdownBlockquote:!1,hideEmojos:!1,btnShowContent:"SHOW CONTENT",hideVideoPreview:!1,btnPlayVideoTxt:"Load and play video",hidePreviewLink:!1,previewMaxLines:"",hideCounterBar:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.#t(),this.linkHeader={},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#e((()=>{this.#i()}))}#t(){Number(this.mtSettings.maxNbPostShow)>Number(this.mtSettings.maxNbPostFetch)&&(console.error(`Please check your settings! The maximum number of posts to show is bigger than the maximum number of posts to fetch. Changing the value of "maxNbPostFetch" to: ${this.mtSettings.maxNbPostShow}`),this.mtSettings.maxNbPostFetch=this.mtSettings.maxNbPostShow)}#e(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#i(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#a("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),i=()=>{if(document.getElementById(this.mtSettings.mtContainerId))t();else{performance.now()-e container with id: "${this.mtSettings.mtContainerId}" after several attempts for ${this.mtSettings.insistSearchContainerTime/1e3} seconds`)}};i()}else document.getElementById(this.mtSettings.mtContainerId)?t():console.error(`Impossible to find the
container with id: "${this.mtSettings.mtContainerId}". Please try to add the option 'insistSearchContainer: true' when initializing the script`)}mtUpdate(){this.#e((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
'),this.#a("updateTimeline")}))}mtColorTheme(t){this.#e((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#s(){if("auto"===this.mtSettings.defaultTheme){let t=window.matchMedia("(prefers-color-scheme: dark)");t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light"),t.addEventListener("change",(t=>{t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light")}))}else this.mtColorTheme(this.mtSettings.defaultTheme)}#o(){return new Promise(((t,e)=>{const i=this.mtSettings.instanceUrl?`${this.mtSettings.instanceUrl}/api/v1/`:this.#n("Please check your instanceUrl value","âš ī¸"),s=this.#r(i),a=Object.entries(s).map((([t,i])=>{const s="timeline"===t;return this.#l(i,s).then((e=>({[t]:e}))).catch((s=>(e(new Error(`Something went wrong fetching data from: ${i}`)),this.#n(s.message),{[t]:[]})))}));Promise.all(a).then((async e=>{if(this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),!this.mtSettings.hidePinnedPosts&&void 0!==this.fetchedData.pinned?.length&&0!==this.fetchedData.pinned.length){const t=this.fetchedData.pinned.map((t=>({...t,pinned:!0})));this.fetchedData.timeline=[...t,...this.fetchedData.timeline]}if(this.#d())t();else{do{await this.#m()}while(!this.#d()&&this.linkHeader.next);t()}}))}))}#r(t){let e={};return"profile"===this.mtSettings.timelineType?this.mtSettings.userId?(e.timeline=`${t}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`,this.mtSettings.hidePinnedPosts||(e.pinned=`${t}accounts/${this.mtSettings.userId}/statuses?pinned=true`)):this.#n("Please check your userId value","âš ī¸"):"hashtag"===this.mtSettings.timelineType?this.mtSettings.hashtagName?e.timeline=`${t}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`:this.#n("Please check your hashtagName value","âš ī¸"):"local"===this.mtSettings.timelineType?e.timeline=`${t}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`:this.#n("Please check your timelineType value","âš ī¸"),this.mtSettings.hideEmojos||(e.emojos=`${t}custom_emojis`),e}async#l(t,e=!1){const i=await fetch(t);if(!i.ok)throw new Error(`\n Failed to fetch the following Url:
${t}
Error status: ${i.status}
Error message: ${i.statusText}\n `);const s=await i.json();return e&&i.headers.get("Link")&&(this.linkHeader=this.#h(i.headers.get("Link"))),s}#d(){return this.fetchedData.timeline.length>=Number(this.mtSettings.maxNbPostFetch)}#m(){return new Promise((t=>{this.linkHeader.next?this.#l(this.linkHeader.next,!0).then((e=>{this.fetchedData.timeline=[...this.fetchedData.timeline,...e],t()})):t()}))}#h(t){const e=t.split(", ").map((t=>t.split("; "))).map((t=>[t[1].replace(/"/g,"").replace("rel=",""),t[0].slice(1,-1)]));return Object.fromEntries(e)}async#a(t){await this.#o();const e=this.fetchedData.timeline;let i=0;if(this.mtBodyNode.replaceChildren(),e.forEach((t=>{const e="public"===t.visibility||!this.mtSettings.hideUnlisted&&"unlisted"===t.visibility,s=this.mtSettings.hideReblog&&t.reblog,a=this.mtSettings.hideReplies&&t.in_reply_to_id;!e||s||a||i${e?.length||0} posts have been fetched from the server
This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;this.#n(t,"📭")}}#g(){"0"!==this.mtSettings.txtMaxLines&&0!==this.mtSettings.txtMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines),"0"!==this.mtSettings.previewMaxLines&&0!==this.mtSettings.previewMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-preview-max-lines",this.mtSettings.previewMaxLines)}#u(t){const e=this.mtBodyNode.getElementsByTagName("article");for(let i=0;i
'+this.#f(t.reblog.account.username)+' avatar
'+this.#f(t.account.username)+' avatar
',a=!this.mtSettings.hideEmojos&&t.reblog.account.display_name?this.#S(t.reblog.account.display_name,t.reblog.account.emojis):t.reblog.account.display_name?t.reblog.account.display_name:t.reblog.account.username,o=this.mtSettings.hideUserAccount?"":'
",s='
'+a+""+o+"
",r=t.reblog.created_at,h=t.reblog.replies_count,m=t.reblog.reblogs_count,d=t.reblog.favourites_count):(n=t.url,i='
'+this.#f(t.account.username)+' avatar
',a=!this.mtSettings.hideEmojos&&t.account.display_name?this.#S(t.account.display_name,t.account.emojis):t.account.display_name?t.account.display_name:t.account.username,o=this.mtSettings.hideUserAccount?"":'
",s='
'+a+""+o+"
",r=t.created_at,h=t.replies_count,m=t.reblogs_count,d=t.favourites_count),l=this.#x(r);const c=`\n
\n ${t.pinned?'':""}\n \n \n ${t.edited_at?" *":""}\n \n
`;let p="";if("0"!==this.mtSettings.txtMaxLines){const e=0!==this.mtSettings.txtMaxLines.length?" truncate":"";p=""!==t.spoiler_text?'
'+this.#y(t.spoiler_text)+'
'+this.#y(t.content)+"
":t.reblog&&""!==t.reblog.content&&""!==t.reblog.spoiler_text?'
'+this.#y(t.reblog.spoiler_text)+'
'+this.#y(t.reblog.content)+"
":t.reblog&&""!==t.reblog.content&&""===t.reblog.spoiler_text?'
'+this.#y(t.reblog.content)+"
":'
'+this.#y(t.content)+"
"}let g=[];if(t.media_attachments.length>0)for(let e in t.media_attachments)g.push(this.#L(t.media_attachments[e],t.sensitive));if(t.reblog&&t.reblog.media_attachments.length>0)for(let e in t.reblog.media_attachments)g.push(this.#L(t.reblog.media_attachments[e],t.reblog.sensitive));g=`
${g.join("")}
`;let u="";!this.mtSettings.hidePreviewLink&&t.card&&(u=this.#T(t.card));let v="";if(t.poll){let e="";for(let i in t.poll.options)e+="
  • "+t.poll.options[i].title+"
  • ";v='
      '+e+"
    "}let b="";if(!this.mtSettings.hideCounterBar){b='
    '+('
    '+h+"
    ")+('
    '+m+"
    ")+('
    '+d+"
    ")+"
    "}return'
    '+i+s+c+"
    "+p+g+u+v+b+"
    "}#N(t,e){function i(t,e){let i=e.replace(/\s+/g,"").toLowerCase();return!(!["src","href","xlink:href"].includes(t)||!i.includes("javascript:")&&!i.includes("data:"))||(!!t.startsWith("on")||void 0)}function s(t){let e=t.attributes;for(let{name:s,value:a}of e)i(s,a)&&t.removeAttribute(s)}let a=(new DOMParser).parseFromString(t,"text/html").body||document.createElement("body");return function(t){let e=t.querySelectorAll("script");for(let t of e)t.remove()}(a),function t(e){let i=e.children;for(let e of i)s(e),t(e)}(a),e?a.childNodes:a.innerHTML}#y(t){let e=t;return e=this.#N(e,!1),e=this.#M(e),this.mtSettings.hideEmojos||(e=this.#S(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#C(e,"

    >","

    ","

    ","

    ")),e}#M(t){let e=t.replaceAll('rel="tag"','rel="tag" target="_blank"');return e=e.replaceAll('class="u-url mention"','class="u-url mention" target="_blank"'),e}#C(t,e,i,s,a){if(t.includes(e)){const o=new RegExp(e+"(.*?)"+i,"gi");return t.replace(o,s+"$1"+a)}return t}#f(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#S(t,e){if(t.includes(":")){for(const i of e){const e=new RegExp(`\\:${i.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${i.shortcode}`)}return t}return t}#x(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateLocale,this.mtSettings.dateOptions).format(e)}#L(t,e=!1){const i=t.type,s=e;let a="";const o='";return"image"===i&&(a='
    '+(s?o:"")+''+(t.description?this.#f(t.description):
    '),"audio"===i&&(a=t.preview_url?'
    '+(s?o:"")+''+(t.description?this.#f(t.description):
    ':'
    '+(s?o:"")+'
    '),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
    '+(s?o:"")+'
    ':'
    '+(s?o:"")+''+(t.description?this.#f(t.description):
    '),a}#P(t,e){let i=document.createElement("dialog");i.id=t,i.classList.add("mt-dialog"),i.dataset.theme=this.mtContainerNode.getAttribute("data-theme"),i.innerHTML=e,document.body.prepend(i),i.showModal(),i.addEventListener("close",(()=>{document.body.removeChild(i)}))}#E(t){const e=Array.from(t.target.parentNode.parentNode.children).filter((t=>!t.classList.contains("mt-post-media-spoiler"))),i=e.indexOf(t.target.parentNode)+1;let s=[];e.forEach(((t,e)=>{let i="";i="gifv"===t.getAttribute("data-media-type")||"video"===t.getAttribute("data-media-type")?`\n \n `:`\n ${t.getAttribute(\n `;const a=`\n \n `;s.push(a)}));const a=`\n \n\n \n\n \n\n \n `;this.#P("mt-carousel",a),s.length>=2&&this.#k(e.length,i)}#k(t,e){let i=e;const s=document.getElementById("mt-carousel-scroll");let a=0,o=!1;const n=document.getElementById("mt-carousel-prev"),r=document.getElementById("mt-carousel-next"),l=(t,e="smooth")=>{document.getElementById("mt-carousel-"+t).scrollIntoView({behavior:e})};l(i,"instant");const d=()=>{clearTimeout(a),a=setTimeout((()=>{o&&(i=(()=>{const t=(s.scrollLeft+s.clientWidth)/s.clientWidth;return Math.round(t+Number.EPSILON)})(),m()),o=!0}),60)};s.addEventListener("scroll",d);const m=()=>{n.hidden=1===i,r.hidden=i===t},h=e=>{const s=e.target.closest("button")?.id;"mt-carousel-next"===s?(o=!1,++i,i>t&&(i=t),l(i),m()):"mt-carousel-prev"===s&&(o=!1,--i,i<1&&(i=1),l(i),m()),"mt-carousel-close"===s&&p()};document.addEventListener("click",h);const c=t=>{"Escape"!==t.key&&27!==t.keyCode||p()};document.addEventListener("keydown",c);const p=()=>{s.removeEventListener("scroll",d),document.removeEventListener("click",h),document.removeEventListener("keydown",c)}}#$(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#_(t){const e=t.target,i=e.nextSibling;e.textContent==this.mtSettings.btnShowMore?(i.classList.remove("spoiler-txt-hidden"),i.classList.add("spoiler-txt-visible"),e.setAttribute("aria-expanded","true"),e.textContent=this.mtSettings.btnShowLess):(i.classList.remove("spoiler-txt-visible"),i.classList.add("spoiler-txt-hidden"),e.setAttribute("aria-expanded","false"),e.textContent=this.mtSettings.btnShowMore)}#H(t){const e=t.target;e.classList.contains("mt-btn-spoiler-media-show")?e.parentNode.classList.remove("mt-post-media-spoiler"):e.parentNode.classList.add("mt-post-media-spoiler")}#T(t){let e="";if("0"!==this.mtSettings.previewMaxLines&&t.description){e=''+this.#A(t.description)+""}return''+(t.image?'
    '+this.#f(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#A(t.provider_name)+"":"")+''+t.title+""+e+(t.author_name?''+this.#A(t.author_name)+"":"")+"
    "}#A(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#b(){let t="",e="";if(this.mtSettings.btnSeeMore){let e="";"profile"===this.mtSettings.timelineType?this.mtSettings.profileName?e=this.mtSettings.profileName:this.#n("Please check your profileName value","âš ī¸"):"hashtag"===this.mtSettings.timelineType?e="tags/"+this.mtSettings.hashtagName:"local"===this.mtSettings.timelineType&&(e="public/local"),t=`\n \n ${this.mtSettings.btnSeeMore}\n `}if(this.mtSettings.btnReload&&(e=`\n `),this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'"),this.mtSettings.btnReload){this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}#v(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||this.mtSettings.disableCarousel&&"image"===s.getAttribute("data-media-type"))&&this.#B(t),e.classList.contains("mt-btn-spoiler-txt")&&this.#_(t),e.classList.contains("mt-btn-spoiler-media")&&this.#H(t),this.mtSettings.disableCarousel||"img"!=i||"image"!==s.getAttribute("data-media-type")&&"audio"!==s.getAttribute("data-media-type")||this.#E(t),("mt-btn-play"==e.className||"svg"==i&&"mt-btn-play"==s.className||"path"==i&&"mt-btn-play"==s.parentNode.className||"img"==i&&("video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")))&&this.#$(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#B(t)}))}#B(t){const e=t.target.closest(".mt-post").dataset.location;"a"!==t.target.localName&&"span"!==t.target.localName&&"button"!==t.target.localName&&"bdi"!==t.target.localName&&"time"!==t.target.localName&&!t.target.classList.contains("mt-post-media-spoiler")&&"mt-post-preview-noImage"!==t.target.className&&"mt-post-avatar-image-big"!==t.target.parentNode.className&&"mt-post-avatar-image-small"!==t.target.parentNode.className&&"mt-post-header-user-name"!==t.target.parentNode.className&&"mt-post-preview-image"!==t.target.parentNode.className&&"mt-post-preview"!==t.target.parentNode.className&&e&&window.open(e,"_blank","noopener")}#p(){const t=e=>{e.target.parentNode.classList.remove(this.mtSettings.spinnerClass),e.target.removeEventListener("load",t),e.target.removeEventListener("error",t)};this.mtBodyNode.querySelectorAll(`.${this.mtSettings.spinnerClass} > img`).forEach((e=>{e.addEventListener("load",t),e.addEventListener("error",t)}))}#n(t,e){const i=e||"❌";throw this.mtBodyNode.innerHTML=`\n
    \n ${i}\n Oops, something's happened:\n
    ${t}
    \n
    `,this.mtBodyNode.setAttribute("role","none"),new Error("Stopping the script due to an error building the timeline.")}}export{t as Init}; +class t{constructor(t={}){this.defaultSettings={mtContainerId:"mt-container",instanceUrl:"https://mastodon.social",timelineType:"local",userId:"",profileName:"",hashtagName:"",spinnerClass:"mt-loading-spinner",defaultTheme:"auto",maxNbPostFetch:"20",maxNbPostShow:"20",dateFormatLocale:"en-GB",dateFormatOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,txtMaxLines:"",filterByLanguage:"",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",markdownBlockquote:!1,hideEmojos:!1,btnShowContent:"SHOW CONTENT",hideVideoPreview:!1,btnPlayVideoTxt:"Load and play video",hidePreviewLink:!1,previewMaxLines:"",hideCounterBar:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.#t(),this.linkHeader={},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#e((()=>{this.#i()}))}#t(){Number(this.mtSettings.maxNbPostShow)>Number(this.mtSettings.maxNbPostFetch)&&(console.error(`Please check your settings! The maximum number of posts to show is bigger than the maximum number of posts to fetch. Changing the value of "maxNbPostFetch" to: ${this.mtSettings.maxNbPostShow}`),this.mtSettings.maxNbPostFetch=this.mtSettings.maxNbPostShow)}#e(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#i(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#a("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),i=()=>{if(document.getElementById(this.mtSettings.mtContainerId))t();else{performance.now()-e container with id: "${this.mtSettings.mtContainerId}" after several attempts for ${this.mtSettings.insistSearchContainerTime/1e3} seconds`)}};i()}else document.getElementById(this.mtSettings.mtContainerId)?t():console.error(`Impossible to find the
    container with id: "${this.mtSettings.mtContainerId}". Please try to add the option 'insistSearchContainer: true' when initializing the script`)}mtUpdate(){this.#e((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
    '),this.#a("updateTimeline")}))}mtColorTheme(t){this.#e((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#s(){if("auto"===this.mtSettings.defaultTheme){let t=window.matchMedia("(prefers-color-scheme: dark)");t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light"),t.addEventListener("change",(t=>{t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light")}))}else this.mtColorTheme(this.mtSettings.defaultTheme)}#n(){return new Promise(((t,e)=>{const i=this.mtSettings.instanceUrl?`${this.mtSettings.instanceUrl}/api/v1/`:this.#o("Please check your instanceUrl value","âš ī¸"),s=this.#r(i),a=Object.entries(s).map((([t,i])=>{const s="timeline"===t;return this.#l(i,s).then((e=>({[t]:e}))).catch((s=>(e(new Error(`Something went wrong fetching data from: ${i}`)),this.#o(s.message),{[t]:[]})))}));Promise.all(a).then((async e=>{if(this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),!this.mtSettings.hidePinnedPosts&&void 0!==this.fetchedData.pinned?.length&&0!==this.fetchedData.pinned.length){const t=this.fetchedData.pinned.map((t=>({...t,pinned:!0})));this.fetchedData.timeline=[...t,...this.fetchedData.timeline]}if(this.#d())t();else{do{await this.#m()}while(!this.#d()&&this.linkHeader.next);t()}}))}))}#r(t){const{timelineType:e,userId:i,hashtagName:s,maxNbPostFetch:a,hidePinnedPosts:n,hideEmojos:o}=this.mtSettings,r={};switch(e){case"profile":if(!i){this.#o("Please check your userId value","âš ī¸");break}r.timeline=`${t}accounts/${i}/statuses?limit=${a}`,n||(r.pinned=`${t}accounts/${i}/statuses?pinned=true`);break;case"hashtag":if(!s){this.#o("Please check your hashtagName value","âš ī¸");break}r.timeline=`${t}timelines/tag/${s}?limit=${a}`;break;case"local":r.timeline=`${t}timelines/public?local=true&limit=${a}`;break;default:this.#o("Please check your timelineType value","âš ī¸")}return o||(r.emojos=`${t}custom_emojis`),r}async#l(t,e=!1){const i=await fetch(t);if(!i.ok)throw new Error(`\n Failed to fetch the following Url:
    ${t}
    Error status: ${i.status}
    Error message: ${i.statusText}\n `);const s=await i.json();return e&&i.headers.get("Link")&&(this.linkHeader=this.#h(i.headers.get("Link"))),s}#d(){return this.fetchedData.timeline.length>=Number(this.mtSettings.maxNbPostFetch)}#m(){return new Promise((t=>{this.linkHeader.next?this.#l(this.linkHeader.next,!0).then((e=>{this.fetchedData.timeline=[...this.fetchedData.timeline,...e],t()})):t()}))}#h(t){const e=t.split(", ").map((t=>t.split("; "))).map((t=>[t[1].replace(/"/g,"").replace("rel=",""),t[0].slice(1,-1)]));return Object.fromEntries(e)}async#a(t){await this.#n();const{hideUnlisted:e,hideReblog:i,hideReplies:s,maxNbPostShow:a,filterByLanguage:n}=this.mtSettings,o=this.fetchedData.timeline;this.mtBodyNode.replaceChildren();if(o.filter((t=>{const a="public"===t.visibility||!e&&"unlisted"===t.visibility,o=i&&t.reblog,r=s&&t.in_reply_to_id,l=t.language||(t.reblog?t.reblog.language:null);return a&&!o&&!r&&(""===n||l===n)})).forEach(((t,e)=>{e${o?.length||0} posts have been fetched from the server
    This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;this.#o(t,"📭")}}#g(){"0"!==this.mtSettings.txtMaxLines&&0!==this.mtSettings.txtMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines),"0"!==this.mtSettings.previewMaxLines&&0!==this.mtSettings.previewMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-preview-max-lines",this.mtSettings.previewMaxLines)}#u(t){const e=this.mtBodyNode.getElementsByTagName("article");for(let i=0;i
    '+this.#f(h)+' avatar
    '+(i?'
    '+this.#f(t.account.username)+' avatar
    ':"")+"
    ",u='
    '+(!this.mtSettings.hideEmojos&&c?this.#S(c,p):c||h)+""+(this.mtSettings.hideUserAccount?"":'
    ")+"
    ",v=this.#x(n),b='
    '+(t.pinned?"...":"")+'"+(t.edited_at?" *":"")+"
    ",w="0"!==this.mtSettings.txtMaxLines?" truncate":"";let f="";const S=s.spoiler_text?s.spoiler_text:s.content;S&&(f='
    '+this.#y(S)+"
    ");const x=[...t.media_attachments,...t.reblog?.media_attachments||[]].map((t=>this.#L(t,s.sensitive))).join("");return'
    '+g+u+b+"
    "+f+(x?`
    ${x}
    `:"")+(!this.mtSettings.hidePreviewLink&&t.card?this.#T(t.card):"")+(t.poll?'
      '+t.poll.options.map((function(t){return"
    • "+t.title+"
    • "})).join("")+"
    ":"")+(this.mtSettings.hideCounterBar?"":'
    '+this.#N("replies",o)+this.#N("reblog",r)+this.#N("favorites",l)+"
    ")+"
    "}#N(t,e){return`
    ${{replies:'',reblog:'',favorites:''}[t]}${e}
    `}#M(t,e){function i(t,e){let i=e.replace(/\s+/g,"").toLowerCase();return!(!["src","href","xlink:href"].includes(t)||!i.includes("javascript:")&&!i.includes("data:"))||(!!t.startsWith("on")||void 0)}function s(t){let e=t.attributes;for(let{name:s,value:a}of e)i(s,a)&&t.removeAttribute(s)}let a=(new DOMParser).parseFromString(t,"text/html").body||document.createElement("body");return function(t){let e=t.querySelectorAll("script");for(let t of e)t.remove()}(a),function t(e){let i=e.children;for(let e of i)s(e),t(e)}(a),e?a.childNodes:a.innerHTML}#y(t){let e=t;return e=this.#M(e,!1),e=this.#C(e),this.mtSettings.hideEmojos||(e=this.#S(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#P(e,"

    >","

    ","

    ","

    ")),e}#C(t){let e=t.replaceAll('rel="tag"','rel="tag" target="_blank"');return e=e.replaceAll('class="u-url mention"','class="u-url mention" target="_blank"'),e}#P(t,e,i,s,a){if(t.includes(e)){const n=new RegExp(e+"(.*?)"+i,"gi");return t.replace(n,s+"$1"+a)}return t}#f(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#S(t,e){if(t.includes(":")){for(const i of e){const e=new RegExp(`\\:${i.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${i.shortcode}`)}return t}return t}#x(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateFormatLocale,this.mtSettings.dateFormatOptions).format(e)}#L(t,e=!1){const i=t.type,s=e;let a="";const n='";return"image"===i&&(a='
    '+(s?n:"")+''+(t.description?this.#f(t.description):
    '),"audio"===i&&(a=t.preview_url?'
    '+(s?n:"")+''+(t.description?this.#f(t.description):
    ':'
    '+(s?n:"")+'
    '),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
    '+(s?n:"")+'
    ':'
    '+(s?n:"")+''+(t.description?this.#f(t.description):
    '),a}#k(t,e){let i=document.createElement("dialog");i.id=t,i.classList.add("mt-dialog"),i.dataset.theme=this.mtContainerNode.getAttribute("data-theme"),i.innerHTML=e,document.body.prepend(i),i.showModal(),i.addEventListener("close",(()=>{document.body.removeChild(i)}))}#E(t){const e=Array.from(t.target.parentNode.parentNode.children).filter((t=>!t.classList.contains("mt-post-media-spoiler"))),i=e.indexOf(t.target.parentNode)+1;let s=[];e.forEach(((t,e)=>{let i="";i="gifv"===t.getAttribute("data-media-type")||"video"===t.getAttribute("data-media-type")?`\n \n `:`\n ${t.getAttribute(\n `;const a=`\n \n `;s.push(a)}));const a=`\n \n\n \n\n \n\n \n `;this.#k("mt-carousel",a),s.length>=2&&this.#$(e.length,i)}#$(t,e){let i=e;const s=document.getElementById("mt-carousel-scroll");let a=0,n=!1;const o=document.getElementById("mt-carousel-prev"),r=document.getElementById("mt-carousel-next"),l=(t,e="smooth")=>{document.getElementById("mt-carousel-"+t).scrollIntoView({behavior:e})};l(i,"instant");const d=()=>{clearTimeout(a),a=setTimeout((()=>{n&&(i=(()=>{const t=(s.scrollLeft+s.clientWidth)/s.clientWidth;return Math.round(t+Number.EPSILON)})(),m()),n=!0}),60)};s.addEventListener("scroll",d);const m=()=>{o.hidden=1===i,r.hidden=i===t},h=e=>{const s=e.target.closest("button")?.id;"mt-carousel-next"===s?(n=!1,++i,i>t&&(i=t),l(i),m()):"mt-carousel-prev"===s&&(n=!1,--i,i<1&&(i=1),l(i),m()),"mt-carousel-close"===s&&p()};document.addEventListener("click",h);const c=t=>{"Escape"!==t.key&&27!==t.keyCode||p()};document.addEventListener("keydown",c);const p=()=>{s.removeEventListener("scroll",d),document.removeEventListener("click",h),document.removeEventListener("keydown",c)}}#B(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#A(t){const e=t.target,i=e.nextSibling;e.textContent==this.mtSettings.btnShowMore?(i.classList.remove("spoiler-txt-hidden"),i.classList.add("spoiler-txt-visible"),e.setAttribute("aria-expanded","true"),e.textContent=this.mtSettings.btnShowLess):(i.classList.remove("spoiler-txt-visible"),i.classList.add("spoiler-txt-hidden"),e.setAttribute("aria-expanded","false"),e.textContent=this.mtSettings.btnShowMore)}#H(t){const e=t.target;e.classList.contains("mt-btn-spoiler-media-show")?e.parentNode.classList.remove("mt-post-media-spoiler"):e.parentNode.classList.add("mt-post-media-spoiler")}#T(t){let e="";if("0"!==this.mtSettings.previewMaxLines&&t.description){e=''+this.#_(t.description)+""}return''+(t.image?'
    '+this.#f(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#_(t.provider_name)+"":"")+''+t.title+""+e+(t.author_name?''+this.#_(t.author_name)+"":"")+"
    "}#_(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#b(){let t="",e="";if(this.mtSettings.btnSeeMore){let e="";"profile"===this.mtSettings.timelineType?this.mtSettings.profileName?e=this.mtSettings.profileName:this.#o("Please check your profileName value","âš ī¸"):"hashtag"===this.mtSettings.timelineType?e="tags/"+this.mtSettings.hashtagName:"local"===this.mtSettings.timelineType&&(e="public/local"),t=`\n \n ${this.mtSettings.btnSeeMore}\n `}if(this.mtSettings.btnReload&&(e=`\n `),this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'"),this.mtSettings.btnReload){this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}#v(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||this.mtSettings.disableCarousel&&"image"===s.getAttribute("data-media-type"))&&this.#D(t),e.classList.contains("mt-btn-spoiler-txt")&&this.#A(t),e.classList.contains("mt-btn-spoiler-media")&&this.#H(t),this.mtSettings.disableCarousel||"img"!=i||"image"!==s.getAttribute("data-media-type")&&"audio"!==s.getAttribute("data-media-type")||this.#E(t),("mt-btn-play"==e.className||"svg"==i&&"mt-btn-play"==s.className||"path"==i&&"mt-btn-play"==s.parentNode.className||"img"==i&&("video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")))&&this.#B(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#D(t)}))}#D(t){const e=t.target.closest(".mt-post")?.dataset.location;if(!e)return;const i=t.target.localName;if("a"===i||"span"===i||"button"===i||"bdi"===i||"time"===i)return;const s=t.target.className;if("mt-post-media-spoiler"===s||"mt-post-preview-noImage"===s)return;const a=t.target.parentNode?.className;"mt-post-avatar-image-big"!==a&&"mt-post-avatar-image-small"!==a&&"mt-post-header-user-name"!==a&&"mt-post-preview-image"!==a&&"mt-post-preview"!==a&&window.open(e,"_blank","noopener")}#p(){const t=e=>{e.target.parentNode.classList.remove(this.mtSettings.spinnerClass),e.target.removeEventListener("load",t),e.target.removeEventListener("error",t)};this.mtBodyNode.querySelectorAll(`.${this.mtSettings.spinnerClass} > img`).forEach((e=>{e.addEventListener("load",t),e.addEventListener("error",t)}))}#o(t,e){const i=e||"❌";throw this.mtBodyNode.innerHTML=`\n
    \n ${i}\n Oops, something's happened:\n
    ${t}
    \n
    `,this.mtBodyNode.setAttribute("role","none"),new Error("Stopping the script due to an error building the timeline.")}}export{t as Init}; diff --git a/dist/mastodon-timeline.min.css b/dist/mastodon-timeline.min.css index d994382..080ede4 100644 --- a/dist/mastodon-timeline.min.css +++ b/dist/mastodon-timeline.min.css @@ -1 +1 @@ -.mt-container,.mt-container[data-theme=light],.mt-dialog,.mt-dialog[data-theme=light]{--mt-txt-max-lines:none;--mt-preview-max-lines:none;--mt-color-bg:#fff;--mt-color-bg-hover:#d9e1e8;--mt-color-line-gray:#c0cdd9;--mt-color-contrast-gray:#606984;--mt-color-content-txt:#000;--mt-color-link:#3a3bff;--mt-color-error-txt:#8b0000;--mt-color-btn-bg:#6364ff;--mt-color-btn-bg-hover:#563acc;--mt-color-btn-txt:#fff;--mt-color-backdrop:#00000090;--mt-color-placeholder:#60698425}.mt-container[data-theme=dark],.mt-dialog[data-theme=dark]{--mt-color-bg:#282c37;--mt-color-bg-hover:#313543;--mt-color-line-gray:#393f4f;--mt-color-contrast-gray:#606984;--mt-color-content-txt:#fff;--mt-color-link:#8c8dff;--mt-color-error-txt:#fe6c6c}.mt-container button,.mt-dialog button{font:inherit}.mt-container a,.mt-container button,.mt-dialog button{cursor:pointer}.mt-container{display:flex;flex-direction:column;height:100%;overflow-y:auto;position:relative;background-color:var(--mt-color-bg);scrollbar-color:var(--mt-color-contrast-gray) var(--mt-color-bg);scrollbar-width:auto;container:mt-container/inline-size}.mt-container::-webkit-scrollbar{width:.25rem;height:.25rem}.mt-container::-webkit-scrollbar-thumb{background-color:var(--mt-color-contrast-gray);border:none;border-radius:3rem}.mt-container::-webkit-scrollbar-thumb:active,.mt-container::-webkit-scrollbar-thumb:hover{background-color:var(--mt-color-contrast-gray)}.mt-container::-webkit-scrollbar-track{background-color:var(--mt-color-bg);border:none;border-radius:0}.mt-container::-webkit-scrollbar-corner,.mt-container::-webkit-scrollbar-track:active,.mt-container::-webkit-scrollbar-track:hover{background-color:var(--mt-color-bg)}.mt-container a,.mt-container a:active,.mt-container a:link{text-decoration:none;color:var(--mt-color-link)}.mt-container a:not(.mt-post-preview):hover{text-decoration:underline}.mt-body{padding:1rem;white-space:pre-wrap;word-wrap:break-word;margin-bottom:1rem}.mt-body .invisible{font-size:0;line-height:0;display:inline-block;width:0;height:0;position:absolute}.mt-post{margin:.25rem;padding:1rem .5rem;position:relative;min-height:3.75rem;background-color:transparent;border-bottom:1px solid var(--mt-color-line-gray)}.mt-post:focus,.mt-post:hover{cursor:pointer;background-color:var(--mt-color-bg-hover)}.mt-post p:last-child{margin-bottom:0}.mt-post-avatar{margin-right:.75rem}.mt-post-avatar-standard{width:2.25rem;height:2.25rem}.mt-post-avatar-boosted{width:3rem;height:3rem;position:relative}.mt-post-avatar-image-big img{aspect-ratio:1/1;width:2.25rem;height:2.25rem;border-radius:.25rem;overflow:hidden}.mt-post-avatar-image-small img{aspect-ratio:1/1;width:1.5rem;height:1.5rem;top:1.5rem;left:1.5rem;position:absolute;border-radius:.25rem;overflow:hidden}.mt-post-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.mt-post-header-user{overflow:hidden;padding-right:.75rem}.mt-post-header-user .mt-custom-emoji{height:1rem;min-width:1rem;width:auto}.mt-post-header-user>a{color:var(--mt-color-content-txt)!important;overflow-wrap:anywhere}.mt-container .mt-post-header-user>a:hover{text-decoration:none}.mt-post-header-user-name{font-weight:600}.mt-container .mt-post-header-user:hover .mt-post-header-user-name{text-decoration:underline}.mt-post-header-user-account{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--mt-color-contrast-gray)}.mt-post-header-date{display:flex;font-size:.75rem;text-align:right;margin-left:auto}.mt-post-header-date .mt-post-pinned{width:1.25rem;margin-top:-.25rem;fill:var(--mt-color-contrast-gray)}.mt-container .mt-post-header-date>a{white-space:nowrap;color:var(--mt-color-contrast-gray)!important}.mt-post-txt{margin-bottom:1rem;color:var(--mt-color-content-txt)}.mt-post-txt .spoiler-txt-hidden{display:none}.mt-post-txt.truncate{display:-webkit-box;overflow:hidden;-webkit-line-clamp:var(--mt-txt-max-lines);-webkit-box-orient:vertical}.mt-post-txt:not(.truncate) .ellipsis::after{content:"..."}.mt-post-txt blockquote{border-left:.25rem solid var(--mt-color-line-gray);margin-left:0;padding-left:.5rem}.mt-post-txt .mt-custom-emoji{height:1.5rem;min-width:1.5rem;margin-bottom:-.25rem;width:auto}.mt-post-poll{margin-bottom:1rem;color:var(--mt-color-content-txt)}.mt-post-poll ul{list-style:none;padding:0;margin:0}.mt-post-poll ul li{font-size:.9rem;margin-bottom:.5rem}.mt-post-poll.mt-post-poll-expired ul li{color:var(--mt-color-contrast-gray)}.mt-post-poll ul li:not(:last-child){margin-bottom:.25rem}.mt-post-poll ul li:before{content:"◯";padding-right:.5rem}.mt-post-poll.mt-post-poll-expired ul li:before{content:"";padding-right:0}.mt-post-media-wrapper{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem;margin-bottom:1rem}.mt-post-media{position:relative;overflow:hidden;width:100%}.mt-post-media-wrapper:has(> :last-child:nth-child(odd)) .mt-post-media:not(:only-child):first-child{grid-column:1/3}.mt-post-media:only-child{grid-column:1/3}.mt-post-media-spoiler>.mt-btn-play,.mt-post-media-spoiler>audio,.mt-post-media-spoiler>img,.mt-post-media-spoiler>video{filter:blur(2rem);pointer-events:none}.mt-post-media,.mt-post-media-spoiler>audio,.mt-post-media-spoiler>img,.mt-post-media>img,.mt-post-media>video{border-radius:.5rem}.mt-post-media>audio{width:100%;position:relative;z-index:1}.mt-post-media>img,.mt-post-media>video{width:100%;height:100%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);object-fit:cover;text-align:center;color:var(--mt-color-content-txt);background-color:var(--mt-color-placeholder)}body:has(dialog.mt-dialog[open]){overflow:hidden}.mt-dialog{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;border:none;color:var(--mt-color-content-txt);background-color:transparent;padding:0;margin:1rem;overflow:hidden}.mt-dialog::backdrop{background-color:var(--mt-color-backdrop);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.mt-carousel-header{position:absolute;top:0;right:0;z-index:2}.mt-carousel-body{width:100%;height:100%}.mt-carousel-scroll{display:flex;width:100%;height:100%;margin:0;padding:0;list-style:none;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;scrollbar-width:none}.mt-carousel-scroll::-webkit-scrollbar{display:none;-webkit-appearance:none}.mt-carousel-item{scroll-snap-align:center;width:100%;height:100%}.mt-carousel-media-wrapper{width:calc(100vw - 2.5rem);height:100%;display:flex;align-items:center;justify-content:center;text-align:center}.mt-carousel-media{width:100%;height:100%;object-fit:contain;padding:2rem}.mt-carousel-next,.mt-carousel-prev{position:absolute;background-color:transparent;border:none;padding:.5rem;z-index:2}.mt-carousel-prev{left:0;padding-left:0}.mt-carousel-next{right:0;padding-right:0}.mt-post-preview{min-height:4rem;display:flex;flex-direction:row;border:1px solid var(--mt-color-line-gray);border-radius:.5rem;color:var(--mt-color-link);font-size:.8rem;margin:1rem 0;overflow:hidden}.mt-post-preview-image{width:40%;align-self:stretch}.mt-post-preview-image img{display:block;width:100%;height:100%;object-fit:cover;color:var(--mt-color-content-txt)}.mt-post-preview-noImage{width:40%;font-size:1.5rem;align-self:center;text-align:center}.mt-post-preview-content{width:60%;display:flex;align-self:center;flex-direction:column;padding:.5rem 1rem;gap:.5rem}.mt-post-preview-content:has(.mt-post-preview-description.truncate){align-self:unset}.mt-post-preview-description{display:block;color:var(--mt-color-contrast-gray)}.mt-post-preview-description.truncate{display:-webkit-box;overflow:hidden;-webkit-line-clamp:var(--mt-preview-max-lines);-webkit-box-orient:vertical}.mt-post-preview-description:not(.truncate) .ellipsis::after{content:"..."}.mt-post-preview-title{font-weight:600}.mt-post-counter-bar{display:flex;min-width:6rem;max-width:40rem;justify-content:space-between;color:var(--mt-color-contrast-gray)}.mt-post-counter-bar-favorites,.mt-post-counter-bar-reblog,.mt-post-counter-bar-replies{display:flex;font-size:.75rem;gap:.25rem;align-items:center;opacity:.5}.mt-post-counter-bar-favorites>svg,.mt-post-counter-bar-reblog>svg,.mt-post-counter-bar-replies>svg{width:1rem;fill:var(--mt-color-contrast-gray)}.mt-btn-play{display:flex;position:absolute;width:3rem;height:3rem;top:calc(50% - 1.5rem);left:calc(50% - 1.5rem);justify-content:center;align-items:center;background-color:transparent;border:none;cursor:pointer}.mt-btn-play>svg{width:2.5rem;height:2.5rem;fill:var(--mt-color-bg);stroke:var(--mt-color-content-txt);stroke-width:1px}.mt-post-media.mt-loading-spinner .mt-btn-play{display:none}.mt-container .mt-btn-dark,.mt-dialog .mt-btn-dark{display:flex;border-radius:.25rem;background-color:var(--mt-color-line-gray);border:0;color:var(--mt-color-content-txt);font-weight:600;font-size:.75rem;text-align:center;padding:.25rem .5rem;line-height:1.25rem;vertical-align:top}.mt-dialog .mt-btn-dark{margin-left:auto}.mt-container .mt-btn-violet,.mt-container a.mt-btn-violet,.mt-dialog .mt-btn-violet,.mt-dialog a.mt-btn-violet{display:flex;align-items:center;gap:.5rem;border-radius:.25rem;border:.5rem;padding:.5rem .75rem;font-size:1rem;font-weight:600;text-align:center;background-color:var(--mt-color-btn-bg);color:var(--mt-color-btn-txt)}.mt-container .mt-btn-violet:hover,.mt-container a.mt-btn-violet:hover,.mt-dialog .mt-btn-violet:hover,.mt-dialog a.mt-btn-violet:hover{background-color:var(--mt-color-btn-bg-hover);text-decoration:none}.mt-post-txt .mt-btn-spoiler-txt{display:inline-block;vertical-align:middle}.mt-post-media.mt-loading-spinner>.mt-btn-spoiler-media{display:none}.mt-post-media>.mt-btn-spoiler-media-show{position:absolute;top:50%;left:50%;z-index:2;transform:translate(-50%,-50%)}.mt-post-media.mt-post-media-spoiler>.mt-btn-spoiler-media-hide,.mt-post-media:not([data-media-width-hd])>.mt-btn-spoiler-media-hide{display:none}.mt-post-media:not(.mt-post-media-spoiler)>.mt-btn-spoiler-media-show{display:none}.mt-post-media>.mt-btn-spoiler-media-hide{position:absolute;top:.5rem;left:.5rem;z-index:2}.mt-post-media>.mt-btn-spoiler-media-hide>svg{fill:var(--mt-color-content-txt);pointer-events:none}.mt-error{position:absolute;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;height:calc(100% - 3.5rem);width:calc(100% - 4.5rem);justify-content:center;align-items:center;color:var(--mt-color-error-txt);padding:.75rem;text-align:center}.mt-error-icon{font-size:2rem;margin-bottom:1rem}.mt-error-message{width:100%;padding:1rem 0}.mt-error-message hr{color:var(--mt-color-line-gray)}.mt-body>.mt-loading-spinner{position:absolute;width:3rem;height:3rem;margin:auto;top:calc(50% - 1.5rem);right:calc(50% - 1.5rem)}.mt-loading-spinner{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'%3E%3Cg%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 64 64' to='360 64 64' dur='1000ms' repeatCount='indefinite'/%3E%3Cpath d='M64 6.69a57.3 57.3 0 1 1 0 114.61A57.3 57.3 0 0 1 6.69 64' fill='none' stroke='%23404040' stroke-width='12'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center center;background-color:transparent;background-size:min(2.5rem,calc(100% - .5rem))}.mt-footer{display:flex;flex-flow:wrap;margin:auto auto 2rem auto;padding:0 1rem;gap:1.5rem;align-items:center;justify-content:center}.visually-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}@supports (container-type:inline-size){@container mt-container (max-width:20rem){.mt-body{padding:0 .5rem}.mt-container .mt-post-header-date>a{white-space:normal}.mt-post-media-wrapper .mt-post-media{grid-column:1/3}}}@supports not (container-type:inline-size){@media screen and (max-width:clamp(20rem,40rem,60rem)){.mt-body{padding:0 .5rem}.mt-container .mt-post-header-date>a{white-space:normal}.mt-post-media-wrapper .mt-post-media{grid-column:1/3}}} \ No newline at end of file +.mt-container,.mt-container[data-theme=light],.mt-dialog,.mt-dialog[data-theme=light]{--mt-txt-max-lines:none;--mt-preview-max-lines:none;--mt-color-bg:#fff;--mt-color-bg-hover:#d9e1e8;--mt-color-line-gray:#c0cdd9;--mt-color-contrast-gray:#606984;--mt-color-content-txt:#000;--mt-color-link:#3a3bff;--mt-color-error-txt:#8b0000;--mt-color-btn-bg:#6364ff;--mt-color-btn-bg-hover:#563acc;--mt-color-btn-txt:#fff;--mt-color-backdrop:#00000090;--mt-color-placeholder:#60698425}.mt-container[data-theme=dark],.mt-dialog[data-theme=dark]{--mt-color-bg:#282c37;--mt-color-bg-hover:#313543;--mt-color-line-gray:#393f4f;--mt-color-contrast-gray:#606984;--mt-color-content-txt:#fff;--mt-color-link:#8c8dff;--mt-color-error-txt:#fe6c6c}.mt-container button,.mt-dialog button{font:inherit}.mt-container a,.mt-container button,.mt-dialog button{cursor:pointer}.mt-container{display:flex;flex-direction:column;height:100%;overflow-y:auto;position:relative;background-color:var(--mt-color-bg);scrollbar-color:var(--mt-color-contrast-gray) var(--mt-color-bg);scrollbar-width:auto;container:mt-container/inline-size}.mt-container::-webkit-scrollbar{width:.25rem;height:.25rem}.mt-container::-webkit-scrollbar-thumb{background-color:var(--mt-color-contrast-gray);border:none;border-radius:3rem}.mt-container::-webkit-scrollbar-thumb:active,.mt-container::-webkit-scrollbar-thumb:hover{background-color:var(--mt-color-contrast-gray)}.mt-container::-webkit-scrollbar-track{background-color:var(--mt-color-bg);border:none;border-radius:0}.mt-container::-webkit-scrollbar-corner,.mt-container::-webkit-scrollbar-track:active,.mt-container::-webkit-scrollbar-track:hover{background-color:var(--mt-color-bg)}.mt-container a,.mt-container a:active,.mt-container a:link{text-decoration:none;color:var(--mt-color-link)}.mt-container a:not(.mt-post-preview):hover{text-decoration:underline}.mt-body{padding:1rem;white-space:pre-wrap;word-wrap:break-word;margin-bottom:1rem}.mt-body .invisible{font-size:0;line-height:0;display:inline-block;width:0;height:0;position:absolute}.mt-post{margin:.25rem;padding:1rem .5rem;position:relative;min-height:3.75rem;background-color:transparent;border-bottom:1px solid var(--mt-color-line-gray)}.mt-post:focus,.mt-post:hover{cursor:pointer;background-color:var(--mt-color-bg-hover)}.mt-post p:last-child{margin-bottom:0}.mt-post-avatar{margin-right:.75rem}.mt-post-avatar-standard{width:2.25rem;height:2.25rem}.mt-post-avatar-boosted{width:3rem;height:3rem;position:relative}.mt-post-avatar-image-big img{aspect-ratio:1/1;width:2.25rem;height:2.25rem;border-radius:.25rem;overflow:hidden}.mt-post-avatar-image-small img{aspect-ratio:1/1;width:1.5rem;height:1.5rem;top:1.5rem;left:1.5rem;position:absolute;border-radius:.25rem;overflow:hidden}.mt-post-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.mt-post-header-user{overflow:hidden;padding-right:.75rem}.mt-post-header-user .mt-custom-emoji{height:1rem;min-width:1rem;width:auto}.mt-post-header-user>a{color:var(--mt-color-content-txt)!important;overflow-wrap:anywhere}.mt-container .mt-post-header-user>a:hover{text-decoration:none}.mt-post-header-user-name{font-weight:600}.mt-container .mt-post-header-user:hover .mt-post-header-user-name{text-decoration:underline}.mt-post-header-user-account{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--mt-color-contrast-gray)}.mt-post-header-date{display:flex;font-size:.75rem;text-align:right;margin-left:auto}.mt-post-header-date .mt-post-pinned{width:1.25rem;margin-top:-.25rem;fill:var(--mt-color-contrast-gray)}.mt-container .mt-post-header-date>a{white-space:nowrap;color:var(--mt-color-contrast-gray)!important}.mt-post-txt{margin-bottom:1rem;color:var(--mt-color-content-txt)}.mt-post-txt .spoiler-txt-hidden{display:none}.mt-post-txt.truncate{display:-webkit-box;overflow:hidden;-webkit-line-clamp:var(--mt-txt-max-lines);-webkit-box-orient:vertical}.mt-post-txt:not(.truncate) .ellipsis::after{content:"..."}.mt-post-txt blockquote{border-left:.25rem solid var(--mt-color-line-gray);margin-left:0;padding-left:.5rem}.mt-post-txt .mt-custom-emoji{height:1.5rem;min-width:1.5rem;margin-bottom:-.25rem;width:auto}.mt-post-poll{margin-bottom:1rem;color:var(--mt-color-content-txt)}.mt-post-poll ul{list-style:none;padding:0;margin:0}.mt-post-poll ul li{font-size:.9rem;margin-bottom:.5rem}.mt-post-poll.mt-post-poll-expired ul li{color:var(--mt-color-contrast-gray)}.mt-post-poll ul li:not(:last-child){margin-bottom:.25rem}.mt-post-poll ul li:before{content:"◯";padding-right:.5rem}.mt-post-poll.mt-post-poll-expired ul li:before{content:"";padding-right:0}.mt-post-media-wrapper{display:grid;grid-template-columns:repeat(2,1fr);gap:.5rem;margin-bottom:1rem}.mt-post-media{position:relative;overflow:hidden;width:100%}.mt-post-media-wrapper:has(> :last-child:nth-child(odd)) .mt-post-media:not(:only-child):first-child{grid-column:1/3}.mt-post-media:only-child{grid-column:1/3}.mt-post-media-spoiler>.mt-btn-play,.mt-post-media-spoiler>audio,.mt-post-media-spoiler>img,.mt-post-media-spoiler>video{filter:blur(2rem);pointer-events:none}.mt-post-media,.mt-post-media-spoiler>audio,.mt-post-media-spoiler>img,.mt-post-media>img,.mt-post-media>video{border-radius:.5rem}.mt-post-media>audio{width:100%;position:relative;z-index:1}.mt-post-media>img,.mt-post-media>video{width:100%;height:100%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);object-fit:cover;text-align:center;color:var(--mt-color-content-txt);background-color:var(--mt-color-placeholder)}body:has(dialog.mt-dialog[open]){overflow:hidden}.mt-dialog{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;border:none;color:var(--mt-color-content-txt);background-color:transparent;padding:0;margin:1rem;overflow:hidden}.mt-dialog::backdrop{background-color:var(--mt-color-backdrop);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.mt-carousel-header{position:absolute;top:0;right:0;z-index:2}.mt-carousel-body{width:100%;height:100%}.mt-carousel-scroll{display:flex;width:100%;height:100%;margin:0;padding:0;list-style:none;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;scrollbar-width:none}.mt-carousel-scroll::-webkit-scrollbar{display:none;-webkit-appearance:none}.mt-carousel-item{scroll-snap-align:center;width:100%;height:100%}.mt-carousel-media-wrapper{width:calc(100vw - 2.5rem);height:100%;display:flex;align-items:center;justify-content:center;text-align:center}.mt-carousel-media{width:100%;height:100%;object-fit:contain;padding:2rem}.mt-carousel-next,.mt-carousel-prev{position:absolute;background-color:transparent;border:none;padding:.5rem;z-index:2}.mt-carousel-prev{left:0;padding-left:0}.mt-carousel-next{right:0;padding-right:0}.mt-post-preview{min-height:4rem;display:flex;flex-direction:row;border:1px solid var(--mt-color-line-gray);border-radius:.5rem;color:var(--mt-color-link);font-size:.8rem;margin:1rem 0;overflow:hidden}.mt-post-preview-image{width:40%;align-self:stretch}.mt-post-preview-image img{display:block;width:100%;height:100%;object-fit:cover;color:var(--mt-color-content-txt)}.mt-post-preview-noImage{width:40%;font-size:1.5rem;align-self:center;text-align:center}.mt-post-preview-content{width:60%;display:flex;align-self:center;flex-direction:column;padding:.5rem 1rem;gap:.5rem}.mt-post-preview-content:has(.mt-post-preview-description.truncate){align-self:unset}.mt-post-preview-description{display:block;color:var(--mt-color-contrast-gray)}.mt-post-preview-description.truncate{display:-webkit-box;overflow:hidden;-webkit-line-clamp:var(--mt-preview-max-lines);-webkit-box-orient:vertical}.mt-post-preview-description:not(.truncate) .ellipsis::after{content:"..."}.mt-post-preview-title{font-weight:600}.mt-post-counter-bar{display:flex;min-width:6rem;max-width:40rem;justify-content:space-between;color:var(--mt-color-contrast-gray)}.mt-post-counter-bar-favorites,.mt-post-counter-bar-reblog,.mt-post-counter-bar-replies{display:flex;font-size:.75rem;gap:.25rem;align-items:center;opacity:.5;cursor:default}.mt-post-counter-bar-favorites>svg,.mt-post-counter-bar-reblog>svg,.mt-post-counter-bar-replies>svg{width:1rem;fill:var(--mt-color-contrast-gray)}.mt-btn-play{display:flex;position:absolute;width:3rem;height:3rem;top:calc(50% - 1.5rem);left:calc(50% - 1.5rem);justify-content:center;align-items:center;background-color:transparent;border:none;cursor:pointer}.mt-btn-play>svg{width:2.5rem;height:2.5rem;fill:var(--mt-color-bg);stroke:var(--mt-color-content-txt);stroke-width:1px}.mt-post-media.mt-loading-spinner .mt-btn-play{display:none}.mt-container .mt-btn-dark,.mt-dialog .mt-btn-dark{display:flex;border-radius:.25rem;background-color:var(--mt-color-line-gray);border:0;color:var(--mt-color-content-txt);font-weight:600;font-size:.75rem;text-align:center;padding:.25rem .5rem;line-height:1.25rem;vertical-align:top}.mt-dialog .mt-btn-dark{margin-left:auto}.mt-container .mt-btn-violet,.mt-container a.mt-btn-violet,.mt-dialog .mt-btn-violet,.mt-dialog a.mt-btn-violet{display:flex;align-items:center;gap:.5rem;border-radius:.25rem;border:.5rem;padding:.5rem .75rem;font-size:1rem;font-weight:600;text-align:center;background-color:var(--mt-color-btn-bg);color:var(--mt-color-btn-txt)}.mt-container .mt-btn-violet:hover,.mt-container a.mt-btn-violet:hover,.mt-dialog .mt-btn-violet:hover,.mt-dialog a.mt-btn-violet:hover{background-color:var(--mt-color-btn-bg-hover);text-decoration:none}.mt-post-txt .mt-btn-spoiler-txt{display:inline-block;vertical-align:middle}.mt-post-media.mt-loading-spinner>.mt-btn-spoiler-media{display:none}.mt-post-media>.mt-btn-spoiler-media-show{position:absolute;top:50%;left:50%;z-index:2;transform:translate(-50%,-50%)}.mt-post-media.mt-post-media-spoiler>.mt-btn-spoiler-media-hide,.mt-post-media:not([data-media-width-hd])>.mt-btn-spoiler-media-hide{display:none}.mt-post-media:not(.mt-post-media-spoiler)>.mt-btn-spoiler-media-show{display:none}.mt-post-media>.mt-btn-spoiler-media-hide{position:absolute;top:.5rem;left:.5rem;z-index:2}.mt-post-media>.mt-btn-spoiler-media-hide>svg{fill:var(--mt-color-content-txt);pointer-events:none}.mt-error{position:absolute;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;height:calc(100% - 3.5rem);width:calc(100% - 4.5rem);justify-content:center;align-items:center;color:var(--mt-color-error-txt);padding:.75rem;text-align:center}.mt-error-icon{font-size:2rem;margin-bottom:1rem}.mt-error-message{width:100%;padding:1rem 0}.mt-error-message hr{color:var(--mt-color-line-gray)}.mt-body>.mt-loading-spinner{position:absolute;width:3rem;height:3rem;margin:auto;top:calc(50% - 1.5rem);right:calc(50% - 1.5rem)}.mt-loading-spinner{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'%3E%3Cg%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 64 64' to='360 64 64' dur='1000ms' repeatCount='indefinite'/%3E%3Cpath d='M64 6.69a57.3 57.3 0 1 1 0 114.61A57.3 57.3 0 0 1 6.69 64' fill='none' stroke='%23404040' stroke-width='12'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center center;background-color:transparent;background-size:min(2.5rem,calc(100% - .5rem))}.mt-footer{display:flex;flex-flow:wrap;margin:auto auto 2rem auto;padding:0 1rem;gap:1.5rem;align-items:center;justify-content:center}.visually-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}@supports (container-type:inline-size){@container mt-container (max-width:20rem){.mt-body{padding:0 .5rem}.mt-container .mt-post-header-date>a{white-space:normal}.mt-post-media-wrapper .mt-post-media{grid-column:1/3}}}@supports not (container-type:inline-size){@media screen and (max-width:clamp(20rem,40rem,60rem)){.mt-body{padding:0 .5rem}.mt-container .mt-post-header-date>a{white-space:normal}.mt-post-media-wrapper .mt-post-media{grid-column:1/3}}} \ No newline at end of file diff --git a/dist/mastodon-timeline.umd.js b/dist/mastodon-timeline.umd.js index 55a307d..ebcff20 100644 --- a/dist/mastodon-timeline.umd.js +++ b/dist/mastodon-timeline.umd.js @@ -2,7 +2,7 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.4.2 + * @version 4.5.0 * @url https://gitlab.com/idotj/mastodon-embed-timeline * @license GNU AGPLv3 - */t.Init=class{constructor(t={}){this.defaultSettings={mtContainerId:"mt-container",instanceUrl:"https://mastodon.social",timelineType:"local",userId:"",profileName:"",hashtagName:"",spinnerClass:"mt-loading-spinner",defaultTheme:"auto",maxNbPostFetch:"20",maxNbPostShow:"20",dateLocale:"en-GB",dateOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,txtMaxLines:"",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",markdownBlockquote:!1,hideEmojos:!1,btnShowContent:"SHOW CONTENT",hideVideoPreview:!1,btnPlayVideoTxt:"Load and play video",hidePreviewLink:!1,previewMaxLines:"",hideCounterBar:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.#t(),this.linkHeader={},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#e((()=>{this.#i()}))}#t(){Number(this.mtSettings.maxNbPostShow)>Number(this.mtSettings.maxNbPostFetch)&&(console.error(`Please check your settings! The maximum number of posts to show is bigger than the maximum number of posts to fetch. Changing the value of "maxNbPostFetch" to: ${this.mtSettings.maxNbPostShow}`),this.mtSettings.maxNbPostFetch=this.mtSettings.maxNbPostShow)}#e(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#i(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#a("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),i=()=>{if(document.getElementById(this.mtSettings.mtContainerId))t();else{performance.now()-e container with id: "${this.mtSettings.mtContainerId}" after several attempts for ${this.mtSettings.insistSearchContainerTime/1e3} seconds`)}};i()}else document.getElementById(this.mtSettings.mtContainerId)?t():console.error(`Impossible to find the
    container with id: "${this.mtSettings.mtContainerId}". Please try to add the option 'insistSearchContainer: true' when initializing the script`)}mtUpdate(){this.#e((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
    '),this.#a("updateTimeline")}))}mtColorTheme(t){this.#e((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#s(){if("auto"===this.mtSettings.defaultTheme){let t=window.matchMedia("(prefers-color-scheme: dark)");t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light"),t.addEventListener("change",(t=>{t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light")}))}else this.mtColorTheme(this.mtSettings.defaultTheme)}#o(){return new Promise(((t,e)=>{const i=this.mtSettings.instanceUrl?`${this.mtSettings.instanceUrl}/api/v1/`:this.#n("Please check your instanceUrl value","âš ī¸"),s=this.#r(i),a=Object.entries(s).map((([t,i])=>{const s="timeline"===t;return this.#l(i,s).then((e=>({[t]:e}))).catch((s=>(e(new Error(`Something went wrong fetching data from: ${i}`)),this.#n(s.message),{[t]:[]})))}));Promise.all(a).then((async e=>{if(this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),!this.mtSettings.hidePinnedPosts&&void 0!==this.fetchedData.pinned?.length&&0!==this.fetchedData.pinned.length){const t=this.fetchedData.pinned.map((t=>({...t,pinned:!0})));this.fetchedData.timeline=[...t,...this.fetchedData.timeline]}if(this.#d())t();else{do{await this.#m()}while(!this.#d()&&this.linkHeader.next);t()}}))}))}#r(t){let e={};return"profile"===this.mtSettings.timelineType?this.mtSettings.userId?(e.timeline=`${t}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`,this.mtSettings.hidePinnedPosts||(e.pinned=`${t}accounts/${this.mtSettings.userId}/statuses?pinned=true`)):this.#n("Please check your userId value","âš ī¸"):"hashtag"===this.mtSettings.timelineType?this.mtSettings.hashtagName?e.timeline=`${t}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`:this.#n("Please check your hashtagName value","âš ī¸"):"local"===this.mtSettings.timelineType?e.timeline=`${t}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`:this.#n("Please check your timelineType value","âš ī¸"),this.mtSettings.hideEmojos||(e.emojos=`${t}custom_emojis`),e}async#l(t,e=!1){const i=await fetch(t);if(!i.ok)throw new Error(`\n Failed to fetch the following Url:
    ${t}
    Error status: ${i.status}
    Error message: ${i.statusText}\n `);const s=await i.json();return e&&i.headers.get("Link")&&(this.linkHeader=this.#h(i.headers.get("Link"))),s}#d(){return this.fetchedData.timeline.length>=Number(this.mtSettings.maxNbPostFetch)}#m(){return new Promise((t=>{this.linkHeader.next?this.#l(this.linkHeader.next,!0).then((e=>{this.fetchedData.timeline=[...this.fetchedData.timeline,...e],t()})):t()}))}#h(t){const e=t.split(", ").map((t=>t.split("; "))).map((t=>[t[1].replace(/"/g,"").replace("rel=",""),t[0].slice(1,-1)]));return Object.fromEntries(e)}async#a(t){await this.#o();const e=this.fetchedData.timeline;let i=0;if(this.mtBodyNode.replaceChildren(),e.forEach((t=>{const e="public"===t.visibility||!this.mtSettings.hideUnlisted&&"unlisted"===t.visibility,s=this.mtSettings.hideReblog&&t.reblog,a=this.mtSettings.hideReplies&&t.in_reply_to_id;!e||s||a||i${e?.length||0} posts have been fetched from the server
    This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;this.#n(t,"📭")}}#g(){"0"!==this.mtSettings.txtMaxLines&&0!==this.mtSettings.txtMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines),"0"!==this.mtSettings.previewMaxLines&&0!==this.mtSettings.previewMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-preview-max-lines",this.mtSettings.previewMaxLines)}#u(t){const e=this.mtBodyNode.getElementsByTagName("article");for(let i=0;i
    '+this.#f(t.reblog.account.username)+' avatar
    '+this.#f(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.reblog.account.display_name?this.#x(t.reblog.account.display_name,t.reblog.account.emojis):t.reblog.account.display_name?t.reblog.account.display_name:t.reblog.account.username,o=this.mtSettings.hideUserAccount?"":'
    ",s='
    '+a+""+o+"
    ",r=t.reblog.created_at,h=t.reblog.replies_count,m=t.reblog.reblogs_count,d=t.reblog.favourites_count):(n=t.url,i='
    '+this.#f(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.account.display_name?this.#x(t.account.display_name,t.account.emojis):t.account.display_name?t.account.display_name:t.account.username,o=this.mtSettings.hideUserAccount?"":'
    ",s='
    '+a+""+o+"
    ",r=t.created_at,h=t.replies_count,m=t.reblogs_count,d=t.favourites_count),l=this.#S(r);const c=`\n
    \n ${t.pinned?'':""}\n \n \n ${t.edited_at?" *":""}\n \n
    `;let p="";if("0"!==this.mtSettings.txtMaxLines){const e=0!==this.mtSettings.txtMaxLines.length?" truncate":"";p=""!==t.spoiler_text?'
    '+this.#y(t.spoiler_text)+'
    '+this.#y(t.content)+"
    ":t.reblog&&""!==t.reblog.content&&""!==t.reblog.spoiler_text?'
    '+this.#y(t.reblog.spoiler_text)+'
    '+this.#y(t.reblog.content)+"
    ":t.reblog&&""!==t.reblog.content&&""===t.reblog.spoiler_text?'
    '+this.#y(t.reblog.content)+"
    ":'
    '+this.#y(t.content)+"
    "}let g=[];if(t.media_attachments.length>0)for(let e in t.media_attachments)g.push(this.#L(t.media_attachments[e],t.sensitive));if(t.reblog&&t.reblog.media_attachments.length>0)for(let e in t.reblog.media_attachments)g.push(this.#L(t.reblog.media_attachments[e],t.reblog.sensitive));g=`
    ${g.join("")}
    `;let u="";!this.mtSettings.hidePreviewLink&&t.card&&(u=this.#T(t.card));let v="";if(t.poll){let e="";for(let i in t.poll.options)e+="
  • "+t.poll.options[i].title+"
  • ";v='
      '+e+"
    "}let b="";if(!this.mtSettings.hideCounterBar){b='
    '+('
    '+h+"
    ")+('
    '+m+"
    ")+('
    '+d+"
    ")+"
    "}return'
    '+i+s+c+"
    "+p+g+u+v+b+"
    "}#N(t,e){function i(t,e){let i=e.replace(/\s+/g,"").toLowerCase();return!(!["src","href","xlink:href"].includes(t)||!i.includes("javascript:")&&!i.includes("data:"))||(!!t.startsWith("on")||void 0)}function s(t){let e=t.attributes;for(let{name:s,value:a}of e)i(s,a)&&t.removeAttribute(s)}let a=(new DOMParser).parseFromString(t,"text/html").body||document.createElement("body");return function(t){let e=t.querySelectorAll("script");for(let t of e)t.remove()}(a),function t(e){let i=e.children;for(let e of i)s(e),t(e)}(a),e?a.childNodes:a.innerHTML}#y(t){let e=t;return e=this.#N(e,!1),e=this.#M(e),this.mtSettings.hideEmojos||(e=this.#x(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#C(e,"

    >","

    ","

    ","

    ")),e}#M(t){let e=t.replaceAll('rel="tag"','rel="tag" target="_blank"');return e=e.replaceAll('class="u-url mention"','class="u-url mention" target="_blank"'),e}#C(t,e,i,s,a){if(t.includes(e)){const o=new RegExp(e+"(.*?)"+i,"gi");return t.replace(o,s+"$1"+a)}return t}#f(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#x(t,e){if(t.includes(":")){for(const i of e){const e=new RegExp(`\\:${i.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${i.shortcode}`)}return t}return t}#S(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateLocale,this.mtSettings.dateOptions).format(e)}#L(t,e=!1){const i=t.type,s=e;let a="";const o='";return"image"===i&&(a='
    '+(s?o:"")+''+(t.description?this.#f(t.description):
    '),"audio"===i&&(a=t.preview_url?'
    '+(s?o:"")+''+(t.description?this.#f(t.description):
    ':'
    '+(s?o:"")+'
    '),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
    '+(s?o:"")+'
    ':'
    '+(s?o:"")+''+(t.description?this.#f(t.description):
    '),a}#P(t,e){let i=document.createElement("dialog");i.id=t,i.classList.add("mt-dialog"),i.dataset.theme=this.mtContainerNode.getAttribute("data-theme"),i.innerHTML=e,document.body.prepend(i),i.showModal(),i.addEventListener("close",(()=>{document.body.removeChild(i)}))}#E(t){const e=Array.from(t.target.parentNode.parentNode.children).filter((t=>!t.classList.contains("mt-post-media-spoiler"))),i=e.indexOf(t.target.parentNode)+1;let s=[];e.forEach(((t,e)=>{let i="";i="gifv"===t.getAttribute("data-media-type")||"video"===t.getAttribute("data-media-type")?`\n \n `:`\n ${t.getAttribute(\n `;const a=`\n \n `;s.push(a)}));const a=`\n \n\n \n\n \n\n \n `;this.#P("mt-carousel",a),s.length>=2&&this.#k(e.length,i)}#k(t,e){let i=e;const s=document.getElementById("mt-carousel-scroll");let a=0,o=!1;const n=document.getElementById("mt-carousel-prev"),r=document.getElementById("mt-carousel-next"),l=(t,e="smooth")=>{document.getElementById("mt-carousel-"+t).scrollIntoView({behavior:e})};l(i,"instant");const d=()=>{clearTimeout(a),a=setTimeout((()=>{o&&(i=(()=>{const t=(s.scrollLeft+s.clientWidth)/s.clientWidth;return Math.round(t+Number.EPSILON)})(),m()),o=!0}),60)};s.addEventListener("scroll",d);const m=()=>{n.hidden=1===i,r.hidden=i===t},h=e=>{const s=e.target.closest("button")?.id;"mt-carousel-next"===s?(o=!1,++i,i>t&&(i=t),l(i),m()):"mt-carousel-prev"===s&&(o=!1,--i,i<1&&(i=1),l(i),m()),"mt-carousel-close"===s&&p()};document.addEventListener("click",h);const c=t=>{"Escape"!==t.key&&27!==t.keyCode||p()};document.addEventListener("keydown",c);const p=()=>{s.removeEventListener("scroll",d),document.removeEventListener("click",h),document.removeEventListener("keydown",c)}}#$(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#_(t){const e=t.target,i=e.nextSibling;e.textContent==this.mtSettings.btnShowMore?(i.classList.remove("spoiler-txt-hidden"),i.classList.add("spoiler-txt-visible"),e.setAttribute("aria-expanded","true"),e.textContent=this.mtSettings.btnShowLess):(i.classList.remove("spoiler-txt-visible"),i.classList.add("spoiler-txt-hidden"),e.setAttribute("aria-expanded","false"),e.textContent=this.mtSettings.btnShowMore)}#H(t){const e=t.target;e.classList.contains("mt-btn-spoiler-media-show")?e.parentNode.classList.remove("mt-post-media-spoiler"):e.parentNode.classList.add("mt-post-media-spoiler")}#T(t){let e="";if("0"!==this.mtSettings.previewMaxLines&&t.description){e=''+this.#A(t.description)+""}return''+(t.image?'
    '+this.#f(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#A(t.provider_name)+"":"")+''+t.title+""+e+(t.author_name?''+this.#A(t.author_name)+"":"")+"
    "}#A(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#b(){let t="",e="";if(this.mtSettings.btnSeeMore){let e="";"profile"===this.mtSettings.timelineType?this.mtSettings.profileName?e=this.mtSettings.profileName:this.#n("Please check your profileName value","âš ī¸"):"hashtag"===this.mtSettings.timelineType?e="tags/"+this.mtSettings.hashtagName:"local"===this.mtSettings.timelineType&&(e="public/local"),t=`\n \n ${this.mtSettings.btnSeeMore}\n `}if(this.mtSettings.btnReload&&(e=`\n `),this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'"),this.mtSettings.btnReload){this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}#v(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||this.mtSettings.disableCarousel&&"image"===s.getAttribute("data-media-type"))&&this.#B(t),e.classList.contains("mt-btn-spoiler-txt")&&this.#_(t),e.classList.contains("mt-btn-spoiler-media")&&this.#H(t),this.mtSettings.disableCarousel||"img"!=i||"image"!==s.getAttribute("data-media-type")&&"audio"!==s.getAttribute("data-media-type")||this.#E(t),("mt-btn-play"==e.className||"svg"==i&&"mt-btn-play"==s.className||"path"==i&&"mt-btn-play"==s.parentNode.className||"img"==i&&("video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")))&&this.#$(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#B(t)}))}#B(t){const e=t.target.closest(".mt-post").dataset.location;"a"!==t.target.localName&&"span"!==t.target.localName&&"button"!==t.target.localName&&"bdi"!==t.target.localName&&"time"!==t.target.localName&&!t.target.classList.contains("mt-post-media-spoiler")&&"mt-post-preview-noImage"!==t.target.className&&"mt-post-avatar-image-big"!==t.target.parentNode.className&&"mt-post-avatar-image-small"!==t.target.parentNode.className&&"mt-post-header-user-name"!==t.target.parentNode.className&&"mt-post-preview-image"!==t.target.parentNode.className&&"mt-post-preview"!==t.target.parentNode.className&&e&&window.open(e,"_blank","noopener")}#p(){const t=e=>{e.target.parentNode.classList.remove(this.mtSettings.spinnerClass),e.target.removeEventListener("load",t),e.target.removeEventListener("error",t)};this.mtBodyNode.querySelectorAll(`.${this.mtSettings.spinnerClass} > img`).forEach((e=>{e.addEventListener("load",t),e.addEventListener("error",t)}))}#n(t,e){const i=e||"❌";throw this.mtBodyNode.innerHTML=`\n
    \n ${i}\n Oops, something's happened:\n
    ${t}
    \n
    `,this.mtBodyNode.setAttribute("role","none"),new Error("Stopping the script due to an error building the timeline.")}}})); + */t.Init=class{constructor(t={}){this.defaultSettings={mtContainerId:"mt-container",instanceUrl:"https://mastodon.social",timelineType:"local",userId:"",profileName:"",hashtagName:"",spinnerClass:"mt-loading-spinner",defaultTheme:"auto",maxNbPostFetch:"20",maxNbPostShow:"20",dateFormatLocale:"en-GB",dateFormatOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,txtMaxLines:"",filterByLanguage:"",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",markdownBlockquote:!1,hideEmojos:!1,btnShowContent:"SHOW CONTENT",hideVideoPreview:!1,btnPlayVideoTxt:"Load and play video",hidePreviewLink:!1,previewMaxLines:"",hideCounterBar:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.#t(),this.linkHeader={},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#e((()=>{this.#i()}))}#t(){Number(this.mtSettings.maxNbPostShow)>Number(this.mtSettings.maxNbPostFetch)&&(console.error(`Please check your settings! The maximum number of posts to show is bigger than the maximum number of posts to fetch. Changing the value of "maxNbPostFetch" to: ${this.mtSettings.maxNbPostShow}`),this.mtSettings.maxNbPostFetch=this.mtSettings.maxNbPostShow)}#e(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#i(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#a("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),i=()=>{if(document.getElementById(this.mtSettings.mtContainerId))t();else{performance.now()-e container with id: "${this.mtSettings.mtContainerId}" after several attempts for ${this.mtSettings.insistSearchContainerTime/1e3} seconds`)}};i()}else document.getElementById(this.mtSettings.mtContainerId)?t():console.error(`Impossible to find the
    container with id: "${this.mtSettings.mtContainerId}". Please try to add the option 'insistSearchContainer: true' when initializing the script`)}mtUpdate(){this.#e((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
    '),this.#a("updateTimeline")}))}mtColorTheme(t){this.#e((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#s(){if("auto"===this.mtSettings.defaultTheme){let t=window.matchMedia("(prefers-color-scheme: dark)");t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light"),t.addEventListener("change",(t=>{t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light")}))}else this.mtColorTheme(this.mtSettings.defaultTheme)}#n(){return new Promise(((t,e)=>{const i=this.mtSettings.instanceUrl?`${this.mtSettings.instanceUrl}/api/v1/`:this.#o("Please check your instanceUrl value","âš ī¸"),s=this.#r(i),a=Object.entries(s).map((([t,i])=>{const s="timeline"===t;return this.#l(i,s).then((e=>({[t]:e}))).catch((s=>(e(new Error(`Something went wrong fetching data from: ${i}`)),this.#o(s.message),{[t]:[]})))}));Promise.all(a).then((async e=>{if(this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),!this.mtSettings.hidePinnedPosts&&void 0!==this.fetchedData.pinned?.length&&0!==this.fetchedData.pinned.length){const t=this.fetchedData.pinned.map((t=>({...t,pinned:!0})));this.fetchedData.timeline=[...t,...this.fetchedData.timeline]}if(this.#d())t();else{do{await this.#m()}while(!this.#d()&&this.linkHeader.next);t()}}))}))}#r(t){const{timelineType:e,userId:i,hashtagName:s,maxNbPostFetch:a,hidePinnedPosts:n,hideEmojos:o}=this.mtSettings,r={};switch(e){case"profile":if(!i){this.#o("Please check your userId value","âš ī¸");break}r.timeline=`${t}accounts/${i}/statuses?limit=${a}`,n||(r.pinned=`${t}accounts/${i}/statuses?pinned=true`);break;case"hashtag":if(!s){this.#o("Please check your hashtagName value","âš ī¸");break}r.timeline=`${t}timelines/tag/${s}?limit=${a}`;break;case"local":r.timeline=`${t}timelines/public?local=true&limit=${a}`;break;default:this.#o("Please check your timelineType value","âš ī¸")}return o||(r.emojos=`${t}custom_emojis`),r}async#l(t,e=!1){const i=await fetch(t);if(!i.ok)throw new Error(`\n Failed to fetch the following Url:
    ${t}
    Error status: ${i.status}
    Error message: ${i.statusText}\n `);const s=await i.json();return e&&i.headers.get("Link")&&(this.linkHeader=this.#h(i.headers.get("Link"))),s}#d(){return this.fetchedData.timeline.length>=Number(this.mtSettings.maxNbPostFetch)}#m(){return new Promise((t=>{this.linkHeader.next?this.#l(this.linkHeader.next,!0).then((e=>{this.fetchedData.timeline=[...this.fetchedData.timeline,...e],t()})):t()}))}#h(t){const e=t.split(", ").map((t=>t.split("; "))).map((t=>[t[1].replace(/"/g,"").replace("rel=",""),t[0].slice(1,-1)]));return Object.fromEntries(e)}async#a(t){await this.#n();const{hideUnlisted:e,hideReblog:i,hideReplies:s,maxNbPostShow:a,filterByLanguage:n}=this.mtSettings,o=this.fetchedData.timeline;this.mtBodyNode.replaceChildren();if(o.filter((t=>{const a="public"===t.visibility||!e&&"unlisted"===t.visibility,o=i&&t.reblog,r=s&&t.in_reply_to_id,l=t.language||(t.reblog?t.reblog.language:null);return a&&!o&&!r&&(""===n||l===n)})).forEach(((t,e)=>{e${o?.length||0} posts have been fetched from the server
    This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;this.#o(t,"📭")}}#g(){"0"!==this.mtSettings.txtMaxLines&&0!==this.mtSettings.txtMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines),"0"!==this.mtSettings.previewMaxLines&&0!==this.mtSettings.previewMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-preview-max-lines",this.mtSettings.previewMaxLines)}#u(t){const e=this.mtBodyNode.getElementsByTagName("article");for(let i=0;i
    '+this.#f(h)+' avatar
    '+(i?'
    '+this.#f(t.account.username)+' avatar
    ':"")+"
    ",u='
    '+(!this.mtSettings.hideEmojos&&c?this.#S(c,p):c||h)+""+(this.mtSettings.hideUserAccount?"":'
    ")+"
    ",v=this.#x(n),b='
    '+(t.pinned?"...":"")+'"+(t.edited_at?" *":"")+"
    ",w="0"!==this.mtSettings.txtMaxLines?" truncate":"";let f="";const S=s.spoiler_text?s.spoiler_text:s.content;S&&(f='
    '+this.#y(S)+"
    ");const x=[...t.media_attachments,...t.reblog?.media_attachments||[]].map((t=>this.#L(t,s.sensitive))).join("");return'
    '+g+u+b+"
    "+f+(x?`
    ${x}
    `:"")+(!this.mtSettings.hidePreviewLink&&t.card?this.#T(t.card):"")+(t.poll?'
      '+t.poll.options.map((function(t){return"
    • "+t.title+"
    • "})).join("")+"
    ":"")+(this.mtSettings.hideCounterBar?"":'
    '+this.#N("replies",o)+this.#N("reblog",r)+this.#N("favorites",l)+"
    ")+"
    "}#N(t,e){return`
    ${{replies:'',reblog:'',favorites:''}[t]}${e}
    `}#M(t,e){function i(t,e){let i=e.replace(/\s+/g,"").toLowerCase();return!(!["src","href","xlink:href"].includes(t)||!i.includes("javascript:")&&!i.includes("data:"))||(!!t.startsWith("on")||void 0)}function s(t){let e=t.attributes;for(let{name:s,value:a}of e)i(s,a)&&t.removeAttribute(s)}let a=(new DOMParser).parseFromString(t,"text/html").body||document.createElement("body");return function(t){let e=t.querySelectorAll("script");for(let t of e)t.remove()}(a),function t(e){let i=e.children;for(let e of i)s(e),t(e)}(a),e?a.childNodes:a.innerHTML}#y(t){let e=t;return e=this.#M(e,!1),e=this.#C(e),this.mtSettings.hideEmojos||(e=this.#S(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#P(e,"

    >","

    ","

    ","

    ")),e}#C(t){let e=t.replaceAll('rel="tag"','rel="tag" target="_blank"');return e=e.replaceAll('class="u-url mention"','class="u-url mention" target="_blank"'),e}#P(t,e,i,s,a){if(t.includes(e)){const n=new RegExp(e+"(.*?)"+i,"gi");return t.replace(n,s+"$1"+a)}return t}#f(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#S(t,e){if(t.includes(":")){for(const i of e){const e=new RegExp(`\\:${i.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${i.shortcode}`)}return t}return t}#x(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateFormatLocale,this.mtSettings.dateFormatOptions).format(e)}#L(t,e=!1){const i=t.type,s=e;let a="";const n='";return"image"===i&&(a='
    '+(s?n:"")+''+(t.description?this.#f(t.description):
    '),"audio"===i&&(a=t.preview_url?'
    '+(s?n:"")+''+(t.description?this.#f(t.description):
    ':'
    '+(s?n:"")+'
    '),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
    '+(s?n:"")+'
    ':'
    '+(s?n:"")+''+(t.description?this.#f(t.description):
    '),a}#k(t,e){let i=document.createElement("dialog");i.id=t,i.classList.add("mt-dialog"),i.dataset.theme=this.mtContainerNode.getAttribute("data-theme"),i.innerHTML=e,document.body.prepend(i),i.showModal(),i.addEventListener("close",(()=>{document.body.removeChild(i)}))}#E(t){const e=Array.from(t.target.parentNode.parentNode.children).filter((t=>!t.classList.contains("mt-post-media-spoiler"))),i=e.indexOf(t.target.parentNode)+1;let s=[];e.forEach(((t,e)=>{let i="";i="gifv"===t.getAttribute("data-media-type")||"video"===t.getAttribute("data-media-type")?`\n \n `:`\n ${t.getAttribute(\n `;const a=`\n \n `;s.push(a)}));const a=`\n \n\n \n\n \n\n \n `;this.#k("mt-carousel",a),s.length>=2&&this.#$(e.length,i)}#$(t,e){let i=e;const s=document.getElementById("mt-carousel-scroll");let a=0,n=!1;const o=document.getElementById("mt-carousel-prev"),r=document.getElementById("mt-carousel-next"),l=(t,e="smooth")=>{document.getElementById("mt-carousel-"+t).scrollIntoView({behavior:e})};l(i,"instant");const d=()=>{clearTimeout(a),a=setTimeout((()=>{n&&(i=(()=>{const t=(s.scrollLeft+s.clientWidth)/s.clientWidth;return Math.round(t+Number.EPSILON)})(),m()),n=!0}),60)};s.addEventListener("scroll",d);const m=()=>{o.hidden=1===i,r.hidden=i===t},h=e=>{const s=e.target.closest("button")?.id;"mt-carousel-next"===s?(n=!1,++i,i>t&&(i=t),l(i),m()):"mt-carousel-prev"===s&&(n=!1,--i,i<1&&(i=1),l(i),m()),"mt-carousel-close"===s&&p()};document.addEventListener("click",h);const c=t=>{"Escape"!==t.key&&27!==t.keyCode||p()};document.addEventListener("keydown",c);const p=()=>{s.removeEventListener("scroll",d),document.removeEventListener("click",h),document.removeEventListener("keydown",c)}}#B(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#A(t){const e=t.target,i=e.nextSibling;e.textContent==this.mtSettings.btnShowMore?(i.classList.remove("spoiler-txt-hidden"),i.classList.add("spoiler-txt-visible"),e.setAttribute("aria-expanded","true"),e.textContent=this.mtSettings.btnShowLess):(i.classList.remove("spoiler-txt-visible"),i.classList.add("spoiler-txt-hidden"),e.setAttribute("aria-expanded","false"),e.textContent=this.mtSettings.btnShowMore)}#H(t){const e=t.target;e.classList.contains("mt-btn-spoiler-media-show")?e.parentNode.classList.remove("mt-post-media-spoiler"):e.parentNode.classList.add("mt-post-media-spoiler")}#T(t){let e="";if("0"!==this.mtSettings.previewMaxLines&&t.description){e=''+this.#_(t.description)+""}return''+(t.image?'
    '+this.#f(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#_(t.provider_name)+"":"")+''+t.title+""+e+(t.author_name?''+this.#_(t.author_name)+"":"")+"
    "}#_(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#b(){let t="",e="";if(this.mtSettings.btnSeeMore){let e="";"profile"===this.mtSettings.timelineType?this.mtSettings.profileName?e=this.mtSettings.profileName:this.#o("Please check your profileName value","âš ī¸"):"hashtag"===this.mtSettings.timelineType?e="tags/"+this.mtSettings.hashtagName:"local"===this.mtSettings.timelineType&&(e="public/local"),t=`\n \n ${this.mtSettings.btnSeeMore}\n `}if(this.mtSettings.btnReload&&(e=`\n `),this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'"),this.mtSettings.btnReload){this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}#v(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||this.mtSettings.disableCarousel&&"image"===s.getAttribute("data-media-type"))&&this.#D(t),e.classList.contains("mt-btn-spoiler-txt")&&this.#A(t),e.classList.contains("mt-btn-spoiler-media")&&this.#H(t),this.mtSettings.disableCarousel||"img"!=i||"image"!==s.getAttribute("data-media-type")&&"audio"!==s.getAttribute("data-media-type")||this.#E(t),("mt-btn-play"==e.className||"svg"==i&&"mt-btn-play"==s.className||"path"==i&&"mt-btn-play"==s.parentNode.className||"img"==i&&("video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")))&&this.#B(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#D(t)}))}#D(t){const e=t.target.closest(".mt-post")?.dataset.location;if(!e)return;const i=t.target.localName;if("a"===i||"span"===i||"button"===i||"bdi"===i||"time"===i)return;const s=t.target.className;if("mt-post-media-spoiler"===s||"mt-post-preview-noImage"===s)return;const a=t.target.parentNode?.className;"mt-post-avatar-image-big"!==a&&"mt-post-avatar-image-small"!==a&&"mt-post-header-user-name"!==a&&"mt-post-preview-image"!==a&&"mt-post-preview"!==a&&window.open(e,"_blank","noopener")}#p(){const t=e=>{e.target.parentNode.classList.remove(this.mtSettings.spinnerClass),e.target.removeEventListener("load",t),e.target.removeEventListener("error",t)};this.mtBodyNode.querySelectorAll(`.${this.mtSettings.spinnerClass} > img`).forEach((e=>{e.addEventListener("load",t),e.addEventListener("error",t)}))}#o(t,e){const i=e||"❌";throw this.mtBodyNode.innerHTML=`\n
    \n ${i}\n Oops, something's happened:\n
    ${t}
    \n
    `,this.mtBodyNode.setAttribute("role","none"),new Error("Stopping the script due to an error building the timeline.")}}})); diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md new file mode 100644 index 0000000..afc81b5 --- /dev/null +++ b/docs/INSTALLATION.md @@ -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 `` and ` + + + +``` + +## 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 + +``` + +```html + +``` + +## 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. diff --git a/docs/SETUP.md b/docs/SETUP.md new file mode 100644 index 0000000..c10ce55 --- /dev/null +++ b/docs/SETUP.md @@ -0,0 +1,239 @@ +# 🐘 Mastodon embed timeline - âš™ī¸ Setup + +## Initialize + +Add the following HTML structure in your page: + +```html +
    +
    +
    +
    +
    +``` + +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: + + 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
    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
    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
    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. + diff --git a/screenshot-light-dark.jpg b/docs/screenshot-light-dark.jpg similarity index 100% rename from screenshot-light-dark.jpg rename to docs/screenshot-light-dark.jpg diff --git a/examples/local-timeline-customized.html b/examples/local-timeline-customized.html index 421ed8f..a30dac2 100644 --- a/examples/local-timeline-customized.html +++ b/examples/local-timeline-customized.html @@ -187,8 +187,8 @@ defaultTheme: "light", maxNbPostFetch: "42", maxNbPostShow: "42", - dateLocale: "en-CA", - dateOptions: { + dateFormatLocale: "en-CA", + dateFormatOptions: { day: "2-digit", month: "2-digit", year: "numeric", @@ -225,8 +225,8 @@ defaultTheme: "light", maxNbPostFetch: "42", maxNbPostShow: "42", - dateLocale: "en-CA", - dateOptions: { + dateFormatLocale: "en-CA", + dateFormatOptions: { day: "2-digit", month: "2-digit", year: "numeric", diff --git a/package-lock.json b/package-lock.json index f74a3c5..ce87bcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "@idotj/mastodon-embed-timeline", - "version": "4.4.2", + "version": "4.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@idotj/mastodon-embed-timeline", - "version": "4.4.2", + "version": "4.5.0", "license": "GNU", "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "clean-css-cli": "^5.6.3", - "rollup": "^4.14.0" + "rollup": "^4.34.8" } }, "node_modules/@jridgewell/gen-mapping": { @@ -95,205 +95,277 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", - "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", + "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", - "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", + "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", - "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", + "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", - "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", + "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "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": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", - "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", + "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", "cpu": [ "arm" ], "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, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", - "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", + "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", - "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", + "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", "cpu": [ "arm64" ], "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, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", - "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", + "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", "cpu": [ - "ppc64le" + "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", - "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", + "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", - "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", + "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", - "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", + "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", - "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", + "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", - "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", + "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", - "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", + "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", - "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", + "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/acorn": { "version": "8.11.3", @@ -346,12 +418,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -433,10 +506,11 @@ "dev": true }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -550,6 +624,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -627,12 +702,13 @@ } }, "node_modules/rollup": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", - "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", + "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -642,21 +718,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.0", - "@rollup/rollup-android-arm64": "4.14.0", - "@rollup/rollup-darwin-arm64": "4.14.0", - "@rollup/rollup-darwin-x64": "4.14.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", - "@rollup/rollup-linux-arm64-gnu": "4.14.0", - "@rollup/rollup-linux-arm64-musl": "4.14.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", - "@rollup/rollup-linux-riscv64-gnu": "4.14.0", - "@rollup/rollup-linux-s390x-gnu": "4.14.0", - "@rollup/rollup-linux-x64-gnu": "4.14.0", - "@rollup/rollup-linux-x64-musl": "4.14.0", - "@rollup/rollup-win32-arm64-msvc": "4.14.0", - "@rollup/rollup-win32-ia32-msvc": "4.14.0", - "@rollup/rollup-win32-x64-msvc": "4.14.0", + "@rollup/rollup-android-arm-eabi": "4.34.8", + "@rollup/rollup-android-arm64": "4.34.8", + "@rollup/rollup-darwin-arm64": "4.34.8", + "@rollup/rollup-darwin-x64": "4.34.8", + "@rollup/rollup-freebsd-arm64": "4.34.8", + "@rollup/rollup-freebsd-x64": "4.34.8", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", + "@rollup/rollup-linux-arm-musleabihf": "4.34.8", + "@rollup/rollup-linux-arm64-gnu": "4.34.8", + "@rollup/rollup-linux-arm64-musl": "4.34.8", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", + "@rollup/rollup-linux-riscv64-gnu": "4.34.8", + "@rollup/rollup-linux-s390x-gnu": "4.34.8", + "@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" } }, @@ -743,6 +823,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/package.json b/package.json index 5ae8a0b..75957a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@idotj/mastodon-embed-timeline", - "version": "4.4.2", - "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.", + "version": "4.5.0", + "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", "author": { "name": "idotj", @@ -34,7 +34,7 @@ }, "devDependencies": { "clean-css-cli": "^5.6.3", - "rollup": "^4.14.0", + "rollup": "^4.34.8", "@rollup/plugin-terser": "^0.4.4" }, "files": [ diff --git a/src/mastodon-timeline.css b/src/mastodon-timeline.css index 6e505ad..9edc4d2 100644 --- a/src/mastodon-timeline.css +++ b/src/mastodon-timeline.css @@ -1,4 +1,4 @@ -/* Mastodon embed timeline v4.4.2 */ +/* Mastodon embed timeline v4.5.0 */ /* More info at: */ /* https://gitlab.com/idotj/mastodon-embed-timeline */ @@ -476,6 +476,7 @@ body:has(dialog.mt-dialog[open]) { gap: 0.25rem; align-items: center; opacity: 0.5; + cursor: default; } .mt-post-counter-bar-replies > svg, .mt-post-counter-bar-reblog > svg, diff --git a/src/mastodon-timeline.js b/src/mastodon-timeline.js index ff8fcac..e8acca3 100644 --- a/src/mastodon-timeline.js +++ b/src/mastodon-timeline.js @@ -1,7 +1,7 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.4.2 + * @version 4.5.0 * @url https://gitlab.com/idotj/mastodon-embed-timeline * @license GNU AGPLv3 */ @@ -20,8 +20,8 @@ export class Init { defaultTheme: "auto", maxNbPostFetch: "20", maxNbPostShow: "20", - dateLocale: "en-GB", - dateOptions: { + dateFormatLocale: "en-GB", + dateFormatOptions: { day: "2-digit", month: "short", year: "numeric", @@ -32,6 +32,7 @@ export class Init { hidePinnedPosts: false, hideUserAccount: false, txtMaxLines: "", + filterByLanguage: "", btnShowMore: "SHOW MORE", btnShowLess: "SHOW LESS", markdownBlockquote: false, @@ -100,7 +101,10 @@ export class Init { * Find main container in DOM before building the timeline */ #getContainerNode() { - // console.log("Initializing Mastodon timeline with settings: ", this.mtSettings); + // console.log( + // "Initializing Mastodon timeline with settings: ", + // this.mtSettings + // ); const assignContainerNode = () => { this.mtContainerNode = document.getElementById( @@ -261,39 +265,55 @@ export class Init { * @returns {Object} */ #setUrls(i) { - let urls = {}; + const { + timelineType, + userId, + hashtagName, + maxNbPostFetch, + hidePinnedPosts, + hideEmojos, + } = this.mtSettings; - if (this.mtSettings.timelineType === "profile") { - if (this.mtSettings.userId) { - urls.timeline = `${i}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`; - if (!this.mtSettings.hidePinnedPosts) { - urls.pinned = `${i}accounts/${this.mtSettings.userId}/statuses?pinned=true`; + const urls = {}; + + switch (timelineType) { + case "profile": + if (!userId) { + this.#showError( + "Please check your userId 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 hashtagName 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( - "Please check your userId value", + "Please check your timelineType 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 hashtagName value", - "âš ī¸" - ); - } - } else if (this.mtSettings.timelineType === "local") { - urls.timeline = `${i}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`; - } else { - this.#showError( - "Please check your timelineType value", - "âš ī¸" - ); } - if (!this.mtSettings.hideEmojos) { + if (!hideEmojos) { urls.emojos = `${i}custom_emojis`; } @@ -375,27 +395,39 @@ export class Init { // console.log("Mastodon timeline data fetched: ", this.fetchedData); + const { + hideUnlisted, + hideReblog, + hideReplies, + maxNbPostShow, + filterByLanguage, + } = this.mtSettings; const posts = this.fetchedData.timeline; let nbPostToShow = 0; - this.mtBodyNode.replaceChildren(); - posts.forEach((post) => { + const filteredPosts = posts.filter((post) => { const isPublicOrUnlisted = post.visibility === "public" || - (!this.mtSettings.hideUnlisted && post.visibility === "unlisted"); - const shouldHideReblog = this.mtSettings.hideReblog && post.reblog; - const shouldHideReplies = - this.mtSettings.hideReplies && post.in_reply_to_id; + (!hideUnlisted && post.visibility === "unlisted"); + const shouldHideReblog = hideReblog && post.reblog; + const shouldHideReplies = 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) - if (isPublicOrUnlisted && !shouldHideReblog && !shouldHideReplies) { - if (nbPostToShow < this.mtSettings.maxNbPostShow) { - this.#appendPost(post, nbPostToShow); - nbPostToShow++; - } else { - // Reached the limit of maximum number of posts to show - } + return ( + isPublicOrUnlisted && + !shouldHideReblog && + !shouldHideReplies && + matchesLanguage + ); + }); + + filteredPosts.forEach((post, index) => { + if (index < maxNbPostShow) { + this.#appendPost(post, index); } }); @@ -472,312 +504,187 @@ export class Init { * @param {Number} i Index of post */ #assamblePost(c, i) { - let avatar, - user, - userName, - accountName, + const isReblog = Boolean(c.reblog); + const post = isReblog ? c.reblog : c; + const { url, - date, - formattedDate, - favoritesCount, - reblogCount, - repliesCount; + created_at: date, + replies_count, + reblogs_count, + favourites_count, + } = post; + const { + avatar, + url: accountUrl, + username, + display_name, + emojis, + } = post.account; - if (c.reblog) { - // BOOSTED post - // Post url - url = c.reblog.url; + // Avatar + const avatarHTML = + '' + + '
    ' + + '
    ' + + '' +
+      this.#escapeHTML(username) +
+      ' avatar' + + "
    " + + (isReblog + ? '
    ' + + '' +
+          this.#escapeHTML(c.account.username) +
+          ' avatar' + + "
    " + : "") + + "
    " + + "
    "; - // Boosted avatar - avatar = - '' + - '
    ' + - '
    ' + - '' +
-        this.#escapeHTML(c.reblog.account.username) +
-        ' avatar' + - "
    " + - '
    ' + - '' +
-        this.#escapeHTML(c.account.username) +
-        ' avatar' + - "
    " + - "
    " + - "
    "; + // User + const userNameFull = + !this.mtSettings.hideEmojos && display_name + ? this.#shortcode2Emojos(display_name, emojis) + : display_name || username; - // User name and url - if (!this.mtSettings.hideEmojos && c.reblog.account.display_name) { - userName = this.#shortcode2Emojos( - c.reblog.account.display_name, - c.reblog.account.emojis - ); - } else { - userName = c.reblog.account.display_name - ? c.reblog.account.display_name - : c.reblog.account.username; - } + const accountName = this.mtSettings.hideUserAccount + ? "" + : '
    "; - if (!this.mtSettings.hideUserAccount) { - accountName = - '
    "; - } else { - accountName = ""; - } - - user = - '
    ' + - '' + - userName + - "" + - accountName + - "" + - "
    "; - - // 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 = - '' + - '
    ' + - '
    ' + - '' +
-        this.#escapeHTML(c.account.username) +
-        ' avatar' + - "
    " + - "
    " + - "
    "; - - // 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 = - '
    "; - } else { - accountName = ""; - } - - user = - '
    ' + - '' + - userName + - "" + - accountName + - "" + - "
    "; - - // Date - date = c.created_at; - - // Counter bar - repliesCount = c.replies_count; - reblogCount = c.reblogs_count; - favoritesCount = c.favourites_count; - } + const userHTML = + '
    ' + + '' + + '' + + userNameFull + + "" + + accountName + + "" + + "
    "; // Date - formattedDate = this.#formatDate(date); - const timestamp = ` -
    - ${ - c.pinned - ? '' - : "" - } - - - ${c.edited_at ? " *" : ""} - -
    `; + const formattedDate = this.#formatDate(date); + const dateHTML = + '
    ' + + (c.pinned ? "..." : "") + + '' + + '" + + (c.edited_at ? " *" : "") + + "" + + "
    "; - // Main text - let content = ""; - if (this.mtSettings.txtMaxLines !== "0") { - const txtCss = - this.mtSettings.txtMaxLines.length !== 0 ? " truncate" : ""; + // Post text + const txtTruncateCss = + this.mtSettings.txtMaxLines !== "0" ? " truncate" : ""; + let postTxt = ""; + const textSource = post.spoiler_text ? post.spoiler_text : post.content; - if (c.spoiler_text !== "") { - content = - '
    ' + - this.#formatPostText(c.spoiler_text) + - ' " + - '
    ' + - this.#formatPostText(c.content) + - "
    " + - "
    "; - } else if ( - c.reblog && - c.reblog.content !== "" && - c.reblog.spoiler_text !== "" - ) { - content = - '
    ' + - this.#formatPostText(c.reblog.spoiler_text) + - ' " + - '
    ' + - this.#formatPostText(c.reblog.content) + - "
    " + - "
    "; - } else if ( - c.reblog && - c.reblog.content !== "" && - c.reblog.spoiler_text === "" - ) { - content = - '
    ' + - '
    ' + - this.#formatPostText(c.reblog.content) + - "
    " + - "
    "; - } else { - content = - '
    ' + - '
    ' + - this.#formatPostText(c.content) + - "
    " + - "
    "; - } + if (textSource) { + postTxt = + '
    ' + + '
    ' + + this.#formatPostText(textSource) + + "
    " + + "
    "; } - // Media attachments - let media = []; - if (c.media_attachments.length > 0) { - for (let i in c.media_attachments) { - media.push(this.#createMedia(c.media_attachments[i], c.sensitive)); - } - } - if (c.reblog && c.reblog.media_attachments.length > 0) { - for (let i in c.reblog.media_attachments) { - media.push( - this.#createMedia(c.reblog.media_attachments[i], c.reblog.sensitive) - ); - } - } - media = `
    ${media.join("")}
    `; + // Media + const media = [ + ...c.media_attachments, + ...(c.reblog?.media_attachments || []), + ] + .map((attachment) => this.#createMedia(attachment, post.sensitive)) + .join(""); + + const mediaHTML = media + ? `
    ${media}
    ` + : ""; // Preview link - let previewLink = ""; - if (!this.mtSettings.hidePreviewLink && c.card) { - previewLink = this.#createPreviewLink(c.card); - } + const previewLinkHTML = + !this.mtSettings.hidePreviewLink && c.card + ? this.#createPreviewLink(c.card) + : ""; // Poll - let poll = ""; - if (c.poll) { - let pollOption = ""; - for (let i in c.poll.options) { - pollOption += "
  • " + c.poll.options[i].title + "
  • "; - } - poll = - '
    ' + "
      " + - pollOption + + c.poll.options + .map(function (opt) { + return "
    • " + opt.title + "
    • "; + }) + .join("") + "
    " + - "
    "; - } + "
    " + : ""; // Counter bar - let counterBar = ""; - if (!this.mtSettings.hideCounterBar) { - const repliesTag = - '
    ' + - '' + - repliesCount + - "
    "; + const counterBarHTML = !this.mtSettings.hideCounterBar + ? '
    ' + + this.#counteBarItem("replies", replies_count) + + this.#counteBarItem("reblog", reblogs_count) + + this.#counteBarItem("favorites", favourites_count) + + "
    " + : ""; - const reblogTag = - '
    ' + - '' + - reblogCount + - "
    "; - - const favoritesTag = - '
    ' + - '' + - favoritesCount + - "
    "; - - counterBar = - '
    ' + - repliesTag + - reblogTag + - favoritesTag + - "
    "; - } - - // Put all elements together in the post container - const post = + return ( '
    ' + '
    ' + - avatar + - user + - timestamp + + avatarHTML + + userHTML + + dateHTML + "
    " + - content + - media + - previewLink + - poll + - counterBar + - "
    "; + postTxt + + mediaHTML + + previewLinkHTML + + pollHTML + + counterBarHTML + + "" + ); + } - return post; + /** + * Build counter bar items + * @param {string} t Type of icon + * @param {Number} i Counter + */ + #counteBarItem(t, c) { + const icons = { + replies: + '', + reblog: + '', + favorites: + '', + }; + return `
    ${icons[t]}${c}
    `; } /** @@ -983,8 +890,8 @@ export class Init { const originalDate = new Date(d); const formattedDate = new Intl.DateTimeFormat( - this.mtSettings.dateLocale, - this.mtSettings.dateOptions + this.mtSettings.dateFormatLocale, + this.mtSettings.dateFormatOptions ).format(originalDate); return formattedDate; @@ -1408,12 +1315,12 @@ export class Init { #createPreviewLink(c) { let previewDescription = ""; if (this.mtSettings.previewMaxLines !== "0" && c.description) { - const txtCss = + const txtTruncateCss = this.mtSettings.previewMaxLines.length !== 0 ? " truncate" : ""; previewDescription = '' + this.#parseHTMLstring(c.description) + ""; @@ -1591,24 +1498,37 @@ export class Init { * @param {Event} e User interaction trigger */ #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 ( - e.target.localName !== "a" && - e.target.localName !== "span" && - e.target.localName !== "button" && - e.target.localName !== "bdi" && - e.target.localName !== "time" && - !e.target.classList.contains("mt-post-media-spoiler") && - e.target.className !== "mt-post-preview-noImage" && - e.target.parentNode.className !== "mt-post-avatar-image-big" && - e.target.parentNode.className !== "mt-post-avatar-image-small" && - e.target.parentNode.className !== "mt-post-header-user-name" && - e.target.parentNode.className !== "mt-post-preview-image" && - e.target.parentNode.className !== "mt-post-preview" && - urlPost - ) { - window.open(urlPost, "_blank", "noopener"); - } + tagName === "a" || + tagName === "span" || + tagName === "button" || + tagName === "bdi" || + tagName === "time" + ) + return; + + const targetClass = e.target.className; + if ( + targetClass === "mt-post-media-spoiler" || + targetClass === "mt-post-preview-noImage" + ) + return; + + 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"); } /**