diff --git a/dist/mastodon-timeline.esm.js b/dist/mastodon-timeline.esm.js index 9776897..0d0758b 100644 --- a/dist/mastodon-timeline.esm.js +++ b/dist/mastodon-timeline.esm.js @@ -1,7 +1,7 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.5.0 + * @version 4.5.1 * @url https://gitlab.com/idotj/mastodon-embed-timeline * @license GNU AGPLv3 */ diff --git a/dist/mastodon-timeline.umd.js b/dist/mastodon-timeline.umd.js index 7aebd34..09b3eb9 100644 --- a/dist/mastodon-timeline.umd.js +++ b/dist/mastodon-timeline.umd.js @@ -2,7 +2,7 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.5.0 + * @version 4.5.1 * @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",dateFormatLocale:"en-GB",dateFormatOptions:{day:"2-digit",month:"short",year:"numeric"},hideUnlisted:!1,hideReblog:!1,hideReplies:!1,hidePinnedPosts:!1,hideUserAccount:!1,txtMaxLines:"",filterByLanguage:"",btnShowMore:"SHOW MORE",btnShowLess:"SHOW LESS",markdownBlockquote:!1,hideEmojos:!1,btnShowContent:"SHOW CONTENT",hideVideoPreview:!1,btnPlayVideoTxt:"Load and play video",hidePreviewLink:!1,previewMaxLines:"",hideCounterBar:!1,disableCarousel:!1,carouselCloseTxt:"Close carousel",carouselPrevTxt:"Previous media item",carouselNextTxt:"Next media item",btnSeeMore:"See more posts at Mastodon",btnReload:"Refresh",insistSearchContainer:!1,insistSearchContainerTime:"3000"},this.mtSettings={...this.defaultSettings,...t},this.#t(),this.linkHeader={},this.mtContainerNode="",this.mtBodyNode="",this.fetchedData={},this.#e((()=>{this.#i()}))}#t(){Number(this.mtSettings.maxNbPostShow)>Number(this.mtSettings.maxNbPostFetch)&&(console.error(`Please check your settings! The maximum number of posts to show is bigger than the maximum number of posts to fetch. Changing the value of "maxNbPostFetch" to: ${this.mtSettings.maxNbPostShow}`),this.mtSettings.maxNbPostFetch=this.mtSettings.maxNbPostShow)}#e(t){"undefined"!=typeof document&&"complete"===document.readyState?t():"undefined"!=typeof document&&"complete"!==document.readyState&&document.addEventListener("DOMContentLoaded",t())}#i(){const t=()=>{this.mtContainerNode=document.getElementById(this.mtSettings.mtContainerId),this.mtBodyNode=this.mtContainerNode.getElementsByClassName("mt-body")[0],this.#s(),this.#a("newTimeline")};if(this.mtSettings.insistSearchContainer){const e=performance.now(),i=()=>{if(document.getElementById(this.mtSettings.mtContainerId))t();else{performance.now()-e container with id: "${this.mtSettings.mtContainerId}" after several attempts for ${this.mtSettings.insistSearchContainerTime/1e3} seconds`)}};i()}else document.getElementById(this.mtSettings.mtContainerId)?t():console.error(`Impossible to find the
container with id: "${this.mtSettings.mtContainerId}". Please try to add the option 'insistSearchContainer: true' when initializing the script`)}mtUpdate(){this.#e((()=>{this.mtBodyNode.replaceChildren(),this.mtBodyNode.insertAdjacentHTML("afterbegin",'
'),this.#a("updateTimeline")}))}mtColorTheme(t){this.#e((()=>{this.mtContainerNode.setAttribute("data-theme",t)}))}#s(){if("auto"===this.mtSettings.defaultTheme){let t=window.matchMedia("(prefers-color-scheme: dark)");t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light"),t.addEventListener("change",(t=>{t.matches?this.mtColorTheme("dark"):this.mtColorTheme("light")}))}else this.mtColorTheme(this.mtSettings.defaultTheme)}#n(){return new Promise(((t,e)=>{const i=this.mtSettings.instanceUrl?`${this.mtSettings.instanceUrl}/api/v1/`:this.#o("Please check your instanceUrl value","⚠️"),s=this.#r(i),a=Object.entries(s).map((([t,i])=>{const s="timeline"===t;return this.#l(i,s).then((e=>({[t]:e}))).catch((s=>(e(new Error(`Something went wrong fetching data from: ${i}`)),this.#o(s.message),{[t]:[]})))}));Promise.all(a).then((async e=>{if(this.fetchedData=e.reduce(((t,e)=>({...t,...e})),{}),!this.mtSettings.hidePinnedPosts&&void 0!==this.fetchedData.pinned?.length&&0!==this.fetchedData.pinned.length){const t=this.fetchedData.pinned.map((t=>({...t,pinned:!0})));this.fetchedData.timeline=[...t,...this.fetchedData.timeline]}if(this.#d())t();else{do{await this.#m()}while(!this.#d()&&this.linkHeader.next);t()}}))}))}#r(t){const{timelineType:e,userId:i,hashtagName:s,maxNbPostFetch:a,hidePinnedPosts:n,hideEmojos:o}=this.mtSettings,r={};switch(e){case"profile":if(!i){this.#o("Please check your userId value","⚠️");break}r.timeline=`${t}accounts/${i}/statuses?limit=${a}`,n||(r.pinned=`${t}accounts/${i}/statuses?pinned=true`);break;case"hashtag":if(!s){this.#o("Please check your hashtagName value","⚠️");break}r.timeline=`${t}timelines/tag/${s}?limit=${a}`;break;case"local":r.timeline=`${t}timelines/public?local=true&limit=${a}`;break;default:this.#o("Please check your timelineType value","⚠️")}return o||(r.emojos=`${t}custom_emojis`),r}async#l(t,e=!1){const i=await fetch(t);if(!i.ok)throw new Error(`\n Failed to fetch the following Url:
${t}
Error status: ${i.status}
Error message: ${i.statusText}\n `);const s=await i.json();return e&&i.headers.get("Link")&&(this.linkHeader=this.#h(i.headers.get("Link"))),s}#d(){return this.fetchedData.timeline.length>=Number(this.mtSettings.maxNbPostFetch)}#m(){return new Promise((t=>{this.linkHeader.next?this.#l(this.linkHeader.next,!0).then((e=>{this.fetchedData.timeline=[...this.fetchedData.timeline,...e],t()})):t()}))}#h(t){const e=t.split(", ").map((t=>t.split("; "))).map((t=>[t[1].replace(/"/g,"").replace("rel=",""),t[0].slice(1,-1)]));return Object.fromEntries(e)}async#a(t){await this.#n();const{hideUnlisted:e,hideReblog:i,hideReplies:s,maxNbPostShow:a,filterByLanguage:n}=this.mtSettings,o=this.fetchedData.timeline;this.mtBodyNode.replaceChildren();if(o.filter((t=>{const a="public"===t.visibility||!e&&"unlisted"===t.visibility,o=i&&t.reblog,r=s&&t.in_reply_to_id,l=t.language||(t.reblog?t.reblog.language:null);return a&&!o&&!r&&(""===n||l===n)})).forEach(((t,e)=>{e${o?.length||0} posts have been fetched from the server
This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;this.#o(t,"📭")}}#g(){"0"!==this.mtSettings.txtMaxLines&&0!==this.mtSettings.txtMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-txt-max-lines",this.mtSettings.txtMaxLines),"0"!==this.mtSettings.previewMaxLines&&0!==this.mtSettings.previewMaxLines.length&&this.mtBodyNode.parentNode.style.setProperty("--mt-preview-max-lines",this.mtSettings.previewMaxLines)}#u(t){const e=this.mtBodyNode.getElementsByTagName("article");for(let i=0;i
'+this.#f(h)+' avatar
'+(i?'
'+this.#f(t.account.username)+' avatar
':"")+"
",u='
'+(!this.mtSettings.hideEmojos&&c?this.#x(c,p):c||h)+""+(this.mtSettings.hideUserAccount?"":'
")+"
",v=this.#S(n),b='
'+(t.pinned?'':"")+'"+(t.edited_at?" *":"")+"
",w="0"!==this.mtSettings.txtMaxLines?" truncate":"";let f="";const x=s.spoiler_text?s.spoiler_text:s.content;x&&(f='
'+this.#y(x)+"
");const S=[...t.media_attachments,...t.reblog?.media_attachments||[]].map((t=>this.#L(t,s.sensitive))).join("");return'
'+g+u+b+"
"+f+(S?`
${S}
`:"")+(!this.mtSettings.hidePreviewLink&&t.card?this.#T(t.card):"")+(t.poll?'
    '+t.poll.options.map((function(t){return"
  • "+t.title+"
  • "})).join("")+"
":"")+(this.mtSettings.hideCounterBar?"":'
'+this.#N("replies",o)+this.#N("reblog",r)+this.#N("favorites",l)+"
")+"
"}#N(t,e){return`
${{replies:'',reblog:'',favorites:''}[t]}${e}
`}#M(t,e){function i(t,e){let i=e.replace(/\s+/g,"").toLowerCase();return!(!["src","href","xlink:href"].includes(t)||!i.includes("javascript:")&&!i.includes("data:"))||(!!t.startsWith("on")||void 0)}function s(t){let e=t.attributes;for(let{name:s,value:a}of e)i(s,a)&&t.removeAttribute(s)}let a=(new DOMParser).parseFromString(t,"text/html").body||document.createElement("body");return function(t){let e=t.querySelectorAll("script");for(let t of e)t.remove()}(a),function t(e){let i=e.children;for(let e of i)s(e),t(e)}(a),e?a.childNodes:a.innerHTML}#y(t){let e=t;return e=this.#M(e,!1),e=this.#C(e),this.mtSettings.hideEmojos||(e=this.#x(e,this.fetchedData.emojos)),this.mtSettings.markdownBlockquote&&(e=this.#P(e,"

>","

","

","

")),e}#C(t){let e=t.replaceAll('rel="tag"','rel="tag" target="_blank"');return e=e.replaceAll('class="u-url mention"','class="u-url mention" target="_blank"'),e}#P(t,e,i,s,a){if(t.includes(e)){const n=new RegExp(e+"(.*?)"+i,"gi");return t.replace(n,s+"$1"+a)}return t}#f(t){return(t??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}#x(t,e){if(t.includes(":")){for(const i of e){const e=new RegExp(`\\:${i.shortcode}\\:`,"g");t=t.replace(e,`Emoji ${i.shortcode}`)}return t}return t}#S(t){const e=new Date(t);return new Intl.DateTimeFormat(this.mtSettings.dateFormatLocale,this.mtSettings.dateFormatOptions).format(e)}#L(t,e=!1){const i=t.type,s=e;let a="";const n='";return"image"===i&&(a='
'+(s?n:"")+''+(t.description?this.#f(t.description):
'),"audio"===i&&(a=t.preview_url?'
'+(s?n:"")+''+(t.description?this.#f(t.description):
':'
'+(s?n:"")+'
'),"video"!==i&&"gifv"!==i||(a=this.mtSettings.hideVideoPreview?'
'+(s?n:"")+'
':'
'+(s?n:"")+''+(t.description?this.#f(t.description):
'),a}#k(t,e){let i=document.createElement("dialog");i.id=t,i.classList.add("mt-dialog"),i.dataset.theme=this.mtContainerNode.getAttribute("data-theme"),i.innerHTML=e,document.body.prepend(i),i.showModal(),i.addEventListener("close",(()=>{document.body.removeChild(i)}))}#E(t){const e=Array.from(t.target.parentNode.parentNode.children).filter((t=>!t.classList.contains("mt-post-media-spoiler"))),i=e.indexOf(t.target.parentNode)+1;let s=[];e.forEach(((t,e)=>{let i="";i="gifv"===t.getAttribute("data-media-type")||"video"===t.getAttribute("data-media-type")?`\n \n `:`\n ${t.getAttribute(\n `;const a=`\n \n `;s.push(a)}));const a=`\n \n\n \n\n \n\n \n `;this.#k("mt-carousel",a),s.length>=2&&this.#$(e.length,i)}#$(t,e){let i=e;const s=document.getElementById("mt-carousel-scroll");let a=0,n=!1;const o=document.getElementById("mt-carousel-prev"),r=document.getElementById("mt-carousel-next"),l=(t,e="smooth")=>{document.getElementById("mt-carousel-"+t).scrollIntoView({behavior:e})};l(i,"instant");const d=()=>{clearTimeout(a),a=setTimeout((()=>{n&&(i=(()=>{const t=(s.scrollLeft+s.clientWidth)/s.clientWidth;return Math.round(t+Number.EPSILON)})(),m()),n=!0}),60)};s.addEventListener("scroll",d);const m=()=>{o.hidden=1===i,r.hidden=i===t},h=e=>{const s=e.target.closest("button")?.id;"mt-carousel-next"===s?(n=!1,++i,i>t&&(i=t),l(i),m()):"mt-carousel-prev"===s&&(n=!1,--i,i<1&&(i=1),l(i),m()),"mt-carousel-close"===s&&p()};document.addEventListener("click",h);const c=t=>{"Escape"!==t.key&&27!==t.keyCode||p()};document.addEventListener("keydown",c);const p=()=>{s.removeEventListener("scroll",d),document.removeEventListener("click",h),document.removeEventListener("keydown",c)}}#B(t){const e=t.target.closest("[data-media-type]"),i=e.dataset.mediaUrlHd;e.replaceChildren(),e.innerHTML=``}#H(t){const e=t.target,i=e.nextSibling;e.textContent==this.mtSettings.btnShowMore?(i.classList.remove("spoiler-txt-hidden"),i.classList.add("spoiler-txt-visible"),e.setAttribute("aria-expanded","true"),e.textContent=this.mtSettings.btnShowLess):(i.classList.remove("spoiler-txt-visible"),i.classList.add("spoiler-txt-hidden"),e.setAttribute("aria-expanded","false"),e.textContent=this.mtSettings.btnShowMore)}#A(t){const e=t.target;e.classList.contains("mt-btn-spoiler-media-show")?e.parentNode.classList.remove("mt-post-media-spoiler"):e.parentNode.classList.add("mt-post-media-spoiler")}#T(t){let e="";if("0"!==this.mtSettings.previewMaxLines&&t.description){e=''+this.#_(t.description)+""}return''+(t.image?'
'+this.#f(t.image_description)+'
':'
📄
')+'
'+(t.provider_name?''+this.#_(t.provider_name)+"":"")+''+t.title+""+e+(t.author_name?''+this.#_(t.author_name)+"":"")+"
"}#_(t){return(new DOMParser).parseFromString(t,"text/html").body.textContent}#b(){let t="",e="";if(this.mtSettings.btnSeeMore){let e="";"profile"===this.mtSettings.timelineType?this.mtSettings.profileName?e=this.mtSettings.profileName:this.#o("Please check your profileName value","⚠️"):"hashtag"===this.mtSettings.timelineType?e="tags/"+this.mtSettings.hashtagName:"local"===this.mtSettings.timelineType&&(e="public/local"),t=`\n \n ${this.mtSettings.btnSeeMore}\n `}if(this.mtSettings.btnReload&&(e=`\n `),this.mtBodyNode.parentNode.insertAdjacentHTML("beforeend",'"),this.mtSettings.btnReload){this.mtContainerNode.getElementsByClassName("btn-refresh")[0].addEventListener("click",(()=>{this.mtUpdate()}))}}#v(){this.mtBodyNode.addEventListener("click",(t=>{const e=t.target,i=e.localName,s=e.parentNode;("article"==i||"article"==e.offsetParent?.localName||this.mtSettings.disableCarousel&&"image"===s.getAttribute("data-media-type"))&&this.#D(t),e.classList.contains("mt-btn-spoiler-txt")&&this.#H(t),e.classList.contains("mt-btn-spoiler-media")&&this.#A(t),this.mtSettings.disableCarousel||"img"!=i||"image"!==s.getAttribute("data-media-type")&&"audio"!==s.getAttribute("data-media-type")||this.#E(t),("mt-btn-play"==e.className||"svg"==i&&"mt-btn-play"==s.className||"path"==i&&"mt-btn-play"==s.parentNode.className||"img"==i&&("video"===s.getAttribute("data-media-type")||"gifv"===s.getAttribute("data-media-type")))&&this.#B(t)})),this.mtBodyNode.addEventListener("keydown",(t=>{const e=t.target.localName;"Enter"===t.key&&"article"==e&&this.#D(t)}))}#D(t){const e=t.target.closest(".mt-post")?.dataset.location;if(!e)return;const i=t.target.localName;if("a"===i||"span"===i||"button"===i||"bdi"===i||"time"===i)return;const s=t.target.className;if("mt-post-media-spoiler"===s||"mt-post-preview-noImage"===s)return;const a=t.target.parentNode?.className;"mt-post-avatar-image-big"!==a&&"mt-post-avatar-image-small"!==a&&"mt-post-header-user-name"!==a&&"mt-post-preview-image"!==a&&"mt-post-preview"!==a&&window.open(e,"_blank","noopener")}#p(){const t=e=>{e.target.parentNode.classList.remove(this.mtSettings.spinnerClass),e.target.removeEventListener("load",t),e.target.removeEventListener("error",t)};this.mtBodyNode.querySelectorAll(`.${this.mtSettings.spinnerClass} > img`).forEach((e=>{e.addEventListener("load",t),e.addEventListener("error",t)}))}#o(t,e){const i=e||"❌";throw this.mtBodyNode.innerHTML=`\n
\n ${i}\n Oops, something's happened:\n
${t}
\n
`,this.mtBodyNode.setAttribute("role","none"),new Error("Stopping the script due to an error building the timeline.")}}})); diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index e361457..cf4d661 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -45,11 +45,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 diff --git a/package-lock.json b/package-lock.json index ce87bcf..8f00792 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@idotj/mastodon-embed-timeline", - "version": "4.5.0", + "version": "4.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@idotj/mastodon-embed-timeline", - "version": "4.5.0", + "version": "4.5.1", "license": "GNU", "devDependencies": { "@rollup/plugin-terser": "^0.4.4", diff --git a/package.json b/package.json index 75957a5..ba00fa2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@idotj/mastodon-embed-timeline", - "version": "4.5.0", + "version": "4.5.1", "description": "Displays a Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.", "license": "GNU", "author": { diff --git a/src/mastodon-timeline.css b/src/mastodon-timeline.css index 9edc4d2..5b35203 100644 --- a/src/mastodon-timeline.css +++ b/src/mastodon-timeline.css @@ -1,4 +1,4 @@ -/* Mastodon embed timeline v4.5.0 */ +/* Mastodon embed timeline v4.5.1 */ /* More info at: */ /* https://gitlab.com/idotj/mastodon-embed-timeline */ diff --git a/src/mastodon-timeline.js b/src/mastodon-timeline.js index 18c9f87..1111afe 100644 --- a/src/mastodon-timeline.js +++ b/src/mastodon-timeline.js @@ -1,7 +1,7 @@ /** * Mastodon embed timeline * @author idotj - * @version 4.5.0 + * @version 4.5.1 * @url https://gitlab.com/idotj/mastodon-embed-timeline * @license GNU AGPLv3 */