Feature/link preview
This commit is contained in:
		
							parent
							
								
									621a597e11
								
							
						
					
					
						commit
						40a5d0a163
					
				| @ -1,3 +1,8 @@ | ||||
| v3.8.1 - 14/08/2023 | ||||
| - Show preview card from link, photo or video URL | ||||
| - Add description for the ALT attribute in images | ||||
| - Improve JS comments | ||||
| 
 | ||||
| v3.8.0 - 04/08/2023 | ||||
| - Add spoiler/sensitive button for reblog content | ||||
| 
 | ||||
| @ -77,8 +82,8 @@ v1.5.3 - 23/05/2021 | ||||
| 
 | ||||
| v1.5.2 - 22/05/2021 | ||||
| - Add avatar name | ||||
| - Use id instead of css class for js selectors | ||||
| - Rearrange functions in js | ||||
| - Use id instead of css class for JS selectors | ||||
| - Rearrange functions in JS | ||||
| 
 | ||||
| v1.5.1 - 17/05/2021 | ||||
| - Add ellipses for long messages | ||||
|  | ||||
| @ -80,6 +80,9 @@ hide_reblog: false, | ||||
| // Hide replies toots. Default: don't hide | ||||
| hide_replies: false, | ||||
| 
 | ||||
| // Hide preview for links. Default: don't hide | ||||
| hide_preview_link: false, | ||||
| 
 | ||||
| // Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag (default: don't apply) | ||||
| markdown_blockquote: false, | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| /* Mastodon embed feed timeline v3.8.0 */ | ||||
| /* Mastodon embed feed timeline v3.8.1 */ | ||||
| /* More info at: */ | ||||
| /* https://gitlab.com/idotj/mastodon-embed-feed-timeline */ | ||||
| 
 | ||||
| @ -40,7 +40,7 @@ html[data-theme="dark"] { | ||||
|   text-decoration: none; | ||||
|   color: var(--link-color); | ||||
| } | ||||
| .mt-timeline a:hover { | ||||
| .mt-timeline a:not(.toot-preview-link):hover { | ||||
|   text-decoration: underline; | ||||
| } | ||||
| .mt-timeline::-webkit-scrollbar { | ||||
| @ -78,7 +78,7 @@ html[data-theme="dark"] { | ||||
| /* Toot container */ | ||||
| .mt-toot { | ||||
|   margin: 0.25rem; | ||||
|   padding: 1rem 0.5rem 2rem 4rem; | ||||
|   padding: 1rem 0.5rem 1.5rem 4rem; | ||||
|   position: relative; | ||||
|   min-height: 3.75rem; | ||||
|   background-color: transparent; | ||||
| @ -119,6 +119,7 @@ html[data-theme="dark"] { | ||||
| .mt-user { | ||||
|   display: table; | ||||
|   font-weight: 600; | ||||
|   margin-bottom: 1rem; | ||||
| } | ||||
| .mt-user > a { | ||||
|   color: var(--content-text) !important; | ||||
| @ -196,7 +197,7 @@ html[data-theme="dark"] { | ||||
| /* Medias */ | ||||
| .toot-media { | ||||
|   overflow: hidden; | ||||
|   margin-bottom: 0.25rem; | ||||
|   margin-bottom: 0.5rem; | ||||
| } | ||||
| .toot-media-preview { | ||||
|   position: relative; | ||||
| @ -239,6 +240,47 @@ html[data-theme="dark"] { | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| /* Preview link */ | ||||
| .toot-preview-link { | ||||
|   min-height: 4rem; | ||||
|   display: flex; | ||||
|   flex-direction: row;   | ||||
| 
 | ||||
|   border: 1px solid var(--line-gray-color); | ||||
|   border-radius: 0.5rem; | ||||
|   color: var(--link-color); | ||||
|   font-size: 0.8rem; | ||||
|   margin: 1rem 0 0.5rem 0; | ||||
|   overflow: hidden; | ||||
| } | ||||
| .toot-preview-image { | ||||
|   width: 40%; | ||||
|   align-self: stretch; | ||||
| } | ||||
| .toot-preview-image img { | ||||
|   display: block; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   object-fit: cover; | ||||
| } | ||||
| .toot-preview-noImage { | ||||
|   width: 40%; | ||||
|   font-size: 1.5rem; | ||||
|   align-self: center; | ||||
|   text-align: center; | ||||
| } | ||||
| .toot-preview-content { | ||||
|   width: 60%; | ||||
|   display: flex; | ||||
|   align-self: center; | ||||
|   flex-direction: column; | ||||
|   padding: 0.5rem 1rem; | ||||
|   gap: 0.5rem; | ||||
| } | ||||
| .toot-preview-title { | ||||
|   font-weight: 600; | ||||
| } | ||||
| 
 | ||||
| /* Spoiler button */ | ||||
| .spoiler-link { | ||||
|   border-radius: 2px; | ||||
|  | ||||
| @ -1,8 +1,13 @@ | ||||
| // Mastodon embed feed timeline v3.8.0
 | ||||
| // More info at:
 | ||||
| // https://gitlab.com/idotj/mastodon-embed-feed-timeline
 | ||||
| /** | ||||
|  * Mastodon embed feed timeline v3.8.1 | ||||
|  * More info at: | ||||
|  * https://gitlab.com/idotj/mastodon-embed-feed-timeline
 | ||||
|  */ | ||||
| 
 | ||||
| // Timeline settings
 | ||||
| /** | ||||
|  * Timeline settings | ||||
|  * Adjust these parameters to customize your timeline | ||||
|  */ | ||||
| window.addEventListener("load", () => { | ||||
|   let mapi = new MastodonApi({ | ||||
|     // Id of the <div> containing the timeline
 | ||||
| @ -38,6 +43,9 @@ window.addEventListener("load", () => { | ||||
|     // Hide replies toots. Default: don't hide
 | ||||
|     hide_replies: false, | ||||
| 
 | ||||
|     // Hide preview card if toot contains a link, photo or video from a URL. Default: don't hide
 | ||||
|     hide_preview_link: false, | ||||
| 
 | ||||
|     // Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag (default: don't apply)
 | ||||
|     markdown_blockquote: false, | ||||
| 
 | ||||
| @ -49,8 +57,13 @@ window.addEventListener("load", () => { | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| let MastodonApi = function (params_) { | ||||
|   // Endpoint access settings / default values
 | ||||
| /** | ||||
|  * Set all variables with customized values or use default ones | ||||
|  * @param {object} params_ User customized values | ||||
|  * Trigger color theme function | ||||
|  * Trigger main function to build the timeline | ||||
|  */ | ||||
| const MastodonApi = function (params_) { | ||||
|   this.DEFAULT_THEME = params_.default_theme || "auto"; | ||||
|   this.INSTANCE_URL = params_.instance_url; | ||||
|   this.USER_ID = params_.user_id || ""; | ||||
| @ -66,6 +79,10 @@ let MastodonApi = function (params_) { | ||||
|     typeof params_.hide_reblog !== "undefined" ? params_.hide_reblog : false; | ||||
|   this.HIDE_REPLIES = | ||||
|     typeof params_.hide_replies !== "undefined" ? params_.hide_replies : false; | ||||
|   this.HIDE_PREVIEW_LINK = | ||||
|     typeof params_.hide_preview_link !== "undefined" | ||||
|       ? params_.hide_preview_link | ||||
|       : false; | ||||
|   this.MARKDOWN_BLOCKQUOTE = | ||||
|     typeof params_.markdown_blockquote !== "undefined" | ||||
|       ? params_.markdown_blockquote | ||||
| @ -73,24 +90,29 @@ let MastodonApi = function (params_) { | ||||
|   this.TEXT_MAX_LINES = params_.text_max_lines || "0"; | ||||
|   this.LINK_SEE_MORE = params_.link_see_more; | ||||
| 
 | ||||
|   // Target selector
 | ||||
|   this.mtBodyContainer = document.getElementById(params_.container_body_id); | ||||
| 
 | ||||
|   // Apply selected appearance
 | ||||
|   this.applyTheme(); | ||||
|   this.setTheme(); | ||||
| 
 | ||||
|   // Get the toots
 | ||||
|   this.getToots(); | ||||
|   this.buildTimeline(); | ||||
| }; | ||||
| 
 | ||||
| // Theme style
 | ||||
| MastodonApi.prototype.applyTheme = function () { | ||||
| /** | ||||
|  * Set the theme style choosen by user or browser/OS | ||||
|  */ | ||||
| MastodonApi.prototype.setTheme = function () { | ||||
|   /** | ||||
|    * Set the theme value in the <html> tag using the attribute "data-theme" | ||||
|    * @param {string} theme Type of theme to apply: dark or light | ||||
|    */ | ||||
|   const setTheme = function (theme) { | ||||
|     document.documentElement.setAttribute("data-theme", theme); | ||||
|   }; | ||||
| 
 | ||||
|   if (this.DEFAULT_THEME === "auto") { | ||||
|     let systemTheme = window.matchMedia("(prefers-color-scheme: dark)"); | ||||
|     systemTheme.matches ? setTheme("dark") : setTheme("light"); | ||||
|     // Update the theme if user change browser/OS preference
 | ||||
|     systemTheme.addEventListener("change", (e) => { | ||||
|       e.matches ? setTheme("dark") : setTheme("light"); | ||||
|     }); | ||||
| @ -99,8 +121,10 @@ MastodonApi.prototype.applyTheme = function () { | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // Listing toots function
 | ||||
| MastodonApi.prototype.getToots = function () { | ||||
| /** | ||||
|  * Listing toots function | ||||
|  */ | ||||
| MastodonApi.prototype.buildTimeline = function () { | ||||
|   let mapi = this; | ||||
|   let requestURL = ""; | ||||
| 
 | ||||
| @ -131,7 +155,6 @@ MastodonApi.prototype.getToots = function () { | ||||
|       // Empty the <div> container
 | ||||
|       this.mtBodyContainer.innerHTML = ""; | ||||
| 
 | ||||
|       // Add toots
 | ||||
|       for (let i in jsonData) { | ||||
|         // First filter (Public / Unlisted)
 | ||||
|         if ( | ||||
| @ -146,7 +169,7 @@ MastodonApi.prototype.getToots = function () { | ||||
|             // Nothing here (Don't append toots)
 | ||||
|           } else { | ||||
|             // Format and append toots
 | ||||
|             appendToot.call(mapi, jsonData[i], i); | ||||
|             appendToot.call(mapi, jsonData[i], Number(i)); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| @ -192,197 +215,21 @@ MastodonApi.prototype.getToots = function () { | ||||
|       this.mtBodyContainer.setAttribute("role", "none"); | ||||
|     }); | ||||
| 
 | ||||
|   // Inner function to add each toot content in container
 | ||||
|   let appendToot = function (status_, index) { | ||||
|     let avatar, user, content, url, date; | ||||
| 
 | ||||
|     if (status_.reblog) { | ||||
|       // BOOSTED toot
 | ||||
|       // Toot url
 | ||||
|       url = status_.reblog.url; | ||||
| 
 | ||||
|       // Boosted avatar
 | ||||
|       avatar = | ||||
|         '<a href="' + | ||||
|         status_.reblog.account.url + | ||||
|         '" class="mt-avatar mt-avatar-boosted" style="background-image:url(' + | ||||
|         status_.reblog.account.avatar + | ||||
|         ');" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|         '<div class="mt-avatar mt-avatar-booster" style="background-image:url(' + | ||||
|         status_.account.avatar + | ||||
|         ');">' + | ||||
|         "</div>" + | ||||
|         '<span class="visually-hidden">' + | ||||
|         status_.account.username + | ||||
|         " avatar" + | ||||
|         "</span>" + | ||||
|         "</a>"; | ||||
| 
 | ||||
|       // User name and url
 | ||||
|       user = | ||||
|         '<div class="mt-user">' + | ||||
|         '<a href="' + | ||||
|         status_.reblog.account.url + | ||||
|         '" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|         status_.reblog.account.username + | ||||
|         '<span class="visually-hidden"> post</span>' + | ||||
|         "</a>" + | ||||
|         "</div>"; | ||||
| 
 | ||||
|       // Date
 | ||||
|       date = this.formatDate(status_.reblog.created_at); | ||||
|     } else { | ||||
|       // STANDARD toot
 | ||||
|       // Toot url
 | ||||
|       url = status_.url; | ||||
| 
 | ||||
|       // Avatar
 | ||||
|       avatar = | ||||
|         '<a href="' + | ||||
|         status_.account.url + | ||||
|         '" class="mt-avatar" style="background-image:url(' + | ||||
|         status_.account.avatar + | ||||
|         ');" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|         '<span class="visually-hidden">' + | ||||
|         status_.account.username + | ||||
|         " avatar" + | ||||
|         "</span>" + | ||||
|         "</a>"; | ||||
| 
 | ||||
|       // User name and url
 | ||||
|       user = | ||||
|         '<div class="mt-user">' + | ||||
|         '<a href="' + | ||||
|         status_.account.url + | ||||
|         '" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|         status_.account.username + | ||||
|         '<span class="visually-hidden"> post</span>' + | ||||
|         "</a>" + | ||||
|         "</div>"; | ||||
| 
 | ||||
|       // Date
 | ||||
|       date = this.formatDate(status_.created_at); | ||||
|     } | ||||
| 
 | ||||
|     // Main text
 | ||||
|     let text_css = ""; | ||||
|     if (this.TEXT_MAX_LINES !== "0") { | ||||
|       text_css = "truncate"; | ||||
|       document.documentElement.style.setProperty( | ||||
|         "--text-max-lines", | ||||
|         this.TEXT_MAX_LINES | ||||
|   /** | ||||
|    * Inner function to add each toot in timeline container | ||||
|    * @param {object} c Toot content | ||||
|    * @param {number} i Index of toot | ||||
|    */ | ||||
|   const appendToot = function (c, i) { | ||||
|     this.mtBodyContainer.insertAdjacentHTML( | ||||
|       "beforeend", | ||||
|       this.assambleToot(c, i) | ||||
|     ); | ||||
|     } | ||||
| 
 | ||||
|     if (status_.spoiler_text !== "") { | ||||
|       content = | ||||
|         '<div class="toot-text">' + | ||||
|         status_.spoiler_text + | ||||
|         ' <button type="button" class="spoiler-link" aria-expanded="false">Show more</button>' + | ||||
|         '<div class="spoiler-text-hidden">' + | ||||
|         this.formatTootText(status_.content) + | ||||
|         "</div>" + | ||||
|         "</div>"; | ||||
|     } else if ( | ||||
|       status_.reblog && | ||||
|       status_.reblog.content !== "" && | ||||
|       status_.reblog.spoiler_text !== "" | ||||
|     ) { | ||||
|       content = | ||||
|         '<div class="toot-text">' + | ||||
|         status_.reblog.spoiler_text + | ||||
|         ' <button type="button" class="spoiler-link" aria-expanded="false">Show more</button>' + | ||||
|         '<div class="spoiler-text-hidden">' + | ||||
|         this.formatTootText(status_.reblog.content) + | ||||
|         "</div>" + | ||||
|         "</div>"; | ||||
|     } else if ( | ||||
|       status_.reblog && | ||||
|       status_.reblog.content !== "" && | ||||
|       status_.reblog.spoiler_text === "" | ||||
|     ) { | ||||
|       content = | ||||
|         '<div class="toot-text ' + | ||||
|         text_css + | ||||
|         '">' + | ||||
|         "<div>" + | ||||
|         this.formatTootText(status_.reblog.content) + | ||||
|         "</div>" + | ||||
|         "</div>"; | ||||
|     } else { | ||||
|       content = | ||||
|         '<div class="toot-text ' + | ||||
|         text_css + | ||||
|         '">' + | ||||
|         "<div>" + | ||||
|         this.formatTootText(status_.content) + | ||||
|         "</div>" + | ||||
|         "</div>"; | ||||
|     } | ||||
| 
 | ||||
|     // Media attachments
 | ||||
|     let media = ""; | ||||
|     if (status_.media_attachments.length > 0) { | ||||
|       for (let picid in status_.media_attachments) { | ||||
|         media = this.replaceMedias( | ||||
|           status_.media_attachments[picid], | ||||
|           status_.sensitive | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|     if (status_.reblog && status_.reblog.media_attachments.length > 0) { | ||||
|       for (let picid in status_.reblog.media_attachments) { | ||||
|         media = this.replaceMedias( | ||||
|           status_.reblog.media_attachments[picid], | ||||
|           status_.reblog.sensitive | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Poll
 | ||||
|     let poll = ""; | ||||
|     let pollOption = ""; | ||||
|     if (status_.poll) { | ||||
|       for (let i in status_.poll.options) { | ||||
|         pollOption += "<li>" + status_.poll.options[i].title + "</li>"; | ||||
|       } | ||||
|       poll = | ||||
|         '<div class="toot-poll">' + "<ul>" + pollOption + "</ul>" + "</div>"; | ||||
|     } | ||||
| 
 | ||||
|     // Date
 | ||||
|     let timestamp = | ||||
|       '<div class="toot-date">' + | ||||
|       '<a href="' + | ||||
|       url + | ||||
|       '" rel="nofollow noopener noreferrer" tabindex="-1" target="_blank">' + | ||||
|       date + | ||||
|       "</a>" + | ||||
|       "</div>"; | ||||
| 
 | ||||
|     // Add all to main toot container
 | ||||
|     let toot = | ||||
|       '<article class="mt-toot" aria-posinset="' + | ||||
|       (Number(index) + 1) + | ||||
|       '" aria-setsize="' + | ||||
|       this.TOOTS_LIMIT + | ||||
|       '" data-location="' + | ||||
|       url + | ||||
|       '" tabindex="0">' + | ||||
|       avatar + | ||||
|       user + | ||||
|       content + | ||||
|       media + | ||||
|       poll + | ||||
|       timestamp + | ||||
|       "</article>"; | ||||
| 
 | ||||
|     this.mtBodyContainer.insertAdjacentHTML("beforeend", toot); | ||||
|   }; | ||||
| 
 | ||||
|   // Toot interactions
 | ||||
|   this.mtBodyContainer.addEventListener("click", function (e) { | ||||
|     // Check if clicked in a toot
 | ||||
|     // Check if toot cointainer was clicked
 | ||||
|     if ( | ||||
|       e.target.localName == "article" || | ||||
|       e.target.offsetParent.localName == "article" || | ||||
| @ -390,7 +237,7 @@ MastodonApi.prototype.getToots = function () { | ||||
|     ) { | ||||
|       openTootURL(e); | ||||
|     } | ||||
|     // Check if clicked in Show More/Less button
 | ||||
|     // Check if Show More/Less button was clicked
 | ||||
|     if ( | ||||
|       e.target.localName == "button" && | ||||
|       e.target.className == "spoiler-link" | ||||
| @ -399,26 +246,33 @@ MastodonApi.prototype.getToots = function () { | ||||
|     } | ||||
|   }); | ||||
|   this.mtBodyContainer.addEventListener("keydown", function (e) { | ||||
|     // Check if Enter key pressed with focus in an article
 | ||||
|     // Check if Enter key was pressed with focus in an article
 | ||||
|     if (e.key === "Enter" && e.target.localName == "article") { | ||||
|       openTootURL(e); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   // Open Toot in a new page avoiding any other natural link
 | ||||
|   /** | ||||
|    * Open toot in a new page avoiding any other natural link | ||||
|    * @param {event} e User interaction trigger | ||||
|    */ | ||||
|   const openTootURL = function (e) { | ||||
|     let urlToot = e.target.closest(".mt-toot").dataset.location; | ||||
|     if ( | ||||
|       e.target.localName !== "a" && | ||||
|       e.target.localName !== "span" && | ||||
|       e.target.localName !== "button" && | ||||
|       e.target.parentNode.className !== "toot-preview-image" && | ||||
|       urlToot | ||||
|     ) { | ||||
|       window.open(urlToot, "_blank"); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   // Spoiler button
 | ||||
|   /** | ||||
|    * Spoiler button | ||||
|    * @param {event} e User interaction trigger | ||||
|    */ | ||||
|   const toogleSpoiler = function (e) { | ||||
|     const nextSibling = e.target.nextSibling; | ||||
|     if (nextSibling.localName === "img") { | ||||
| @ -443,7 +297,206 @@ MastodonApi.prototype.getToots = function () { | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| // Handle text changes made to Toots
 | ||||
| /** | ||||
|  * Build toot structure | ||||
|  * @param {object} c Toot content | ||||
|  * @param {number} i Index of toot | ||||
|  */ | ||||
| MastodonApi.prototype.assambleToot = function (c, i) { | ||||
|   let avatar, user, content, url, date; | ||||
| 
 | ||||
|   if (c.reblog) { | ||||
|     // BOOSTED toot
 | ||||
|     // Toot url
 | ||||
|     url = c.reblog.url; | ||||
| 
 | ||||
|     // Boosted avatar
 | ||||
|     avatar = | ||||
|       '<a href="' + | ||||
|       c.reblog.account.url + | ||||
|       '" class="mt-avatar mt-avatar-boosted" style="background-image:url(' + | ||||
|       c.reblog.account.avatar + | ||||
|       ');" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|       '<div class="mt-avatar mt-avatar-booster" style="background-image:url(' + | ||||
|       c.account.avatar + | ||||
|       ');">' + | ||||
|       "</div>" + | ||||
|       '<span class="visually-hidden">' + | ||||
|       c.account.username + | ||||
|       " avatar" + | ||||
|       "</span>" + | ||||
|       "</a>"; | ||||
| 
 | ||||
|     // User name and url
 | ||||
|     user = | ||||
|       '<div class="mt-user">' + | ||||
|       '<a href="' + | ||||
|       c.reblog.account.url + | ||||
|       '" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|       c.reblog.account.username + | ||||
|       '<span class="visually-hidden"> post</span>' + | ||||
|       "</a>" + | ||||
|       "</div>"; | ||||
| 
 | ||||
|     // Date
 | ||||
|     date = this.formatDate(c.reblog.created_at); | ||||
|   } else { | ||||
|     // STANDARD toot
 | ||||
|     // Toot url
 | ||||
|     url = c.url; | ||||
| 
 | ||||
|     // Avatar
 | ||||
|     avatar = | ||||
|       '<a href="' + | ||||
|       c.account.url + | ||||
|       '" class="mt-avatar" style="background-image:url(' + | ||||
|       c.account.avatar + | ||||
|       ');" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|       '<span class="visually-hidden">' + | ||||
|       c.account.username + | ||||
|       " avatar" + | ||||
|       "</span>" + | ||||
|       "</a>"; | ||||
| 
 | ||||
|     // User name and url
 | ||||
|     user = | ||||
|       '<div class="mt-user">' + | ||||
|       '<a href="' + | ||||
|       c.account.url + | ||||
|       '" rel="nofollow noopener noreferrer" target="_blank">' + | ||||
|       c.account.username + | ||||
|       '<span class="visually-hidden"> post</span>' + | ||||
|       "</a>" + | ||||
|       "</div>"; | ||||
| 
 | ||||
|     // Date
 | ||||
|     date = this.formatDate(c.created_at); | ||||
|   } | ||||
| 
 | ||||
|   // Main text
 | ||||
|   let text_css = ""; | ||||
|   if (this.TEXT_MAX_LINES !== "0") { | ||||
|     text_css = "truncate"; | ||||
|     document.documentElement.style.setProperty( | ||||
|       "--text-max-lines", | ||||
|       this.TEXT_MAX_LINES | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (c.spoiler_text !== "") { | ||||
|     content = | ||||
|       '<div class="toot-text">' + | ||||
|       c.spoiler_text + | ||||
|       ' <button type="button" class="spoiler-link" aria-expanded="false">Show more</button>' + | ||||
|       '<div class="spoiler-text-hidden">' + | ||||
|       this.formatTootText(c.content) + | ||||
|       "</div>" + | ||||
|       "</div>"; | ||||
|   } else if ( | ||||
|     c.reblog && | ||||
|     c.reblog.content !== "" && | ||||
|     c.reblog.spoiler_text !== "" | ||||
|   ) { | ||||
|     content = | ||||
|       '<div class="toot-text">' + | ||||
|       c.reblog.spoiler_text + | ||||
|       ' <button type="button" class="spoiler-link" aria-expanded="false">Show more</button>' + | ||||
|       '<div class="spoiler-text-hidden">' + | ||||
|       this.formatTootText(c.reblog.content) + | ||||
|       "</div>" + | ||||
|       "</div>"; | ||||
|   } else if ( | ||||
|     c.reblog && | ||||
|     c.reblog.content !== "" && | ||||
|     c.reblog.spoiler_text === "" | ||||
|   ) { | ||||
|     content = | ||||
|       '<div class="toot-text ' + | ||||
|       text_css + | ||||
|       '">' + | ||||
|       "<div>" + | ||||
|       this.formatTootText(c.reblog.content) + | ||||
|       "</div>" + | ||||
|       "</div>"; | ||||
|   } else { | ||||
|     content = | ||||
|       '<div class="toot-text ' + | ||||
|       text_css + | ||||
|       '">' + | ||||
|       "<div>" + | ||||
|       this.formatTootText(c.content) + | ||||
|       "</div>" + | ||||
|       "</div>"; | ||||
|   } | ||||
| 
 | ||||
|   // Media attachments
 | ||||
|   let media = ""; | ||||
|   if (c.media_attachments.length > 0) { | ||||
|     for (let picid in c.media_attachments) { | ||||
|       media = this.placeMedias(c.media_attachments[picid], c.sensitive); | ||||
|     } | ||||
|   } | ||||
|   if (c.reblog && c.reblog.media_attachments.length > 0) { | ||||
|     for (let picid in c.reblog.media_attachments) { | ||||
|       media = this.placeMedias( | ||||
|         c.reblog.media_attachments[picid], | ||||
|         c.reblog.sensitive | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Preview link
 | ||||
|   let preview_link = ""; | ||||
|   if (!this.HIDE_PREVIEW_LINK && c.card) { | ||||
|     preview_link = this.placePreviewLink(c.card); | ||||
|   } | ||||
| 
 | ||||
|   // Poll
 | ||||
|   let poll = ""; | ||||
|   let pollOption = ""; | ||||
|   if (c.poll) { | ||||
|     for (let i in c.poll.options) { | ||||
|       pollOption += "<li>" + c.poll.options[i].title + "</li>"; | ||||
|     } | ||||
|     poll = '<div class="toot-poll">' + "<ul>" + pollOption + "</ul>" + "</div>"; | ||||
|   } | ||||
| 
 | ||||
|   // Date
 | ||||
|   const timestamp = | ||||
|     '<div class="toot-date">' + | ||||
|     '<a href="' + | ||||
|     url + | ||||
|     '" rel="nofollow noopener noreferrer" tabindex="-1" target="_blank">' + | ||||
|     date + | ||||
|     "</a>" + | ||||
|     "</div>"; | ||||
| 
 | ||||
|   // Add all to main toot container
 | ||||
|   const toot = | ||||
|     '<article class="mt-toot" aria-posinset="' + | ||||
|     (i + 1) + | ||||
|     '" aria-setsize="' + | ||||
|     this.TOOTS_LIMIT + | ||||
|     '" data-location="' + | ||||
|     url + | ||||
|     '" tabindex="0">' + | ||||
|     avatar + | ||||
|     user + | ||||
|     content + | ||||
|     media + | ||||
|     preview_link + | ||||
|     poll + | ||||
|     timestamp + | ||||
|     "</article>"; | ||||
| 
 | ||||
|   return toot; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Handle text changes made to toots | ||||
|  * @param {string} c Text content | ||||
|  * @returns {string} Text content modified | ||||
|  */ | ||||
| MastodonApi.prototype.formatTootText = function (c) { | ||||
|   let content = c; | ||||
| 
 | ||||
| @ -464,17 +517,30 @@ MastodonApi.prototype.formatTootText = function (c) { | ||||
|   return content; | ||||
| }; | ||||
| 
 | ||||
| // Add target="_blank" to all #hashtags and @mentions
 | ||||
| /** | ||||
|  * Add target="_blank" to all #hashtags and @mentions in the toot | ||||
|  * @param {string} c Text content | ||||
|  * @returns {string} Text content modified | ||||
|  */ | ||||
| MastodonApi.prototype.addTarget2hashtagMention = function (c) { | ||||
|   let content = c.replaceAll('rel="tag"', 'rel="tag" target="_blank"'); | ||||
|   content = content.replaceAll( | ||||
|     'class="u-url mention"', | ||||
|     'class="u-url mention" target="_blank"' | ||||
|   ); | ||||
| 
 | ||||
|   return content; | ||||
| }; | ||||
| 
 | ||||
| // Find all start/end <tags> and replace them by another start/end <tags>
 | ||||
| /** | ||||
|  * Find all start/end <tags> and replace them by another start/end <tags> | ||||
|  * @param {string} c Text content | ||||
|  * @param {string} initialTagOpen Start HTML tag to replace | ||||
|  * @param {string} initialTagClose End HTML tag to replace | ||||
|  * @param {string} replacedTagOpen New start HTML tag | ||||
|  * @param {string} replacedTagClose New end HTML tag | ||||
|  * @returns {string} Text in HTML format | ||||
|  */ | ||||
| MastodonApi.prototype.replaceHTMLtag = function ( | ||||
|   c, | ||||
|   initialTagOpen, | ||||
| @ -484,14 +550,20 @@ MastodonApi.prototype.replaceHTMLtag = function ( | ||||
| ) { | ||||
|   if (c.includes(initialTagOpen)) { | ||||
|     const regex = new RegExp(initialTagOpen + "(.*?)" + initialTagClose, "gi"); | ||||
| 
 | ||||
|     return c.replace(regex, replacedTagOpen + "$1" + replacedTagClose); | ||||
|   } else { | ||||
|     return c; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // Place media
 | ||||
| MastodonApi.prototype.replaceMedias = function (m, s) { | ||||
| /** | ||||
|  * Place media | ||||
|  * @param {object} m Media content | ||||
|  * @param {boolean} s Spoiler/Sensitive status | ||||
|  * @returns {string} Media in HTML format | ||||
|  */ | ||||
| MastodonApi.prototype.placeMedias = function (m, s) { | ||||
|   let spoiler = s || false; | ||||
|   const pic = | ||||
|     '<div class="toot-media ' + | ||||
| @ -500,13 +572,51 @@ MastodonApi.prototype.replaceMedias = function (m, s) { | ||||
|     (spoiler ? '<button class="spoiler-link">Show content</button>' : "") + | ||||
|     '<img onload="removeSpinner(this)" onerror="removeSpinner(this)" src="' + | ||||
|     m.preview_url + | ||||
|     '" alt="" loading="lazy" />' + | ||||
|     '" alt="' + | ||||
|     (m.description ? m.description : "") + | ||||
|     '" loading="lazy" />' + | ||||
|     "</div>"; | ||||
| 
 | ||||
|   return pic; | ||||
| }; | ||||
| 
 | ||||
| // Format date
 | ||||
| /** | ||||
|  * Place preview link | ||||
|  * @param {object} c Preview link content | ||||
|  * @returns {string} Preview link in HTML format | ||||
|  */ | ||||
| MastodonApi.prototype.placePreviewLink = function (c) { | ||||
|   let card = | ||||
|     '<a href="' + | ||||
|     c.url + | ||||
|     '" class="toot-preview-link" target="_blank" rel="noopener noreferrer">' + | ||||
|     (c.image | ||||
|       ? '<div class="toot-preview-image"><img onload="removeSpinner(this)" onerror="removeSpinner(this)" src="' + | ||||
|         c.image + | ||||
|         '" alt="" loading="lazy" /></div>' | ||||
|       : '<div class="toot-preview-noImage">📄</div>') + | ||||
|     "</div>" + | ||||
|     '<div class="toot-preview-content">' + | ||||
|     (c.provider_name | ||||
|       ? '<span class="toot-preview-provider">' + c.provider_name + "</span>" | ||||
|       : "") + | ||||
|     '<span class="toot-preview-title">' + | ||||
|     c.title + | ||||
|     "</span>" + | ||||
|     (c.author_name | ||||
|       ? '<span class="toot-preview-author">By ' + c.author_name + "</span>" | ||||
|       : "") + | ||||
|     "</div>" + | ||||
|     "</a>"; | ||||
| 
 | ||||
|   return card; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Format date | ||||
|  * @param {string} d Date in ISO format (YYYY-MM-DDTHH:mm:ss.sssZ) | ||||
|  * @returns {string} Date formated (MM DD, YYYY) | ||||
|  */ | ||||
| MastodonApi.prototype.formatDate = function (d) { | ||||
|   const monthNames = [ | ||||
|     "Jan", | ||||
| @ -535,11 +645,16 @@ MastodonApi.prototype.formatDate = function (d) { | ||||
|   return displayDate; | ||||
| }; | ||||
| 
 | ||||
| // Loading spinner
 | ||||
| /** | ||||
|  * Loading spinner | ||||
|  * @param {object} e Image containing the spinner | ||||
|  */ | ||||
| const removeSpinner = function (e) { | ||||
|   const spinnerCSS = "loading-spinner"; | ||||
| 
 | ||||
|   // Find closest parent container (1st, 2nd or 3rd level)
 | ||||
|   let spinnerContainer = e.closest("." + spinnerCSS); | ||||
| 
 | ||||
|   if (spinnerContainer) { | ||||
|     spinnerContainer.classList.remove(spinnerCSS); | ||||
|   } | ||||
|  | ||||
							
								
								
									
										2
									
								
								src/mastodon-timeline.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/mastodon-timeline.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								src/mastodon-timeline.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/mastodon-timeline.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 i.j
						i.j