From 852994af055ddda30f31ac1a2a8c99dfd05fdf21 Mon Sep 17 00:00:00 2001 From: "i.j" Date: Sat, 9 Mar 2024 11:31:09 +0000 Subject: [PATCH] Feature/lightbox-carousel --- CHANGELOG | 10 +- README.md | 23 +- dist/mastodon-timeline.esm.js | 4 +- dist/mastodon-timeline.min.css | 2 +- dist/mastodon-timeline.umd.js | 4 +- examples/local-timeline-customized.html | 39 +- package-lock.json | 189 ++++++++- package.json | 2 +- rollup.config.js | 40 +- src/mastodon-timeline.css | 167 ++++++-- src/mastodon-timeline.js | 512 ++++++++++++++++++------ 11 files changed, 787 insertions(+), 205 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b69ea5a..7e1b65a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +v4.3.5 - 09/03/2024 +- Add a carousel/lightbox for pictures and videos in a post +- Improve data set for media items +- Enable loop for videos by default +- Small UI improvements + v4.3.3 - 01/03/2024 - Fix click conflict on user name - Render emojos in user name @@ -58,7 +64,7 @@ v3.13.1 - 12/01/2024 v3.12.0 - 11/12/2023 - Fix link preview event on click -v3.11.0 - 4/11/2023 +v3.11.0 - 04/11/2023 - Update icons - Improve loader spinner @@ -103,7 +109,7 @@ v3.8.1 - 14/08/2023 - Improve JS comments v3.8.0 - 04/08/2023 -- Add spoiler/sensitive button for reblog content +- Add sensitive/spoiler button for reblog content v3.7.2 - 25/07/2023 - Use window.onload to take async attribute into account diff --git a/README.md b/README.md index fd11511..4f5b7bd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Mastodon timeline widget screenshot](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, only with a CSS and JS file. Demo running: @@ -65,11 +65,11 @@ This option allows you to start without the need to upload any files on your ser Copy the following CSS and JS links to include them in your project: ```html - + ``` ```html - + ``` ### Package manager @@ -108,7 +108,7 @@ To get your timeline up add the following HTML structure in your page: ``` -Then after that you can initialize the script by running: +Now you can then initialize the script running: ```js const myTimeline = new MastodonTimeline.Init(); @@ -246,7 +246,7 @@ Here you have all the options available to quickly setup and customize your time // Default: don't hide hidePinnedPosts: false, - // Hide user account under the user name + // Hide the user account under the user name // Default: don't hide hideUserAccount: false, @@ -270,15 +270,24 @@ Here you have all the options available to quickly setup and customize your time // Default: don't apply markdownBlockquote: false, + // Show a carousel/lightbox when the user clicks on a picture in a post + // Default: 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", + // Limit the text content to a maximum number of lines // Default: 0 (unlimited) txtMaxLines: "0", - // Customize the text of the button used for showing/hiding sensitive/spolier text + // Customize the text of the button used for showing/hiding sensitive/spoiler text btnShowMore: "SHOW MORE", btnShowLess: "SHOW LESS", - // Customize the text of the button used for showing sensitive/spolier media content + // Customize the text of the button used for showing sensitive/spoiler media content btnShowContent: "SHOW CONTENT", // Customize the text of the button pointing to the Mastodon page placed at the end of the timeline diff --git a/dist/mastodon-timeline.esm.js b/dist/mastodon-timeline.esm.js index 1bcbbec..1a2ba5c 100644 --- a/dist/mastodon-timeline.esm.js +++ b/dist/mastodon-timeline.esm.js @@ -1,8 +1,8 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.3.3 + * @version 4.3.5 * @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,hideEmojos:!1,hideVideoPreview:!1,hidePreviewLink:!1,hideCounterBar:!1,markdownBlockquote:!1,txtMaxLines:"0",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",btnShowContent:"SHOW CONTENT",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#t((()=>{this.#e()}))}#t(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#e(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#i("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),s=()=>{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`)}};s()}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.#t((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
'),this.#i("updateTimeline")}))}mtColorTheme(t){this.#t((()=>{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)}#a(){return new Promise(((t,e)=>{let s={};this.mtSettings.instanceUrl?"profile"===this.mtSettings.timelineType?this.mtSettings.userId?(s.timeline=`${this.mtSettings.instanceUrl}/api/v1/accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`,this.mtSettings.hidePinnedPosts||(s.pinned=`${this.mtSettings.instanceUrl}/api/v1/accounts/${this.mtSettings.userId}/statuses?pinned=true`)):this.#o("Please check your userId value","⚠️"):"hashtag"===this.mtSettings.timelineType?this.mtSettings.hashtagName?s.timeline=`${this.mtSettings.instanceUrl}/api/v1/timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your hashtagName value","⚠️"):"local"===this.mtSettings.timelineType?s.timeline=`${this.mtSettings.instanceUrl}/api/v1/timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your timelineType value","⚠️"):this.#o("Please check your instanceUrl value","⚠️"),this.mtSettings.hideEmojos||(s.emojos=this.mtSettings.instanceUrl+"/api/v1/custom_emojis");const i=Object.entries(s).map((([t,s])=>async function(t){const e=await fetch(t);if(!e.ok)throw new Error("Failed to fetch the following Url:
"+t+"
Error status: "+e.status+"
Error message: "+e.statusText);return await e.json()}(s).then((e=>({[t]:e}))).catch((i=>(e(new Error("Something went wrong fetching data from: "+s)),this.#o(i.message),{[t]:[]})))));Promise.all(i).then((e=>{this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),t()}))}))}async#i(t){let e;if(await this.#a(),this.mtSettings.hidePinnedPosts||void 0===this.fetchedData.pinned?.length||0===this.fetchedData.pinned.length)e=this.fetchedData.timeline;else{e=[...this.fetchedData.pinned.map((t=>({...t,pinned:!0}))),...this.fetchedData.timeline]}this.mtBodyNode.replaceChildren();let s=0;for(let t in e)("public"==e[t].visibility||!this.mtSettings.hideUnlisted&&"unlisted"==e[t].visibility)&&(this.mtSettings.hideReblog&&e[t].reblog||this.mtSettings.hideReplies&&e[t].in_reply_to_id||sThis may be due to an incorrect configuration in the parameters or to filters applied (to hide certains type of posts)";this.#o(t,"📭")}else"newTimeline"===t?(this.#r(),this.#l(),this.#m()):"updateTimeline"===t?this.#r():this.#o("The function buildTimeline() was expecting a param")}#n(t,e){this.mtBodyNode.insertAdjacentHTML("beforeend",this.#d(t,e))}#d(t,e){let s,i,a,o,n,r,l,m,d,c;t.reblog?(n=t.reblog.url,s='
'+this.#c(t.reblog.account.username)+' avatar
'+this.#c(t.account.username)+' avatar
',a=!this.mtSettings.hideEmojos&&t.reblog.account.display_name?this.#h(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?"":'
",i='
'+a+""+o+"
",r=t.reblog.created_at,c=t.reblog.replies_count,d=t.reblog.reblogs_count,m=t.reblog.favourites_count):(n=t.url,s='
'+this.#c(t.account.username)+' avatar
',a=!this.mtSettings.hideEmojos&&t.account.display_name?this.#h(t.account.display_name,t.account.emojis):t.account.display_name?t.account.display_name:t.account.username,o=this.mtSettings.hideUserAccount?"":'
",i='
'+a+""+o+"
",r=t.created_at,c=t.replies_count,d=t.reblogs_count,m=t.favourites_count),l=this.#p(r);const h='
'+(t.pinned?'':"")+'"+(t.edited_at?" *":"")+"
";let p="";"0"!==this.mtSettings.txtMaxLines&&(p=" truncate",this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines));let g="";g=""!==t.spoiler_text?'
'+t.spoiler_text+'
'+this.#g(t.content)+"
":t.reblog&&""!==t.reblog.content&&""!==t.reblog.spoiler_text?'
'+t.reblog.spoiler_text+'
'+this.#g(t.reblog.content)+"
":t.reblog&&""!==t.reblog.content&&""===t.reblog.spoiler_text?'
'+this.#g(t.reblog.content)+"
":'
'+this.#g(t.content)+"
";let u=[];if(t.media_attachments.length>0)for(let e in t.media_attachments)u.push(this.#u(t.media_attachments[e],t.sensitive));if(t.reblog&&t.reblog.media_attachments.length>0)for(let e in t.reblog.media_attachments)u.push(this.#u(t.reblog.media_attachments[e],t.reblog.sensitive));let v="";!this.mtSettings.hidePreviewLink&&t.card&&(v=this.#v(t.card));let b="";if(t.poll){let e="";for(let s in t.poll.options)e+="
  • "+t.poll.options[s].title+"
  • ";b='
      '+e+"
    "}let w="";if(!this.mtSettings.hideCounterBar){w='
    '+('
    '+c+"
    ")+('
    '+d+"
    ")+('
    '+m+"
    ")+"
    "}return'
    '+s+i+h+"
    "+g+u.join("")+v+b+w+"
    "}#g(t){let e=t;return e=this.#b(e),this.mtSettings.hideEmojos||(e=this.#h(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#w(e,"

    >","

    ","

    ","

    ")),e}#b(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}#w(t,e,s,i,a){if(t.includes(e)){const o=new RegExp(e+"(.*?)"+s,"gi");return t.replace(o,i+"$1"+a)}return t}#c(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#h(t,e){if(t.includes(":")){for(const s of e){const e=new RegExp(`\\:${s.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${s.shortcode}`)}return t}return t}#p(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateLocale,this.mtSettings.dateOptions).format(e)}#u(t,e){const s=e||!1,i=t.type;let a="";return"image"===i&&(a='
    '+(s?'":"")+''+(t.description?this.#c(t.description):
    '),"audio"===i&&(a=t.preview_url?'
    '+(s?'":"")+''+(t.description?this.#c(t.description):
    ':'
    '+(s?'":"")+'
    '),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
    '+(s?'":"")+'
    ':'
    '+(s?'":"")+''+(t.description?this.#c(t.description):
    '),a}#S(t){const e=t.target.closest("[data-video-url]"),s=e.dataset.videoUrl;e.replaceChildren(),e.innerHTML=''}#f(t){const e=t.target.nextSibling;"img"===e.localName||"audio"===e.localName||"video"===e.localName?(t.target.parentNode.classList.remove("mt-post-media-spoiler"),t.target.style.display="none"):(e.classList.contains("spoiler-txt-hidden")||e.classList.contains("spoiler-txt-visible"))&&(t.target.textContent==this.mtSettings.btnShowMore?(e.classList.remove("spoiler-txt-hidden"),e.classList.add("spoiler-txt-visible"),t.target.setAttribute("aria-expanded","true"),t.target.textContent=this.mtSettings.btnShowLess):(e.classList.remove("spoiler-txt-visible"),e.classList.add("spoiler-txt-hidden"),t.target.setAttribute("aria-expanded","false"),t.target.textContent=this.mtSettings.btnShowMore))}#v(t){return''+(t.image?'
    '+this.#c(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#y(t.provider_name)+"":"")+''+t.title+""+(t.author_name?''+this.#y(t.author_name)+"":"")+"
    "}#y(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#m(){if(this.mtSettings.btnSeeMore||this.mtSettings.btnReload){this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'');const t=this.mtContainerNode.getElementsByClassName("mt-footer")[0];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");const s=''+this.mtSettings.btnSeeMore+"";t.insertAdjacentHTML("beforeend",s)}if(this.mtSettings.btnReload){const e='";t.insertAdjacentHTML("beforeend",e);this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}}#l(){this.mtBodyNode.addEventListener("click",(t=>{("article"==t.target.localName||"article"==t.target.offsetParent?.localName||"img"==t.target.localName&&!t.target.parentNode.getAttribute("data-video-url"))&&this.#N(t),"button"==t.target.localName&&t.target.classList.contains("mt-btn-spoiler")&&this.#f(t),("mt-post-media-play-icon"==t.target.className||"svg"==t.target.localName&&"mt-post-media-play-icon"==t.target.parentNode.className||"path"==t.target.localName&&"mt-post-media-play-icon"==t.target.parentNode.parentNode.className||"img"==t.target.localName&&t.target.parentNode.getAttribute("data-video-url"))&&this.#S(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{"Enter"===t.key&&"article"==t.target.localName&&this.#N(t)}))}#N(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&&"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-preview-image"!==t.target.parentNode.className&&"mt-post-preview"!==t.target.parentNode.className&&e&&window.open(e,"_blank","noopener")}#r(){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 s=e||"❌";throw this.mtBodyNode.innerHTML='
    '+s+'
    Oops, something\'s happened:
    '+t+"
    ",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",dateLocale:"en-GB",dateOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,hideEmojos:!1,hideVideoPreview:!1,hidePreviewLink:!1,hideCounterBar:!1,markdownBlockquote:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",txtMaxLines:"0",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",btnShowContent:"SHOW CONTENT",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#t((()=>{this.#e()}))}#t(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#e(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#i(),this.#s("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.#t((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
    '),this.#s("updateTimeline")}))}mtColorTheme(t){this.#t((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#i(){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)}#a(){return new Promise(((t,e)=>{const i=`${this.mtSettings.instanceUrl}/api/v1/`;let s={};this.mtSettings.instanceUrl?"profile"===this.mtSettings.timelineType?this.mtSettings.userId?(s.timeline=`${i}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`,this.mtSettings.hidePinnedPosts||(s.pinned=`${i}accounts/${this.mtSettings.userId}/statuses?pinned=true`)):this.#o("Please check your userId value","⚠️"):"hashtag"===this.mtSettings.timelineType?this.mtSettings.hashtagName?s.timeline=`${i}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your hashtagName value","⚠️"):"local"===this.mtSettings.timelineType?s.timeline=`${i}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your timelineType value","⚠️"):this.#o("Please check your instanceUrl value","⚠️"),this.mtSettings.hideEmojos||(s.emojos=`${i}custom_emojis`);const a=Object.entries(s).map((([t,i])=>this.#n(i).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((e=>{this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),t()}))}))}async#n(t){const e=await fetch(t);if(!e.ok)throw new Error(`\n Failed to fetch the following Url:
    ${t}
    Error status: ${e.status}
    Error message: ${e.statusText}\n `);return await e.json()}async#s(t){let e;if(await this.#a(),this.mtSettings.hidePinnedPosts||void 0===this.fetchedData.pinned?.length||0===this.fetchedData.pinned.length)e=this.fetchedData.timeline;else{e=[...this.fetchedData.pinned.map((t=>({...t,pinned:!0}))),...this.fetchedData.timeline]}this.mtBodyNode.replaceChildren();let i=0;for(let t in e)("public"==e[t].visibility||!this.mtSettings.hideUnlisted&&"unlisted"==e[t].visibility)&&(this.mtSettings.hideReblog&&e[t].reblog||this.mtSettings.hideReplies&&e[t].in_reply_to_id||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.#o(t,"📭")}else"newTimeline"===t?(this.#l(),this.#d(),this.#m()):"updateTimeline"===t?this.#l():this.#o("The function buildTimeline() was expecting a param")}#r(t,e){this.mtBodyNode.insertAdjacentHTML("beforeend",this.#h(t,e))}#h(t,e){let i,s,a,o,n,r,l,d,m,h;t.reblog?(n=t.reblog.url,i='
    '+this.#c(t.reblog.account.username)+' avatar
    '+this.#c(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.reblog.account.display_name?this.#p(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.#c(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.account.display_name?this.#p(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.#g(r);const c=`\n
    \n ${t.pinned?'':""}\n \n \n ${t.edited_at?" *":""}\n \n
    `;let p="";"0"!==this.mtSettings.txtMaxLines&&(p=" truncate",this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines));let g="";g=""!==t.spoiler_text?'
    '+t.spoiler_text+'
    '+this.#u(t.content)+"
    ":t.reblog&&""!==t.reblog.content&&""!==t.reblog.spoiler_text?'
    '+t.reblog.spoiler_text+'
    '+this.#u(t.reblog.content)+"
    ":t.reblog&&""!==t.reblog.content&&""===t.reblog.spoiler_text?'
    '+this.#u(t.reblog.content)+"
    ":'
    '+this.#u(t.content)+"
    ";let u=[];if(t.media_attachments.length>0)for(let e in t.media_attachments)u.push(this.#v(t.media_attachments[e],t.sensitive));if(t.reblog&&t.reblog.media_attachments.length>0)for(let e in t.reblog.media_attachments)u.push(this.#v(t.reblog.media_attachments[e],t.reblog.sensitive));u=`
    ${u.join("")}
    `;let v="";!this.mtSettings.hidePreviewLink&&t.card&&(v=this.#b(t.card));let b="";if(t.poll){let e="";for(let i in t.poll.options)e+="
  • "+t.poll.options[i].title+"
  • ";b='
      '+e+"
    "}let w="";if(!this.mtSettings.hideCounterBar){w='
    '+('
    '+h+"
    ")+('
    '+m+"
    ")+('
    '+d+"
    ")+"
    "}return'
    '+i+s+c+"
    "+g+u+v+b+w+"
    "}#u(t){let e=t;return e=this.#w(e),this.mtSettings.hideEmojos||(e=this.#p(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#f(e,"

    >","

    ","

    ","

    ")),e}#w(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}#f(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}#c(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#p(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}#g(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateLocale,this.mtSettings.dateOptions).format(e)}#v(t,e){const i=e||!1,s=t.type;let a="";return"image"===s&&(a='
    '+(i?'":"")+''+(t.description?this.#c(t.description):
    '),"audio"===s&&(a=t.preview_url?'
    '+(i?'":"")+''+(t.description?this.#c(t.description):
    ':'
    '+(i?'":"")+'
    '),"video"!==s&&"gifv"!==s||(a=this.mtSettings.hideVideoPreview?'
    '+(i?'":"")+'
    ':'
    '+(i?'":"")+''+(t.description?this.#c(t.description):
    '),a}#S(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)}))}#y(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.#S("mt-carousel",a),s.length>=2&&this.#x(e.length,i)}#x(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)}}#L(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#N(t){const e=t.target,i=e.nextSibling;"img"===i.localName||"audio"===i.localName||"video"===i.localName?(e.parentNode.classList.remove("mt-post-media-spoiler"),e.style.display="none"):(i.classList.contains("spoiler-txt-hidden")||i.classList.contains("spoiler-txt-visible"))&&(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))}#b(t){return''+(t.image?'
    '+this.#c(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#T(t.provider_name)+"":"")+''+t.title+""+(t.author_name?''+this.#T(t.author_name)+"":"")+"
    "}#T(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#m(){if(this.mtSettings.btnSeeMore||this.mtSettings.btnReload){this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'');const t=this.mtContainerNode.getElementsByClassName("mt-footer")[0];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");const i=`\n \n ${this.mtSettings.btnSeeMore}\n `;t.insertAdjacentHTML("beforeend",i)}if(this.mtSettings.btnReload){const e=`\n `;t.insertAdjacentHTML("beforeend",e);this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}}#d(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||"img"==i&&this.mtSettings.disableCarousel&&"video"!==s.getAttribute("data-media-type")&&"gifv"!==s.getAttribute("data-media-type"))&&this.#C(t),"button"==i&&e.classList.contains("mt-btn-spoiler")&&this.#N(t),"img"!=i||this.mtSettings.disableCarousel||"video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")||this.#y(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.#L(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#C(t)}))}#C(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-preview-image"!==t.target.parentNode.className&&"mt-post-preview"!==t.target.parentNode.className&&e&&window.open(e,"_blank","noopener")}#l(){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 093fa50..ed907dc 100644 --- a/dist/mastodon-timeline.min.css +++ b/dist/mastodon-timeline.min.css @@ -1 +1 @@ -.mt-container,.mt-container[data-theme=light]{--mt-txt-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-container[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{font:inherit}.mt-container a,.mt-container 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}.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 clamp(.25rem,4vw,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{position:relative;overflow:hidden;margin-bottom:1rem}.mt-post-media-spoiler>.mt-post-media-play-icon,.mt-post-media-spoiler>audio,.mt-post-media-spoiler>img,.mt-post-media-spoiler>video{filter:blur(2rem);pointer-events:none}.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%);text-align:center;color:var(--mt-color-content-txt)}.mt-post-media.mt-loading-spinner .mt-post-media-play-icon{display:none}.mt-post-media-play-icon{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-post-media-play-icon>svg{width:2.5rem;height:2.5rem;fill:var(--mt-color-bg);stroke:var(--mt-color-content-txt);stroke-width:1px}.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-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-container .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:0 .5rem;line-height:1.25rem;vertical-align:top}.mt-container .mt-btn-violet,.mt-container 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{background-color:var(--mt-color-btn-bg-hover);text-decoration:none}.mt-post-txt .mt-btn-spoiler{display:inline-block}.mt-post-media.mt-loading-spinner>.mt-btn-spoiler{display:none}.mt-post-media>.mt-btn-spoiler{position:absolute;top:50%;left:50%;z-index:2;transform:translate(-50%,-50%)}.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}.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 1.5rem;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} \ 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-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-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}.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 clamp(.25rem,4vw,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:flex;flex-direction:column;gap:1rem;margin-bottom:1rem}.mt-post-media{position:relative;overflow:hidden}.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%);text-align:center;color:var(--mt-color-content-txt)}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-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{display:inline-block;vertical-align:middle}.mt-post-media.mt-loading-spinner>.mt-btn-spoiler{display:none}.mt-post-media>.mt-btn-spoiler{position:absolute;top:50%;left:50%;z-index:2;transform:translate(-50%,-50%)}.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 1.5rem;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} \ No newline at end of file diff --git a/dist/mastodon-timeline.umd.js b/dist/mastodon-timeline.umd.js index d933cd6..748c8ba 100644 --- a/dist/mastodon-timeline.umd.js +++ b/dist/mastodon-timeline.umd.js @@ -2,7 +2,7 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.3.3 + * @version 4.3.5 * @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,hideEmojos:!1,hideVideoPreview:!1,hidePreviewLink:!1,hideCounterBar:!1,markdownBlockquote:!1,txtMaxLines:"0",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",btnShowContent:"SHOW CONTENT",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#t((()=>{this.#e()}))}#t(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#e(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#i("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),s=()=>{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`)}};s()}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.#t((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
    '),this.#i("updateTimeline")}))}mtColorTheme(t){this.#t((()=>{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)}#a(){return new Promise(((t,e)=>{let s={};this.mtSettings.instanceUrl?"profile"===this.mtSettings.timelineType?this.mtSettings.userId?(s.timeline=`${this.mtSettings.instanceUrl}/api/v1/accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`,this.mtSettings.hidePinnedPosts||(s.pinned=`${this.mtSettings.instanceUrl}/api/v1/accounts/${this.mtSettings.userId}/statuses?pinned=true`)):this.#o("Please check your userId value","⚠️"):"hashtag"===this.mtSettings.timelineType?this.mtSettings.hashtagName?s.timeline=`${this.mtSettings.instanceUrl}/api/v1/timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your hashtagName value","⚠️"):"local"===this.mtSettings.timelineType?s.timeline=`${this.mtSettings.instanceUrl}/api/v1/timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your timelineType value","⚠️"):this.#o("Please check your instanceUrl value","⚠️"),this.mtSettings.hideEmojos||(s.emojos=this.mtSettings.instanceUrl+"/api/v1/custom_emojis");const i=Object.entries(s).map((([t,s])=>async function(t){const e=await fetch(t);if(!e.ok)throw new Error("Failed to fetch the following Url:
    "+t+"
    Error status: "+e.status+"
    Error message: "+e.statusText);return await e.json()}(s).then((e=>({[t]:e}))).catch((i=>(e(new Error("Something went wrong fetching data from: "+s)),this.#o(i.message),{[t]:[]})))));Promise.all(i).then((e=>{this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),t()}))}))}async#i(t){let e;if(await this.#a(),this.mtSettings.hidePinnedPosts||void 0===this.fetchedData.pinned?.length||0===this.fetchedData.pinned.length)e=this.fetchedData.timeline;else{e=[...this.fetchedData.pinned.map((t=>({...t,pinned:!0}))),...this.fetchedData.timeline]}this.mtBodyNode.replaceChildren();let s=0;for(let t in e)("public"==e[t].visibility||!this.mtSettings.hideUnlisted&&"unlisted"==e[t].visibility)&&(this.mtSettings.hideReblog&&e[t].reblog||this.mtSettings.hideReplies&&e[t].in_reply_to_id||sThis may be due to an incorrect configuration in the parameters or to filters applied (to hide certains type of posts)";this.#o(t,"📭")}else"newTimeline"===t?(this.#r(),this.#l(),this.#m()):"updateTimeline"===t?this.#r():this.#o("The function buildTimeline() was expecting a param")}#n(t,e){this.mtBodyNode.insertAdjacentHTML("beforeend",this.#d(t,e))}#d(t,e){let s,i,a,o,n,r,l,m,d,c;t.reblog?(n=t.reblog.url,s='
    '+this.#c(t.reblog.account.username)+' avatar
    '+this.#c(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.reblog.account.display_name?this.#h(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?"":'
    ",i='
    '+a+""+o+"
    ",r=t.reblog.created_at,c=t.reblog.replies_count,d=t.reblog.reblogs_count,m=t.reblog.favourites_count):(n=t.url,s='
    '+this.#c(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.account.display_name?this.#h(t.account.display_name,t.account.emojis):t.account.display_name?t.account.display_name:t.account.username,o=this.mtSettings.hideUserAccount?"":'
    ",i='
    '+a+""+o+"
    ",r=t.created_at,c=t.replies_count,d=t.reblogs_count,m=t.favourites_count),l=this.#p(r);const h='
    '+(t.pinned?'':"")+'"+(t.edited_at?" *":"")+"
    ";let p="";"0"!==this.mtSettings.txtMaxLines&&(p=" truncate",this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines));let g="";g=""!==t.spoiler_text?'
    '+t.spoiler_text+'
    '+this.#g(t.content)+"
    ":t.reblog&&""!==t.reblog.content&&""!==t.reblog.spoiler_text?'
    '+t.reblog.spoiler_text+'
    '+this.#g(t.reblog.content)+"
    ":t.reblog&&""!==t.reblog.content&&""===t.reblog.spoiler_text?'
    '+this.#g(t.reblog.content)+"
    ":'
    '+this.#g(t.content)+"
    ";let u=[];if(t.media_attachments.length>0)for(let e in t.media_attachments)u.push(this.#u(t.media_attachments[e],t.sensitive));if(t.reblog&&t.reblog.media_attachments.length>0)for(let e in t.reblog.media_attachments)u.push(this.#u(t.reblog.media_attachments[e],t.reblog.sensitive));let v="";!this.mtSettings.hidePreviewLink&&t.card&&(v=this.#v(t.card));let b="";if(t.poll){let e="";for(let s in t.poll.options)e+="
  • "+t.poll.options[s].title+"
  • ";b='
      '+e+"
    "}let f="";if(!this.mtSettings.hideCounterBar){f='
    '+('
    '+c+"
    ")+('
    '+d+"
    ")+('
    '+m+"
    ")+"
    "}return'
    '+s+i+h+"
    "+g+u.join("")+v+b+f+"
    "}#g(t){let e=t;return e=this.#b(e),this.mtSettings.hideEmojos||(e=this.#h(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#f(e,"

    >","

    ","

    ","

    ")),e}#b(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}#f(t,e,s,i,a){if(t.includes(e)){const o=new RegExp(e+"(.*?)"+s,"gi");return t.replace(o,i+"$1"+a)}return t}#c(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#h(t,e){if(t.includes(":")){for(const s of e){const e=new RegExp(`\\:${s.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${s.shortcode}`)}return t}return t}#p(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateLocale,this.mtSettings.dateOptions).format(e)}#u(t,e){const s=e||!1,i=t.type;let a="";return"image"===i&&(a='
    '+(s?'":"")+''+(t.description?this.#c(t.description):
    '),"audio"===i&&(a=t.preview_url?'
    '+(s?'":"")+''+(t.description?this.#c(t.description):
    ':'
    '+(s?'":"")+'
    '),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
    '+(s?'":"")+'
    ':'
    '+(s?'":"")+''+(t.description?this.#c(t.description):
    '),a}#w(t){const e=t.target.closest("[data-video-url]"),s=e.dataset.videoUrl;e.replaceChildren(),e.innerHTML=''}#S(t){const e=t.target.nextSibling;"img"===e.localName||"audio"===e.localName||"video"===e.localName?(t.target.parentNode.classList.remove("mt-post-media-spoiler"),t.target.style.display="none"):(e.classList.contains("spoiler-txt-hidden")||e.classList.contains("spoiler-txt-visible"))&&(t.target.textContent==this.mtSettings.btnShowMore?(e.classList.remove("spoiler-txt-hidden"),e.classList.add("spoiler-txt-visible"),t.target.setAttribute("aria-expanded","true"),t.target.textContent=this.mtSettings.btnShowLess):(e.classList.remove("spoiler-txt-visible"),e.classList.add("spoiler-txt-hidden"),t.target.setAttribute("aria-expanded","false"),t.target.textContent=this.mtSettings.btnShowMore))}#v(t){return''+(t.image?'
    '+this.#c(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#y(t.provider_name)+"":"")+''+t.title+""+(t.author_name?''+this.#y(t.author_name)+"":"")+"
    "}#y(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#m(){if(this.mtSettings.btnSeeMore||this.mtSettings.btnReload){this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'');const t=this.mtContainerNode.getElementsByClassName("mt-footer")[0];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");const s=''+this.mtSettings.btnSeeMore+"";t.insertAdjacentHTML("beforeend",s)}if(this.mtSettings.btnReload){const e='";t.insertAdjacentHTML("beforeend",e);this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}}#l(){this.mtBodyNode.addEventListener("click",(t=>{("article"==t.target.localName||"article"==t.target.offsetParent?.localName||"img"==t.target.localName&&!t.target.parentNode.getAttribute("data-video-url"))&&this.#N(t),"button"==t.target.localName&&t.target.classList.contains("mt-btn-spoiler")&&this.#S(t),("mt-post-media-play-icon"==t.target.className||"svg"==t.target.localName&&"mt-post-media-play-icon"==t.target.parentNode.className||"path"==t.target.localName&&"mt-post-media-play-icon"==t.target.parentNode.parentNode.className||"img"==t.target.localName&&t.target.parentNode.getAttribute("data-video-url"))&&this.#w(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{"Enter"===t.key&&"article"==t.target.localName&&this.#N(t)}))}#N(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&&"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-preview-image"!==t.target.parentNode.className&&"mt-post-preview"!==t.target.parentNode.className&&e&&window.open(e,"_blank","noopener")}#r(){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 s=e||"❌";throw this.mtBodyNode.innerHTML='
    '+s+'
    Oops, something\'s happened:
    '+t+"
    ",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",dateLocale:"en-GB",dateOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,hideEmojos:!1,hideVideoPreview:!1,hidePreviewLink:!1,hideCounterBar:!1,markdownBlockquote:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",txtMaxLines:"0",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",btnShowContent:"SHOW CONTENT",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#t((()=>{this.#e()}))}#t(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#e(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#i(),this.#s("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.#t((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
    '),this.#s("updateTimeline")}))}mtColorTheme(t){this.#t((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#i(){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)}#a(){return new Promise(((t,e)=>{const i=`${this.mtSettings.instanceUrl}/api/v1/`;let s={};this.mtSettings.instanceUrl?"profile"===this.mtSettings.timelineType?this.mtSettings.userId?(s.timeline=`${i}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`,this.mtSettings.hidePinnedPosts||(s.pinned=`${i}accounts/${this.mtSettings.userId}/statuses?pinned=true`)):this.#o("Please check your userId value","⚠️"):"hashtag"===this.mtSettings.timelineType?this.mtSettings.hashtagName?s.timeline=`${i}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your hashtagName value","⚠️"):"local"===this.mtSettings.timelineType?s.timeline=`${i}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`:this.#o("Please check your timelineType value","⚠️"):this.#o("Please check your instanceUrl value","⚠️"),this.mtSettings.hideEmojos||(s.emojos=`${i}custom_emojis`);const a=Object.entries(s).map((([t,i])=>this.#n(i).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((e=>{this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),t()}))}))}async#n(t){const e=await fetch(t);if(!e.ok)throw new Error(`\n Failed to fetch the following Url:
    ${t}
    Error status: ${e.status}
    Error message: ${e.statusText}\n `);return await e.json()}async#s(t){let e;if(await this.#a(),this.mtSettings.hidePinnedPosts||void 0===this.fetchedData.pinned?.length||0===this.fetchedData.pinned.length)e=this.fetchedData.timeline;else{e=[...this.fetchedData.pinned.map((t=>({...t,pinned:!0}))),...this.fetchedData.timeline]}this.mtBodyNode.replaceChildren();let i=0;for(let t in e)("public"==e[t].visibility||!this.mtSettings.hideUnlisted&&"unlisted"==e[t].visibility)&&(this.mtSettings.hideReblog&&e[t].reblog||this.mtSettings.hideReplies&&e[t].in_reply_to_id||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.#o(t,"📭")}else"newTimeline"===t?(this.#l(),this.#d(),this.#m()):"updateTimeline"===t?this.#l():this.#o("The function buildTimeline() was expecting a param")}#r(t,e){this.mtBodyNode.insertAdjacentHTML("beforeend",this.#h(t,e))}#h(t,e){let i,s,a,o,n,r,l,d,m,h;t.reblog?(n=t.reblog.url,i='
    '+this.#c(t.reblog.account.username)+' avatar
    '+this.#c(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.reblog.account.display_name?this.#p(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.#c(t.account.username)+' avatar
    ',a=!this.mtSettings.hideEmojos&&t.account.display_name?this.#p(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.#g(r);const c=`\n
    \n ${t.pinned?'':""}\n \n \n ${t.edited_at?" *":""}\n \n
    `;let p="";"0"!==this.mtSettings.txtMaxLines&&(p=" truncate",this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines));let g="";g=""!==t.spoiler_text?'
    '+t.spoiler_text+'
    '+this.#u(t.content)+"
    ":t.reblog&&""!==t.reblog.content&&""!==t.reblog.spoiler_text?'
    '+t.reblog.spoiler_text+'
    '+this.#u(t.reblog.content)+"
    ":t.reblog&&""!==t.reblog.content&&""===t.reblog.spoiler_text?'
    '+this.#u(t.reblog.content)+"
    ":'
    '+this.#u(t.content)+"
    ";let u=[];if(t.media_attachments.length>0)for(let e in t.media_attachments)u.push(this.#v(t.media_attachments[e],t.sensitive));if(t.reblog&&t.reblog.media_attachments.length>0)for(let e in t.reblog.media_attachments)u.push(this.#v(t.reblog.media_attachments[e],t.reblog.sensitive));u=`
    ${u.join("")}
    `;let v="";!this.mtSettings.hidePreviewLink&&t.card&&(v=this.#b(t.card));let b="";if(t.poll){let e="";for(let i in t.poll.options)e+="
  • "+t.poll.options[i].title+"
  • ";b='
      '+e+"
    "}let w="";if(!this.mtSettings.hideCounterBar){w='
    '+('
    '+h+"
    ")+('
    '+m+"
    ")+('
    '+d+"
    ")+"
    "}return'
    '+i+s+c+"
    "+g+u+v+b+w+"
    "}#u(t){let e=t;return e=this.#w(e),this.mtSettings.hideEmojos||(e=this.#p(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#f(e,"

    >","

    ","

    ","

    ")),e}#w(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}#f(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}#c(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#p(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}#g(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateLocale,this.mtSettings.dateOptions).format(e)}#v(t,e){const i=e||!1,s=t.type;let a="";return"image"===s&&(a='
    '+(i?'":"")+''+(t.description?this.#c(t.description):
    '),"audio"===s&&(a=t.preview_url?'
    '+(i?'":"")+''+(t.description?this.#c(t.description):
    ':'
    '+(i?'":"")+'
    '),"video"!==s&&"gifv"!==s||(a=this.mtSettings.hideVideoPreview?'
    '+(i?'":"")+'
    ':'
    '+(i?'":"")+''+(t.description?this.#c(t.description):
    '),a}#S(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)}))}#y(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.#S("mt-carousel",a),s.length>=2&&this.#x(e.length,i)}#x(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)}}#L(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#N(t){const e=t.target,i=e.nextSibling;"img"===i.localName||"audio"===i.localName||"video"===i.localName?(e.parentNode.classList.remove("mt-post-media-spoiler"),e.style.display="none"):(i.classList.contains("spoiler-txt-hidden")||i.classList.contains("spoiler-txt-visible"))&&(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))}#b(t){return''+(t.image?'
    '+this.#c(t.image_description)+'
    ':'
    📄
    ')+'
    '+(t.provider_name?''+this.#T(t.provider_name)+"":"")+''+t.title+""+(t.author_name?''+this.#T(t.author_name)+"":"")+"
    "}#T(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#m(){if(this.mtSettings.btnSeeMore||this.mtSettings.btnReload){this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'');const t=this.mtContainerNode.getElementsByClassName("mt-footer")[0];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");const i=`\n \n ${this.mtSettings.btnSeeMore}\n `;t.insertAdjacentHTML("beforeend",i)}if(this.mtSettings.btnReload){const e=`\n `;t.insertAdjacentHTML("beforeend",e);this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}}#d(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||"img"==i&&this.mtSettings.disableCarousel&&"video"!==s.getAttribute("data-media-type")&&"gifv"!==s.getAttribute("data-media-type"))&&this.#C(t),"button"==i&&e.classList.contains("mt-btn-spoiler")&&this.#N(t),"img"!=i||this.mtSettings.disableCarousel||"video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")||this.#y(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.#L(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#C(t)}))}#C(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-preview-image"!==t.target.parentNode.className&&"mt-post-preview"!==t.target.parentNode.className&&e&&window.open(e,"_blank","noopener")}#l(){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/examples/local-timeline-customized.html b/examples/local-timeline-customized.html index d9f8f43..60eba96 100644 --- a/examples/local-timeline-customized.html +++ b/examples/local-timeline-customized.html @@ -60,24 +60,42 @@ margin: 2rem 0; } - /* Customized CSS styles */ + /* Example of customized CSS styles */ .mt-container { + font-family: monospace; background-color: transparent; border: 1px solid white; } - .mt-post { - border-bottom: none; - box-shadow: 2px 2px 6px 2px rgba(0, 0, 0, 0.25); - margin-bottom: 1rem; - } - .mt-post-avatar-image-big img { - border-radius: 0; - } .mt-container a, .mt-container a:active, .mt-container a:link { color: darkgreen; } + .mt-post-header { + margin-bottom: 0; + } + .mt-post-avatar-image-big img { + border-radius: 0; + } + .mt-post-header-user { + margin-right: auto; + } + .mt-post-header-date { + position: absolute; + bottom: 1rem; + left: 3.5rem; + } + .mt-post { + border-bottom: none; + box-shadow: 2px 2px 6px 2px rgba(0, 0, 0, 0.25); + margin-bottom: 1rem; + padding-bottom: 2rem; + } + .mt-post-txt, + .mt-post-media-wrapper { + width: calc(100% - 3rem); + margin-left: auto; + } @@ -105,8 +123,7 @@

    At JS level, it defaults to the light theme and the date is displayed in US format using digits only. In order to achieve a minimalist - style, the following options have been changed at its - initialization: + style, the following options have been changed at its initialization:

             
    diff --git a/package-lock.json b/package-lock.json
    index d4d6e8b..2759e1a 100644
    --- a/package-lock.json
    +++ b/package-lock.json
    @@ -1,12 +1,12 @@
     {
       "name": "@idotj/mastodon-embed-timeline",
    -  "version": "4.3.3",
    +  "version": "4.3.5",
       "lockfileVersion": 3,
       "requires": true,
       "packages": {
         "": {
           "name": "@idotj/mastodon-embed-timeline",
    -      "version": "4.3.3",
    +      "version": "4.3.5",
           "license": "GNU",
           "devDependencies": {
             "@rollup/plugin-terser": "^0.4.4",
    @@ -15,14 +15,14 @@
           }
         },
         "node_modules/@jridgewell/gen-mapping": {
    -      "version": "0.3.3",
    -      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
    -      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
    +      "version": "0.3.5",
    +      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
    +      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
           "dev": true,
           "dependencies": {
    -        "@jridgewell/set-array": "^1.0.1",
    +        "@jridgewell/set-array": "^1.2.1",
             "@jridgewell/sourcemap-codec": "^1.4.10",
    -        "@jridgewell/trace-mapping": "^0.3.9"
    +        "@jridgewell/trace-mapping": "^0.3.24"
           },
           "engines": {
             "node": ">=6.0.0"
    @@ -38,9 +38,9 @@
           }
         },
         "node_modules/@jridgewell/set-array": {
    -      "version": "1.1.2",
    -      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
    -      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
    +      "version": "1.2.1",
    +      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
    +      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
           "dev": true,
           "engines": {
             "node": ">=6.0.0"
    @@ -63,9 +63,9 @@
           "dev": true
         },
         "node_modules/@jridgewell/trace-mapping": {
    -      "version": "0.3.22",
    -      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
    -      "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
    +      "version": "0.3.24",
    +      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.24.tgz",
    +      "integrity": "sha512-+VaWXDa6+l6MhflBvVXjIEAzb59nQ2JUK3bwRp2zRpPtU+8TFRy9Gg/5oIcNlkEL5PGlBFGfemUVvIgLnTzq7Q==",
           "dev": true,
           "dependencies": {
             "@jridgewell/resolve-uri": "^3.1.0",
    @@ -94,6 +94,110 @@
             }
           }
         },
    +    "node_modules/@rollup/rollup-android-arm-eabi": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz",
    +      "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==",
    +      "cpu": [
    +        "arm"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "android"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-android-arm64": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz",
    +      "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==",
    +      "cpu": [
    +        "arm64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "android"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-darwin-arm64": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz",
    +      "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==",
    +      "cpu": [
    +        "arm64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "darwin"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-darwin-x64": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz",
    +      "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==",
    +      "cpu": [
    +        "x64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "darwin"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz",
    +      "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==",
    +      "cpu": [
    +        "arm"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "linux"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-linux-arm64-gnu": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz",
    +      "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==",
    +      "cpu": [
    +        "arm64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "linux"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-linux-arm64-musl": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz",
    +      "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==",
    +      "cpu": [
    +        "arm64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "linux"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz",
    +      "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==",
    +      "cpu": [
    +        "riscv64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "linux"
    +      ]
    +    },
         "node_modules/@rollup/rollup-linux-x64-gnu": {
           "version": "4.12.0",
           "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz",
    @@ -120,6 +224,45 @@
             "linux"
           ]
         },
    +    "node_modules/@rollup/rollup-win32-arm64-msvc": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz",
    +      "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==",
    +      "cpu": [
    +        "arm64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "win32"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-win32-ia32-msvc": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz",
    +      "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==",
    +      "cpu": [
    +        "ia32"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "win32"
    +      ]
    +    },
    +    "node_modules/@rollup/rollup-win32-x64-msvc": {
    +      "version": "4.12.0",
    +      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz",
    +      "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==",
    +      "cpu": [
    +        "x64"
    +      ],
    +      "dev": true,
    +      "optional": true,
    +      "os": [
    +        "win32"
    +      ]
    +    },
         "node_modules/@types/estree": {
           "version": "1.0.5",
           "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
    @@ -281,6 +424,20 @@
           "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
           "dev": true
         },
    +    "node_modules/fsevents": {
    +      "version": "2.3.3",
    +      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
    +      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
    +      "dev": true,
    +      "hasInstallScript": true,
    +      "optional": true,
    +      "os": [
    +        "darwin"
    +      ],
    +      "engines": {
    +        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
    +      }
    +    },
         "node_modules/glob": {
           "version": "7.2.3",
           "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
    @@ -530,9 +687,9 @@
           }
         },
         "node_modules/terser": {
    -      "version": "5.27.1",
    -      "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.1.tgz",
    -      "integrity": "sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==",
    +      "version": "5.28.1",
    +      "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
    +      "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
           "dev": true,
           "dependencies": {
             "@jridgewell/source-map": "^0.3.3",
    diff --git a/package.json b/package.json
    index d092105..f9e417f 100644
    --- a/package.json
    +++ b/package.json
    @@ -1,6 +1,6 @@
     {
       "name": "@idotj/mastodon-embed-timeline",
    -  "version": "4.3.3",
    +  "version": "4.3.5",
       "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.",
       "license": "GNU",
       "author": {
    diff --git a/rollup.config.js b/rollup.config.js
    index 37f1f63..4ebf294 100644
    --- a/rollup.config.js
    +++ b/rollup.config.js
    @@ -1,20 +1,20 @@
    -import terser from "@rollup/plugin-terser";
    -
    -export default [
    -  {
    -    input: "src/mastodon-timeline.js",
    -    output: [
    -      {
    -        file: "dist/mastodon-timeline.esm.js",
    -        format: "esm",
    -        sourcemap: false,
    -      },
    -      {
    -        name: "MastodonTimeline",
    -        file: "dist/mastodon-timeline.umd.js",
    -        format: "umd",
    -      },
    -    ],
    -    plugins: [terser()],
    -  },
    -];
    +import terser from "@rollup/plugin-terser";
    +
    +export default [
    +  {
    +    input: "src/mastodon-timeline.js",
    +    output: [
    +      {
    +        file: "dist/mastodon-timeline.esm.js",
    +        format: "esm",
    +        sourcemap: false,
    +      },
    +      {
    +        name: "MastodonTimeline",
    +        file: "dist/mastodon-timeline.umd.js",
    +        format: "umd",
    +      },
    +    ],
    +    plugins: [terser()],
    +  },
    +];
    diff --git a/src/mastodon-timeline.css b/src/mastodon-timeline.css
    index 6f58a5a..beff56a 100644
    --- a/src/mastodon-timeline.css
    +++ b/src/mastodon-timeline.css
    @@ -1,10 +1,12 @@
    -/* Mastodon embed timeline v4.3.3 */
    +/* Mastodon embed timeline v4.3.5 */
     /* More info at: */
     /* https://gitlab.com/idotj/mastodon-embed-timeline */
     
     /* Variables */
     .mt-container,
    -.mt-container[data-theme="light"] {
    +.mt-dialog,
    +.mt-container[data-theme="light"],
    +.mt-dialog[data-theme="light"] {
       --mt-txt-max-lines: none;
       --mt-color-bg: #fff;
       --mt-color-bg-hover: #d9e1e8;
    @@ -16,8 +18,10 @@
       --mt-color-btn-bg: #6364ff;
       --mt-color-btn-bg-hover: #563acc;
       --mt-color-btn-txt: #fff;
    +  --mt-color-backdrop: #00000090;
     }
    -.mt-container[data-theme="dark"] {
    +.mt-container[data-theme="dark"],
    +.mt-dialog[data-theme="dark"] {
       --mt-color-bg: #282c37;
       --mt-color-bg-hover: #313543;
       --mt-color-line-gray: #393f4f;
    @@ -28,11 +32,13 @@
     }
     
     /* Reset CSS */
    -.mt-container button {
    +.mt-container button,
    +.mt-dialog button {
       font: inherit;
     }
     .mt-container a,
    -.mt-container button {
    +.mt-container button,
    +.mt-dialog button {
       cursor: pointer;
     }
     
    @@ -254,18 +260,30 @@
     }
     
     /* Medias */
    +.mt-post-media-wrapper {
    +  display: flex;
    +  flex-direction: column;
    +  gap: 1rem;
    +  margin-bottom: 1rem;
    +}
     .mt-post-media {
       position: relative;
       overflow: hidden;
    -  margin-bottom: 1rem;
     }
     .mt-post-media-spoiler > img,
     .mt-post-media-spoiler > audio,
     .mt-post-media-spoiler > video,
    -.mt-post-media-spoiler > .mt-post-media-play-icon {
    +.mt-post-media-spoiler > .mt-btn-play {
       filter: blur(2rem);
       pointer-events: none;
     }
    +.mt-post-media,
    +.mt-post-media-spoiler > img,
    +.mt-post-media-spoiler > audio,
    +.mt-post-media > img,
    +.mt-post-media > video {
    +  border-radius: 0.5rem;
    +}
     .mt-post-media > audio {
       width: 100%;
       position: relative;
    @@ -282,28 +300,93 @@
       text-align: center;
       color: var(--mt-color-content-txt);
     }
    -.mt-post-media.mt-loading-spinner .mt-post-media-play-icon {
    -  display: none;
    +
    +/* Dialog - modal */
    +body:has(dialog.mt-dialog[open]) {
    +  overflow: hidden;
     }
    -.mt-post-media-play-icon {
    +.mt-dialog {
       display: flex;
    -  position: absolute;
    -  width: 3rem;
    -  height: 3rem;
    -  top: calc(50% - 1.5rem);
    -  left: calc(50% - 1.5rem);
    -  justify-content: center;
    +  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);
    +}
    +
    +/* Carousel/lightbox */
    +.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-prev,
    +.mt-carousel-next {
    +  position: absolute;
       background-color: transparent;
       border: none;
    -  cursor: pointer;
    +  padding: 0.5rem;
    +  z-index: 2;
     }
    -.mt-post-media-play-icon > svg {
    -  width: 2.5rem;
    -  height: 2.5rem;
    -  fill: var(--mt-color-bg);
    -  stroke: var(--mt-color-content-txt);
    -  stroke-width: 1px;
    +.mt-carousel-prev {
    +  left: 0;
    +  padding-left: 0;
    +}
    +.mt-carousel-next {
    +  right: 0;
    +  padding-right: 0;
     }
     
     /* Preview link */
    @@ -372,7 +455,31 @@
     }
     
     /* Buttons */
    -.mt-container .mt-btn-dark {
    +.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: 0.25rem;
       background-color: var(--mt-color-line-gray);
    @@ -381,11 +488,15 @@
       font-weight: 600;
       font-size: 0.75rem;
       text-align: center;
    -  padding: 0 0.5rem;
    +  padding: 0.25rem 0.5rem;
       line-height: 1.25rem;
    -
       vertical-align: top;
     }
    +.mt-dialog .mt-btn-dark {
    +  margin-left: auto;
    +}
    +.mt-dialog .mt-btn-violet,
    +.mt-dialog a.mt-btn-violet,
     .mt-container .mt-btn-violet,
     .mt-container a.mt-btn-violet {
       display: flex;
    @@ -400,6 +511,8 @@
       background-color: var(--mt-color-btn-bg);
       color: var(--mt-color-btn-txt);
     }
    +.mt-dialog .mt-btn-violet:hover,
    +.mt-dialog a.mt-btn-violet:hover,
     .mt-container .mt-btn-violet:hover,
     .mt-container a.mt-btn-violet:hover {
       background-color: var(--mt-color-btn-bg-hover);
    @@ -407,6 +520,7 @@
     }
     .mt-post-txt .mt-btn-spoiler {
       display: inline-block;
    +  vertical-align: middle;
     }
     .mt-post-media.mt-loading-spinner > .mt-btn-spoiler {
       display: none;
    @@ -436,6 +550,7 @@
     }
     .mt-error-icon {
       font-size: 2rem;
    +  margin-bottom: 1rem;
     }
     .mt-error-message {
       width: 100%;
    diff --git a/src/mastodon-timeline.js b/src/mastodon-timeline.js
    index 3679e4a..bd0cd8e 100644
    --- a/src/mastodon-timeline.js
    +++ b/src/mastodon-timeline.js
    @@ -1,7 +1,7 @@
     /**
      * Mastodon embed timeline
      * @author idotj
    - * @version 4.3.3
    + * @version 4.3.5
      * @url https://gitlab.com/idotj/mastodon-embed-timeline
      * @license GNU AGPLv3
      */
    @@ -36,6 +36,10 @@ export class Init {
           hidePreviewLink: false,
           hideCounterBar: false,
           markdownBlockquote: false,
    +      disableCarousel: false,
    +      carouselCloseTxt: "Close carousel",
    +      carouselPrevTxt: "Previous media item",
    +      carouselNextTxt: "Next media item",
           txtMaxLines: "0",
           btnShowMore: "SHOW MORE",
           btnShowLess: "SHOW LESS",
    @@ -172,42 +176,17 @@ export class Init {
        * Requests to the server to collect all the data
        * @returns {object} Data container
        */
    -  #fetchTimelineData() {
    +  #getTimelineData() {
         return new Promise((resolve, reject) => {
    -      /**
    -       * Fetch data from server
    -       * @param {string} url address to fetch
    -       * @returns {array} List of objects
    -       */
    -      async function fetchData(url) {
    -        const response = await fetch(url);
    -
    -        if (!response.ok) {
    -          throw new Error(
    -            "Failed to fetch the following Url: 
    " + - url + - "
    " + - "Error status: " + - response.status + - "
    " + - "Error message: " + - response.statusText - ); - } - - const data = await response.json(); - return data; - } - - // Urls to fetch + const instanceApiUrl = `${this.mtSettings.instanceUrl}/api/v1/`; let urls = {}; if (this.mtSettings.instanceUrl) { if (this.mtSettings.timelineType === "profile") { if (this.mtSettings.userId) { - urls.timeline = `${this.mtSettings.instanceUrl}/api/v1/accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`; + urls.timeline = `${instanceApiUrl}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`; if (!this.mtSettings.hidePinnedPosts) { - urls.pinned = `${this.mtSettings.instanceUrl}/api/v1/accounts/${this.mtSettings.userId}/statuses?pinned=true`; + urls.pinned = `${instanceApiUrl}accounts/${this.mtSettings.userId}/statuses?pinned=true`; } } else { this.#showError( @@ -217,7 +196,7 @@ export class Init { } } else if (this.mtSettings.timelineType === "hashtag") { if (this.mtSettings.hashtagName) { - urls.timeline = `${this.mtSettings.instanceUrl}/api/v1/timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`; + urls.timeline = `${instanceApiUrl}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`; } else { this.#showError( "Please check your hashtagName value", @@ -225,7 +204,7 @@ export class Init { ); } } else if (this.mtSettings.timelineType === "local") { - urls.timeline = `${this.mtSettings.instanceUrl}/api/v1/timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`; + urls.timeline = `${instanceApiUrl}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`; } else { this.#showError( "Please check your timelineType value", @@ -239,15 +218,15 @@ export class Init { ); } if (!this.mtSettings.hideEmojos) { - urls.emojos = this.mtSettings.instanceUrl + "/api/v1/custom_emojis"; + urls.emojos = `${instanceApiUrl}custom_emojis`; } const urlsPromises = Object.entries(urls).map(([key, url]) => { - return fetchData(url) + return this.#fetchData(url) .then((data) => ({ [key]: data })) .catch((error) => { reject( - new Error("Something went wrong fetching data from: " + url) + new Error(`Something went wrong fetching data from: ${url}`) ); this.#showError(error.message); return { [key]: [] }; @@ -266,12 +245,30 @@ export class Init { }); } + /** + * Fetch data from server + * @param {string} url address to fetch + * @returns {array} List of objects + */ + async #fetchData(url) { + const response = await fetch(url); + + if (!response.ok) { + throw new Error(` + Failed to fetch the following Url:
    ${url}
    Error status: ${response.status}
    Error message: ${response.statusText} + `); + } + + const data = await response.json(); + return data; + } + /** * Filter all fetched posts and append them on the timeline * @param {string} t Type of build (new or reload) */ async #buildTimeline(t) { - await this.#fetchTimelineData(); + await this.#getTimelineData(); // Merge pinned posts with timeline posts let posts; @@ -320,10 +317,9 @@ export class Init { // If there are no posts to display, show an error message if (this.mtBodyNode.innerHTML === "") { - const errorMessage = - "No posts to show
    " + - (posts?.length || 0) + - " posts have been fetched from the server
    This may be due to an incorrect configuration in the parameters or to filters applied (to hide certains type of posts)"; + const errorMessage = `No posts to show
    ${ + posts?.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.#showError(errorMessage, "📭"); } else { if (t === "newTimeline") { @@ -379,14 +375,14 @@ export class Init { '' +
-        this.#escapeHtml(c.reblog.account.username) +
+        this.#escapeHTML(c.reblog.account.username) +
         ' avatar' + "" + '
    ' + '' +
-        this.#escapeHtml(c.account.username) +
+        this.#escapeHTML(c.account.username) +
         ' avatar' + "
    " + "" + @@ -399,7 +395,9 @@ export class Init { c.reblog.account.emojis ); } else { - userName = c.reblog.account.display_name ? c.reblog.account.display_name : c.reblog.account.username; + userName = c.reblog.account.display_name + ? c.reblog.account.display_name + : c.reblog.account.username; } if (!this.mtSettings.hideUserAccount) { @@ -446,7 +444,7 @@ export class Init { '' +
-        this.#escapeHtml(c.account.username) +
+        this.#escapeHTML(c.account.username) +
         ' avatar' + "" + "" + @@ -459,7 +457,9 @@ export class Init { c.account.emojis ); } else { - userName = c.account.display_name ? c.account.display_name : c.account.username; + userName = c.account.display_name + ? c.account.display_name + : c.account.username; } if (!this.mtSettings.hideUserAccount) { @@ -495,22 +495,20 @@ export class Init { // Date formattedDate = this.#formatDate(date); - const timestamp = - '
    ' + - (c.pinned - ? '' - : "") + - '' + - '" + - (c.edited_at ? " *" : "") + - "" + - "
    "; + const timestamp = ` +
    + ${ + c.pinned + ? '' + : "" + } + + + ${c.edited_at ? " *" : ""} + +
    `; // Main text let txtCss = ""; @@ -587,6 +585,7 @@ export class Init { ); } } + media = `
    ${media.join("")}
    `; // Preview link let previewLink = ""; @@ -655,7 +654,7 @@ export class Init { timestamp + "" + content + - media.join("") + + media + previewLink + poll + counterBar + @@ -743,7 +742,7 @@ export class Init { * @param {string} s String * @returns {string} String */ - #escapeHtml(s) { + #escapeHTML(s) { return (s ?? "") .replaceAll("&", "&") .replaceAll("<", "<") @@ -793,7 +792,7 @@ export class Init { /** * Create media element * @param {object} m Media content - * @param {boolean} s Spoiler/Sensitive status + * @param {boolean} s Sensitive/spoiler status * @returns {string} Media in HTML format */ #createMedia(m, s) { @@ -806,6 +805,16 @@ export class Init { '
    ' + @@ -817,7 +826,7 @@ export class Init { '' +
-        (m.description ? this.#escapeHtml(m.description) : ' + "
    "; } @@ -828,6 +837,16 @@ export class Init { '
    ' + @@ -842,13 +861,15 @@ export class Init { '' +
-          (m.description ? this.#escapeHtml(m.description) : ' + "
    "; } else { media = '
    ' + (spoiler ? '' + + '' + "
    "; } else { media = '
    ' + @@ -899,7 +938,7 @@ export class Init { : "") + '' + + '" loop>' + "
    "; } } @@ -907,16 +946,234 @@ export class Init { return media; } + /** + * Open a dialog/modal with the styles of Mastodon timeline + * @param {string} i Dialog Id name + * @param {string} c Dialog HTML content + */ + #openDialog(i, c) { + let dialog = document.createElement("dialog"); + dialog.id = i; + dialog.classList.add("mt-dialog"); + dialog.dataset.theme = this.mtContainerNode.getAttribute("data-theme"); + dialog.innerHTML = c; + document.body.prepend(dialog); + dialog.showModal(); + dialog.addEventListener("close", () => { + document.body.removeChild(dialog); + }); + } + + /** + * Build a carousel/lightbox with the media content in the post clicked + * @param {event} e User interaction trigger + */ + #buildCarousel(e) { + // List all medias in the post and remove sensitive/spoiler medias + const mediaSiblings = Array.from( + e.target.parentNode.parentNode.children + ).filter((element) => !element.classList.contains("mt-post-media-spoiler")); + const mediaClickedIndex = mediaSiblings.indexOf(e.target.parentNode) + 1; + + // Build media element and wrapper + let mediaItems = []; + mediaSiblings.forEach((sibling, i) => { + let mediaElement = ""; + if ( + sibling.getAttribute("data-media-type") === "gifv" || + sibling.getAttribute("data-media-type") === "video" + ) { + mediaElement = ` + + `; + } else { + mediaElement = ` + ${sibling.getAttribute(
+          + `; + } + + const mediaWrapper = ` + + `; + + mediaItems.push(mediaWrapper); + }); + + // Build carousel + const carouselHTML = ` + + + + + + + + `; + + // Call dialog/modal with carousel "id" and HTML content + this.#openDialog("mt-carousel", carouselHTML); + + // Set carousel interactions for horizontal scroll and buttons + if (mediaItems.length >= 2) { + this.#setCarouselInteractions(mediaSiblings.length, mediaClickedIndex); + } + } + + /** + * Add interactions for the carousel + * @param {number} t Total number of medias loaded + * @param {number} m Index position of media clicked by user + */ + #setCarouselInteractions(t, m) { + let currentMediaIndex = m; + const carousel = document.getElementById("mt-carousel-scroll"); + let scrollTimeout = 0; + let userScrolling = false; + const prevBtn = document.getElementById("mt-carousel-prev"); + const nextBtn = document.getElementById("mt-carousel-next"); + + // Scroll the carusel to the media element + const scrollCarouselTo = (i, behavior = "smooth") => { + document + .getElementById("mt-carousel-" + i) + .scrollIntoView({ behavior: behavior }); + }; + // First run, place the scroll on clicked media + scrollCarouselTo(currentMediaIndex, "instant"); + + // Get current index of the media shown on screen + const updateMediaIndex = () => { + const scrolledMedia = + (carousel.scrollLeft + carousel.clientWidth) / carousel.clientWidth; + return Math.round(scrolledMedia + Number.EPSILON); + }; + + // Scroll interactions + const isScrolling = () => { + clearTimeout(scrollTimeout); + scrollTimeout = setTimeout(() => { + if (userScrolling) { + currentMediaIndex = updateMediaIndex(); + checkBtnsVisibility(); + } + userScrolling = true; + }, 60); + }; + carousel.addEventListener("scroll", isScrolling); + + // Click interactions + const checkBtnsVisibility = () => { + prevBtn.hidden = currentMediaIndex === 1; + nextBtn.hidden = currentMediaIndex === t; + }; + + const userClick = (e) => { + const idTarget = e.target.closest("button")?.id; + + // Prev/next buttons + if (idTarget === "mt-carousel-next") { + userScrolling = false; + ++currentMediaIndex; + if (currentMediaIndex > t) currentMediaIndex = t; + scrollCarouselTo(currentMediaIndex); + checkBtnsVisibility(); + } else if (idTarget === "mt-carousel-prev") { + userScrolling = false; + --currentMediaIndex; + if (currentMediaIndex < 1) currentMediaIndex = 1; + scrollCarouselTo(currentMediaIndex); + checkBtnsVisibility(); + } + + // Close button + if (idTarget === "mt-carousel-close") { + killEventListeners(); + } + }; + document.addEventListener("click", userClick); + + // Keyboard interactions + const userKeyDown = (e) => { + if (e.key === "Escape" || e.keyCode === 27) { + killEventListeners(); + } + }; + document.addEventListener("keydown", userKeyDown); + + // Kill carousel listeners + const killEventListeners = () => { + carousel.removeEventListener("scroll", isScrolling); + document.removeEventListener("click", userClick); + document.removeEventListener("keydown", userKeyDown); + }; + } + /** * Replace the video preview image by the video player * @param {event} e User interaction trigger */ #loadPostVideo(e) { - const parentNode = e.target.closest("[data-video-url]"); - const videoUrl = parentNode.dataset.videoUrl; + const parentNode = e.target.closest("[data-media-type]"); + const urlVideo = parentNode.dataset.mediaUrlHd; parentNode.replaceChildren(); - parentNode.innerHTML = - ''; + parentNode.innerHTML = ``; } /** @@ -924,28 +1181,29 @@ export class Init { * @param {event} e User interaction trigger */ #toogleSpoiler(e) { - const nextSibling = e.target.nextSibling; + const target = e.target; + const nextSibling = target.nextSibling; if ( nextSibling.localName === "img" || nextSibling.localName === "audio" || nextSibling.localName === "video" ) { - e.target.parentNode.classList.remove("mt-post-media-spoiler"); - e.target.style.display = "none"; + target.parentNode.classList.remove("mt-post-media-spoiler"); + target.style.display = "none"; } else if ( nextSibling.classList.contains("spoiler-txt-hidden") || nextSibling.classList.contains("spoiler-txt-visible") ) { - if (e.target.textContent == this.mtSettings.btnShowMore) { + if (target.textContent == this.mtSettings.btnShowMore) { nextSibling.classList.remove("spoiler-txt-hidden"); nextSibling.classList.add("spoiler-txt-visible"); - e.target.setAttribute("aria-expanded", "true"); - e.target.textContent = this.mtSettings.btnShowLess; + target.setAttribute("aria-expanded", "true"); + target.textContent = this.mtSettings.btnShowLess; } else { nextSibling.classList.remove("spoiler-txt-visible"); nextSibling.classList.add("spoiler-txt-hidden"); - e.target.setAttribute("aria-expanded", "false"); - e.target.textContent = this.mtSettings.btnShowMore; + target.setAttribute("aria-expanded", "false"); + target.textContent = this.mtSettings.btnShowMore; } } } @@ -966,7 +1224,7 @@ export class Init { '">' +
-          this.#escapeHtml(c.image_description) +
+          this.#escapeHTML(c.image_description) +
           '' : '
    📄
    ') + "" + @@ -1032,25 +1290,26 @@ export class Init { } else if (this.mtSettings.timelineType === "local") { btnSeeMorePath = "public/local"; } - const btnSeeMoreHTML = - '' + - this.mtSettings.btnSeeMore + - ""; + const btnSeeMoreHTML = ` + + ${this.mtSettings.btnSeeMore} + `; containerFooter.insertAdjacentHTML("beforeend", btnSeeMoreHTML); } // Create button to refresh the timeline if (this.mtSettings.btnReload) { - const btnReloadHTML = - '"; + const btnReloadHTML = ` + `; containerFooter.insertAdjacentHTML("beforeend", btnReloadHTML); @@ -1068,39 +1327,57 @@ export class Init { */ #setPostsInteracion() { this.mtBodyNode.addEventListener("click", (e) => { + const target = e.target; + const localName = target.localName; + const parentNode = target.parentNode; + // Check if post cointainer was clicked if ( - e.target.localName == "article" || - e.target.offsetParent?.localName == "article" || - (e.target.localName == "img" && - !e.target.parentNode.getAttribute("data-video-url")) + localName == "article" || + target.offsetParent?.localName == "article" || + (localName == "img" && + this.mtSettings.disableCarousel && + parentNode.getAttribute("data-media-type") !== "video" && + parentNode.getAttribute("data-media-type") !== "gifv") ) { this.#openPostUrl(e); } + // Check if Show More/Less button was clicked if ( - e.target.localName == "button" && - e.target.classList.contains("mt-btn-spoiler") + localName == "button" && + target.classList.contains("mt-btn-spoiler") ) { this.#toogleSpoiler(e); } + + // Check if image in post was clicked + if ( + localName == "img" && + !this.mtSettings.disableCarousel && + parentNode.getAttribute("data-media-type") !== "video" && + parentNode.getAttribute("data-media-type") !== "gifv" + ) { + this.#buildCarousel(e); + } + // Check if video preview image or play icon/button was clicked if ( - e.target.className == "mt-post-media-play-icon" || - (e.target.localName == "svg" && - e.target.parentNode.className == "mt-post-media-play-icon") || - (e.target.localName == "path" && - e.target.parentNode.parentNode.className == - "mt-post-media-play-icon") || - (e.target.localName == "img" && - e.target.parentNode.getAttribute("data-video-url")) + target.className == "mt-btn-play" || + (localName == "svg" && parentNode.className == "mt-btn-play") || + (localName == "path" && + parentNode.parentNode.className == "mt-btn-play") || + (localName == "img" && + (parentNode.getAttribute("data-media-type") === "video" || + parentNode.getAttribute("data-media-type") === "gifv")) ) { this.#loadPostVideo(e); } }); this.mtBodyNode.addEventListener("keydown", (e) => { + const localName = e.target.localName; // Check if Enter key was pressed with focus in an article - if (e.key === "Enter" && e.target.localName == "article") { + if (e.key === "Enter" && localName == "article") { this.#openPostUrl(e); } }); @@ -1118,6 +1395,7 @@ export class Init { 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" && @@ -1155,12 +1433,12 @@ export class Init { */ #showError(t, i) { const icon = i || "❌"; - this.mtBodyNode.innerHTML = - '
    ' + - icon + - '
    Oops, something\'s happened:
    ' + - t + - "
    "; + this.mtBodyNode.innerHTML = ` +
    + ${icon} + Oops, something's happened: +
    ${t}
    +
    `; this.mtBodyNode.setAttribute("role", "none"); throw new Error( "Stopping the script due to an error building the timeline."