Feature/posts limit
This commit is contained in:
		
							parent
							
								
									a78de16fd9
								
							
						
					
					
						commit
						410e83bce1
					
				
							
								
								
									
										10
									
								
								CHANGELOG
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								CHANGELOG
									
									
									
									
									
								
							| @ -1,3 +1,11 @@ | |||||||
|  | v4.3.10 - 21/03/2024 | ||||||
|  | - Allow to load more than 20 or 40 posts | ||||||
|  | - Add link preview description | ||||||
|  | - Allow to choose a maximum number of lines of text in preview description | ||||||
|  | - Fix carousel conflict with avatar and emojos images | ||||||
|  | - Fix possible error in aria-setsize with values greater than the total number of posts | ||||||
|  | - JS refactoring | ||||||
|  | 
 | ||||||
| v4.3.7 - 12/03/2024 | v4.3.7 - 12/03/2024 | ||||||
| - Display medias inside post using CSS grid | - Display medias inside post using CSS grid | ||||||
| - Add a placeholder bg-color for images | - Add a placeholder bg-color for images | ||||||
| @ -168,7 +176,7 @@ v3.1.1 - 28/01/2023 | |||||||
| 
 | 
 | ||||||
| v3.1.0 - 21/01/2023 | v3.1.0 - 21/01/2023 | ||||||
| - Fix spoiler content show/hide | - Fix spoiler content show/hide | ||||||
| - Add feature, choose a maximum number of lines of text | - Allow to choose a maximum number of lines of text in posts | ||||||
| - Hide button to user page if 'btn_see_more' is empty | - Hide button to user page if 'btn_see_more' is empty | ||||||
| 
 | 
 | ||||||
| v2.12.0 - 02/12/2022 | v2.12.0 - 02/12/2022 | ||||||
|  | |||||||
							
								
								
									
										88
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								README.md
									
									
									
									
									
								
							| @ -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: | Copy the following CSS and JS links to include them in your project: | ||||||
| 
 | 
 | ||||||
| ```html | ```html | ||||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.7/dist/mastodon-timeline.min.css" integrity="sha256-TxNxDe916jqa7iqnY5d3/1SuHlB+/4r9XEH0kOwh2Nc=" crossorigin="anonymous"> | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.10/dist/mastodon-timeline.min.css" crossorigin="anonymous"> | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ```html | ```html | ||||||
| <script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.7/dist/mastodon-timeline.umd.js" integrity="sha256-VK7I7SRA8gZaOzjlIQ6aeG0vOlkzuRnstJi2fgR3L80=" crossorigin="anonymous"></script> | <script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.10/dist/mastodon-timeline.umd.js" crossorigin="anonymous"></script> | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### Package manager | ### Package manager | ||||||
| @ -182,48 +182,54 @@ Here you have all the options available to quickly setup and customize your time | |||||||
| 
 | 
 | ||||||
| ```js | ```js | ||||||
|   // Id of the <div> containing the timeline |   // Id of the <div> containing the timeline | ||||||
|  |   // Default: "mt-container" | ||||||
|   mtContainerId: "mt-container", |   mtContainerId: "mt-container", | ||||||
| 
 | 
 | ||||||
|   // Mastodon instance Url including https:// |   // Mastodon instance Url including https:// | ||||||
|  |   // Default: "https://mastodon.social" | ||||||
|   instanceUrl: "https://mastodon.social", |   instanceUrl: "https://mastodon.social", | ||||||
| 
 | 
 | ||||||
|   // Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag' |   // Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag' | ||||||
|   // Default: local |   // Default: "local" | ||||||
|   timelineType: "local", |   timelineType: "local", | ||||||
| 
 | 
 | ||||||
|   // Your user ID number on Mastodon instance |   // Your user ID number on Mastodon instance | ||||||
|   // Leave it empty if you didn't choose 'profile' as type of timeline |   // Leave it empty if you didn't choose 'profile' as type of timeline | ||||||
|  |   // Default: "" | ||||||
|   userId: "", |   userId: "", | ||||||
| 
 | 
 | ||||||
|   // Your user name on Mastodon instance (including the @ symbol at the beginning) |   // Your user name on Mastodon instance (including the @ symbol at the beginning) | ||||||
|   // Leave it empty if you didn't choose 'profile' as type of timeline |   // Leave it empty if you didn't choose 'profile' as type of timeline | ||||||
|  |   // Default: "" | ||||||
|   profileName: "", |   profileName: "", | ||||||
| 
 | 
 | ||||||
|   // The name of the hashtag (not including the # symbol) |   // The name of the hashtag (not including the # symbol) | ||||||
|   // Leave it empty if you didn't choose 'hashtag' as type of timeline |   // Leave it empty if you didn't choose 'hashtag' as type of timeline | ||||||
|  |   // Default: "" | ||||||
|   hashtagName: "", |   hashtagName: "", | ||||||
| 
 | 
 | ||||||
|   // Class name for the loading spinner (also used in CSS file) |   // Class name for the loading spinner (also used in CSS file) | ||||||
|  |   // Default: "mt-loading-spinner" | ||||||
|   spinnerClass: "mt-loading-spinner", |   spinnerClass: "mt-loading-spinner", | ||||||
| 
 | 
 | ||||||
|   // Preferred color theme: 'light', 'dark' or 'auto' |   // Preferred color theme: "light", "dark" or "auto" | ||||||
|   // Default: auto |   // Default: "auto" | ||||||
|   defaultTheme: "auto", |   defaultTheme: "auto", | ||||||
| 
 | 
 | ||||||
|   // Maximum number of posts to request to the server |   // Maximum number of posts to request to the server | ||||||
|   // Default: 20 |   // Default: "20" | ||||||
|   maxNbPostFetch: "20", |   maxNbPostFetch: "20", | ||||||
| 
 | 
 | ||||||
|   // Maximum number of posts to show in the timeline |   // Maximum number of posts to show in the timeline | ||||||
|   // Default: 20 |   // Default: "20" | ||||||
|   maxNbPostShow: "20", |   maxNbPostShow: "20", | ||||||
| 
 | 
 | ||||||
|   // Specifies the format of the date according to the chosen language/country |   // Specifies the format of the date according to the chosen language/country | ||||||
|   // Default: British English (day-month-year order) |   // Default: "en-GB" (British English: day-month-year order) | ||||||
|   dateLocale: "en-GB", |   dateLocale: "en-GB", | ||||||
| 
 | 
 | ||||||
|   // Customize the date format using the options |   // Customize the date format using the options for day, month and year | ||||||
|   // Default: DD MMM YYYY |   // Default: day: "2-digit", month: "short", year: "numeric" (DD MMM YYYY) | ||||||
|   dateOptions: { |   dateOptions: { | ||||||
|     day: "2-digit", |     day: "2-digit", | ||||||
|     month: "short", |     month: "short", | ||||||
| @ -231,47 +237,64 @@ Here you have all the options available to quickly setup and customize your time | |||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   // Hide unlisted posts |   // Hide unlisted posts | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideUnlisted: false, |   hideUnlisted: false, | ||||||
| 
 | 
 | ||||||
|   // Hide boosted posts |   // Hide boosted posts | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideReblog: false, |   hideReblog: false, | ||||||
| 
 | 
 | ||||||
|   // Hide replies posts |   // Hide replies posts | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideReplies: false, |   hideReplies: false, | ||||||
| 
 | 
 | ||||||
|   // Hide pinned posts from the profile timeline |   // Hide pinned posts from the profile timeline | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hidePinnedPosts: false, |   hidePinnedPosts: false, | ||||||
| 
 | 
 | ||||||
|   // Hide the user account under the user name |   // Hide the user account under the user name | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideUserAccount: false, |   hideUserAccount: false, | ||||||
| 
 | 
 | ||||||
|  |   // Limit the text content to a maximum number of lines | ||||||
|  |   // Use "0" to show no text | ||||||
|  |   // Default: "" (unlimited) | ||||||
|  |   txtMaxLines: "", | ||||||
|  | 
 | ||||||
|  |   // Customize the text of the button used for showing/hiding sensitive/spoiler text | ||||||
|  |   btnShowMore: "SHOW MORE", | ||||||
|  |   btnShowLess: "SHOW LESS",   | ||||||
|  | 
 | ||||||
|  |   // Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag | ||||||
|  |   // Default: false (don't apply) | ||||||
|  |   markdownBlockquote: false,   | ||||||
|  | 
 | ||||||
|   // Hide custom emojis available on the server |   // Hide custom emojis available on the server | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideEmojos: false,   |   hideEmojos: false,   | ||||||
| 
 | 
 | ||||||
|  |   // Customize the text of the button used for showing sensitive/spoiler media content | ||||||
|  |   btnShowContent: "SHOW CONTENT",   | ||||||
|  | 
 | ||||||
|   // Hide video image preview and load video player instead |   // Hide video image preview and load video player instead | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideVideoPreview: false, |   hideVideoPreview: false, | ||||||
| 
 | 
 | ||||||
|   // Hide preview card if post contains a link, photo or video from a Url |   // Hide preview card if post contains a link, photo or video from a Url | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hidePreviewLink: false, |   hidePreviewLink: false, | ||||||
| 
 | 
 | ||||||
|  |   // Limit the preview text description to a maximum number of lines | ||||||
|  |   // Use "0" to show no text | ||||||
|  |   // Default: "" (unlimited) | ||||||
|  |   previewMaxLines: "", | ||||||
|  | 
 | ||||||
|   // Hide replies, boosts and favourites posts counter |   // Hide replies, boosts and favourites posts counter | ||||||
|   // Default: don't hide |   // Default: false (don't hide) | ||||||
|   hideCounterBar: false, |   hideCounterBar: false, | ||||||
| 
 | 
 | ||||||
|   // Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag |   // Disable a carousel/lightbox when the user clicks on a picture in a post | ||||||
|   // Default: don't apply |   // Default: false (not disabled) | ||||||
|   markdownBlockquote: false, |  | ||||||
| 
 |  | ||||||
|   // Show a carousel/lightbox when the user clicks on a picture in a post |  | ||||||
|   // Default: not disabled |  | ||||||
|   disableCarousel: false, |   disableCarousel: false, | ||||||
| 
 | 
 | ||||||
|   // Customize the text of the buttons used for the carousel/lightbox |   // Customize the text of the buttons used for the carousel/lightbox | ||||||
| @ -279,17 +302,6 @@ Here you have all the options available to quickly setup and customize your time | |||||||
|   carouselPrevTxt: "Previous media item", |   carouselPrevTxt: "Previous media item", | ||||||
|   carouselNextTxt: "Next 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/spoiler text |  | ||||||
|   btnShowMore: "SHOW MORE", |  | ||||||
|   btnShowLess: "SHOW LESS", |  | ||||||
| 
 |  | ||||||
|   // 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 |   // Customize the text of the button pointing to the Mastodon page placed at the end of the timeline | ||||||
|   // Leave the value empty to hide it |   // Leave the value empty to hide it | ||||||
|   btnSeeMore: "See more posts at Mastodon", |   btnSeeMore: "See more posts at Mastodon", | ||||||
| @ -299,11 +311,11 @@ Here you have all the options available to quickly setup and customize your time | |||||||
|   btnReload: "Refresh", |   btnReload: "Refresh", | ||||||
| 
 | 
 | ||||||
|   // Keep searching for the main <div> container before building the timeline. Useful in some cases where extra time is needed to render the page |   // Keep searching for the main <div> container before building the timeline. Useful in some cases where extra time is needed to render the page | ||||||
|   // Default: don't apply |   // Default: false (don't apply) | ||||||
|   insistSearchContainer: false, |   insistSearchContainer: false, | ||||||
| 
 | 
 | ||||||
|   // Defines the maximum time to continue searching for the main <div> container |   // Defines the maximum time to continue searching for the main <div> container | ||||||
|   // Default: 3 seconds |   // Default: "3000" (3 seconds) | ||||||
|   insistSearchContainerTime: "3000", |   insistSearchContainerTime: "3000", | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								dist/mastodon-timeline.esm.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								dist/mastodon-timeline.esm.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/mastodon-timeline.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/mastodon-timeline.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										4
									
								
								dist/mastodon-timeline.umd.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								dist/mastodon-timeline.umd.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -113,7 +113,7 @@ | |||||||
|         <h1>🐘 Mastodon embed timeline</h1> |         <h1>🐘 Mastodon embed timeline</h1> | ||||||
|         <h2>Local timeline (customized)</h2> |         <h2>Local timeline (customized)</h2> | ||||||
|         <p> |         <p> | ||||||
|           This example shows 10 posts from the following instance: |           This example shows 42 posts from the following instance: | ||||||
|           <br /> |           <br /> | ||||||
|           <a |           <a | ||||||
|             href="https://mastodon.social/public/local" |             href="https://mastodon.social/public/local" | ||||||
| @ -183,6 +183,8 @@ | |||||||
|     const myTimeline = new MastodonTimeline.Init({ |     const myTimeline = new MastodonTimeline.Init({ | ||||||
|       instanceUrl: "https://mastodon.online", |       instanceUrl: "https://mastodon.online", | ||||||
|       defaultTheme: "light", |       defaultTheme: "light", | ||||||
|  |       maxNbPostFetch: "42", | ||||||
|  |       maxNbPostShow: "42", | ||||||
|       dateLocale: "en-CA", |       dateLocale: "en-CA", | ||||||
|       dateOptions: { |       dateOptions: { | ||||||
|         day: "2-digit", |         day: "2-digit", | ||||||
| @ -219,7 +221,8 @@ | |||||||
|       const myTimeline = new MastodonTimeline.Init({ |       const myTimeline = new MastodonTimeline.Init({ | ||||||
|         instanceUrl: "https://mastodon.online", |         instanceUrl: "https://mastodon.online", | ||||||
|         defaultTheme: "light", |         defaultTheme: "light", | ||||||
|         maxNbPostShow: "10", |         maxNbPostFetch: "42", | ||||||
|  |         maxNbPostShow: "42", | ||||||
|         dateLocale: "en-CA", |         dateLocale: "en-CA", | ||||||
|         dateOptions: { |         dateOptions: { | ||||||
|           day: "2-digit", |           day: "2-digit", | ||||||
|  | |||||||
| @ -123,7 +123,8 @@ | |||||||
| 
 | 
 | ||||||
|         <h2>Theme API</h2> |         <h2>Theme API</h2> | ||||||
|         <p> |         <p> | ||||||
|           You can change your timeline color calling the function <strong>mtColorTheme()</strong> |           You can change your timeline color calling the function | ||||||
|  |           <strong>mtColorTheme()</strong> | ||||||
|         </p> |         </p> | ||||||
|         <div class="dummy-buttons-container"> |         <div class="dummy-buttons-container"> | ||||||
|           <button onclick="myTimeline01.mtColorTheme('dark')"> |           <button onclick="myTimeline01.mtColorTheme('dark')"> | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,12 +1,12 @@ | |||||||
| { | { | ||||||
|   "name": "@idotj/mastodon-embed-timeline", |   "name": "@idotj/mastodon-embed-timeline", | ||||||
|   "version": "4.3.7", |   "version": "4.3.10", | ||||||
|   "lockfileVersion": 3, |   "lockfileVersion": 3, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "name": "@idotj/mastodon-embed-timeline", |       "name": "@idotj/mastodon-embed-timeline", | ||||||
|       "version": "4.3.7", |       "version": "4.3.10", | ||||||
|       "license": "GNU", |       "license": "GNU", | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
|         "@rollup/plugin-terser": "^0.4.4", |         "@rollup/plugin-terser": "^0.4.4", | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@idotj/mastodon-embed-timeline", |   "name": "@idotj/mastodon-embed-timeline", | ||||||
|   "version": "4.3.7", |   "version": "4.3.10", | ||||||
|   "description": "Displays Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.", |   "description": "Displays Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.", | ||||||
|   "license": "GNU", |   "license": "GNU", | ||||||
|   "author": { |   "author": { | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| /* Mastodon embed timeline v4.3.7 */ | /* Mastodon embed timeline v4.3.10 */ | ||||||
| /* More info at: */ | /* More info at: */ | ||||||
| /* https://gitlab.com/idotj/mastodon-embed-timeline */ | /* https://gitlab.com/idotj/mastodon-embed-timeline */ | ||||||
| 
 | 
 | ||||||
| @ -8,6 +8,7 @@ | |||||||
| .mt-container[data-theme="light"], | .mt-container[data-theme="light"], | ||||||
| .mt-dialog[data-theme="light"] { | .mt-dialog[data-theme="light"] { | ||||||
|   --mt-txt-max-lines: none; |   --mt-txt-max-lines: none; | ||||||
|  |   --mt-preview-max-lines: none; | ||||||
|   --mt-color-bg: #fff; |   --mt-color-bg: #fff; | ||||||
|   --mt-color-bg-hover: #d9e1e8; |   --mt-color-bg-hover: #d9e1e8; | ||||||
|   --mt-color-line-gray: #c0cdd9; |   --mt-color-line-gray: #c0cdd9; | ||||||
| @ -438,6 +439,23 @@ body:has(dialog.mt-dialog[open]) { | |||||||
|   padding: 0.5rem 1rem; |   padding: 0.5rem 1rem; | ||||||
|   gap: 0.5rem; |   gap: 0.5rem; | ||||||
| } | } | ||||||
|  | .mt-post-preview-content:has(.mt-post-preview-description.truncate) { | ||||||
|  |   align-self: unset; | ||||||
|  | } | ||||||
|  | .mt-post-preview-description { | ||||||
|  |   display: block; | ||||||
|  |   color: var(--mt-color-contrast-gray); | ||||||
|  | } | ||||||
|  | .mt-post-preview-description.truncate { | ||||||
|  |   display: -webkit-box; | ||||||
|  |   overflow: hidden; | ||||||
|  |   -webkit-line-clamp: var(--mt-preview-max-lines); | ||||||
|  |   -webkit-box-orient: vertical; | ||||||
|  | } | ||||||
|  | .mt-post-preview-description:not(.truncate) .ellipsis::after { | ||||||
|  |   content: "..."; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .mt-post-preview-title { | .mt-post-preview-title { | ||||||
|   font-weight: 600; |   font-weight: 600; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| /** | /** | ||||||
|  * Mastodon embed timeline |  * Mastodon embed timeline | ||||||
|  * @author idotj |  * @author idotj | ||||||
|  * @version 4.3.7 |  * @version 4.3.10 | ||||||
|  * @url https://gitlab.com/idotj/mastodon-embed-timeline
 |  * @url https://gitlab.com/idotj/mastodon-embed-timeline
 | ||||||
|  * @license GNU AGPLv3 |  * @license GNU AGPLv3 | ||||||
|  */ |  */ | ||||||
| @ -31,19 +31,20 @@ export class Init { | |||||||
|       hideReplies: false, |       hideReplies: false, | ||||||
|       hidePinnedPosts: false, |       hidePinnedPosts: false, | ||||||
|       hideUserAccount: false, |       hideUserAccount: false, | ||||||
|  |       txtMaxLines: "", | ||||||
|  |       btnShowMore: "SHOW MORE", | ||||||
|  |       btnShowLess: "SHOW LESS", | ||||||
|  |       markdownBlockquote: false, | ||||||
|       hideEmojos: false, |       hideEmojos: false, | ||||||
|  |       btnShowContent: "SHOW CONTENT", | ||||||
|       hideVideoPreview: false, |       hideVideoPreview: false, | ||||||
|       hidePreviewLink: false, |       hidePreviewLink: false, | ||||||
|  |       previewMaxLines: "", | ||||||
|       hideCounterBar: false, |       hideCounterBar: false, | ||||||
|       markdownBlockquote: false, |  | ||||||
|       disableCarousel: false, |       disableCarousel: false, | ||||||
|       carouselCloseTxt: "Close carousel", |       carouselCloseTxt: "Close carousel", | ||||||
|       carouselPrevTxt: "Previous media item", |       carouselPrevTxt: "Previous media item", | ||||||
|       carouselNextTxt: "Next media item", |       carouselNextTxt: "Next media item", | ||||||
|       txtMaxLines: "0", |  | ||||||
|       btnShowMore: "SHOW MORE", |  | ||||||
|       btnShowLess: "SHOW LESS", |  | ||||||
|       btnShowContent: "SHOW CONTENT", |  | ||||||
|       btnSeeMore: "See more posts at Mastodon", |       btnSeeMore: "See more posts at Mastodon", | ||||||
|       btnReload: "Refresh", |       btnReload: "Refresh", | ||||||
|       insistSearchContainer: false, |       insistSearchContainer: false, | ||||||
| @ -52,6 +53,9 @@ export class Init { | |||||||
| 
 | 
 | ||||||
|     this.mtSettings = { ...this.defaultSettings, ...customSettings }; |     this.mtSettings = { ...this.defaultSettings, ...customSettings }; | ||||||
| 
 | 
 | ||||||
|  |     this.#checkMaxNbPost(); | ||||||
|  | 
 | ||||||
|  |     this.linkHeader = {}; | ||||||
|     this.mtContainerNode = ""; |     this.mtContainerNode = ""; | ||||||
|     this.mtBodyNode = ""; |     this.mtBodyNode = ""; | ||||||
|     this.fetchedData = {}; |     this.fetchedData = {}; | ||||||
| @ -61,6 +65,21 @@ export class Init { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** | ||||||
|  |    * Verify that the values of posts fetched and showed are consistent | ||||||
|  |    */ | ||||||
|  |   #checkMaxNbPost() { | ||||||
|  |     if ( | ||||||
|  |       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; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * Trigger callback when DOM loaded or completed |    * Trigger callback when DOM loaded or completed | ||||||
|    * @param {function} c Callback executed |    * @param {function} c Callback executed | ||||||
| @ -146,7 +165,7 @@ export class Init { | |||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Apply the color theme in the timeline |    * Apply the color theme in the timeline | ||||||
|    * @param {string} themeType Type of color theme |    * @param {string} themeType Type of color theme ('light' or 'dark') | ||||||
|    */ |    */ | ||||||
|   mtColorTheme(themeType) { |   mtColorTheme(themeType) { | ||||||
|     this.#onDOMContentLoaded(() => { |     this.#onDOMContentLoaded(() => { | ||||||
| @ -178,51 +197,18 @@ export class Init { | |||||||
|    */ |    */ | ||||||
|   #getTimelineData() { |   #getTimelineData() { | ||||||
|     return new Promise((resolve, reject) => { |     return new Promise((resolve, reject) => { | ||||||
|       const instanceApiUrl = `${this.mtSettings.instanceUrl}/api/v1/`; |       const instanceApiUrl = this.mtSettings.instanceUrl | ||||||
|       let urls = {}; |         ? `${this.mtSettings.instanceUrl}/api/v1/` | ||||||
| 
 |         : this.#showError( | ||||||
|       if (this.mtSettings.instanceUrl) { |  | ||||||
|         if (this.mtSettings.timelineType === "profile") { |  | ||||||
|           if (this.mtSettings.userId) { |  | ||||||
|             urls.timeline = `${instanceApiUrl}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`; |  | ||||||
|             if (!this.mtSettings.hidePinnedPosts) { |  | ||||||
|               urls.pinned = `${instanceApiUrl}accounts/${this.mtSettings.userId}/statuses?pinned=true`; |  | ||||||
|             } |  | ||||||
|           } else { |  | ||||||
|             this.#showError( |  | ||||||
|               "Please check your <strong>userId</strong> value", |  | ||||||
|               "⚠️" |  | ||||||
|             ); |  | ||||||
|           } |  | ||||||
|         } else if (this.mtSettings.timelineType === "hashtag") { |  | ||||||
|           if (this.mtSettings.hashtagName) { |  | ||||||
|             urls.timeline = `${instanceApiUrl}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`; |  | ||||||
|           } else { |  | ||||||
|             this.#showError( |  | ||||||
|               "Please check your <strong>hashtagName</strong> value", |  | ||||||
|               "⚠️" |  | ||||||
|             ); |  | ||||||
|           } |  | ||||||
|         } else if (this.mtSettings.timelineType === "local") { |  | ||||||
|           urls.timeline = `${instanceApiUrl}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`; |  | ||||||
|         } else { |  | ||||||
|           this.#showError( |  | ||||||
|             "Please check your <strong>timelineType</strong> value", |  | ||||||
|             "⚠️" |  | ||||||
|           ); |  | ||||||
|         } |  | ||||||
|       } else { |  | ||||||
|         this.#showError( |  | ||||||
|             "Please check your <strong>instanceUrl</strong> value", |             "Please check your <strong>instanceUrl</strong> value", | ||||||
|             "⚠️" |             "⚠️" | ||||||
|           ); |           ); | ||||||
|       } | 
 | ||||||
|       if (!this.mtSettings.hideEmojos) { |       const urls = this.#setUrls(instanceApiUrl); | ||||||
|         urls.emojos = `${instanceApiUrl}custom_emojis`; |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       const urlsPromises = Object.entries(urls).map(([key, url]) => { |       const urlsPromises = Object.entries(urls).map(([key, url]) => { | ||||||
|         return this.#fetchData(url) |         const headers = key === "timeline"; | ||||||
|  |         return this.#fetchData(url, headers) | ||||||
|           .then((data) => ({ [key]: data })) |           .then((data) => ({ [key]: data })) | ||||||
|           .catch((error) => { |           .catch((error) => { | ||||||
|             reject( |             reject( | ||||||
| @ -234,44 +220,12 @@ export class Init { | |||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       // Fetch all urls simultaneously
 |       // Fetch all urls simultaneously
 | ||||||
|       Promise.all(urlsPromises).then((dataObjects) => { |       Promise.all(urlsPromises).then(async (dataObjects) => { | ||||||
|         this.fetchedData = dataObjects.reduce((result, dataItem) => { |         this.fetchedData = dataObjects.reduce((result, dataItem) => { | ||||||
|           return { ...result, ...dataItem }; |           return { ...result, ...dataItem }; | ||||||
|         }, {}); |         }, {}); | ||||||
| 
 | 
 | ||||||
|         // console.log("Mastodon timeline data fetched: ", this.fetchedData);
 |  | ||||||
|         resolve(); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |  | ||||||
|    * 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:<br/>${url}<hr>Error status: ${response.status}<hr>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.#getTimelineData(); |  | ||||||
| 
 |  | ||||||
|         // Merge pinned posts with timeline posts
 |         // Merge pinned posts with timeline posts
 | ||||||
|     let posts; |  | ||||||
|         if ( |         if ( | ||||||
|           !this.mtSettings.hidePinnedPosts && |           !this.mtSettings.hidePinnedPosts && | ||||||
|           this.fetchedData.pinned?.length !== undefined && |           this.fetchedData.pinned?.length !== undefined && | ||||||
| @ -281,56 +235,224 @@ export class Init { | |||||||
|             ...obj, |             ...obj, | ||||||
|             pinned: true, |             pinned: true, | ||||||
|           })); |           })); | ||||||
|       posts = [...pinnedPosts, ...this.fetchedData.timeline]; |           this.fetchedData.timeline = [ | ||||||
|     } else { |             ...pinnedPosts, | ||||||
|       posts = this.fetchedData.timeline; |             ...this.fetchedData.timeline, | ||||||
|  |           ]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     // Empty container body
 |         // Fetch more posts if maxNbPostFetch is not reached
 | ||||||
|  |         if (this.#isNbPostsFulfilled()) { | ||||||
|  |           resolve(); | ||||||
|  |         } else { | ||||||
|  |           do { | ||||||
|  |             await this.#fetchMorePosts(); | ||||||
|  |           } while (!this.#isNbPostsFulfilled() && this.linkHeader.next); | ||||||
|  |           resolve(); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Set all urls before fetching the data | ||||||
|  |    * @param {string} Instance url api | ||||||
|  |    * @returns {object} | ||||||
|  |    */ | ||||||
|  |   #setUrls(i) { | ||||||
|  |     let urls = {}; | ||||||
|  | 
 | ||||||
|  |     if (this.mtSettings.timelineType === "profile") { | ||||||
|  |       if (this.mtSettings.userId) { | ||||||
|  |         urls.timeline = `${i}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`; | ||||||
|  |         if (!this.mtSettings.hidePinnedPosts) { | ||||||
|  |           urls.pinned = `${i}accounts/${this.mtSettings.userId}/statuses?pinned=true`; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         this.#showError( | ||||||
|  |           "Please check your <strong>userId</strong> value", | ||||||
|  |           "⚠️" | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } else if (this.mtSettings.timelineType === "hashtag") { | ||||||
|  |       if (this.mtSettings.hashtagName) { | ||||||
|  |         urls.timeline = `${i}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`; | ||||||
|  |       } else { | ||||||
|  |         this.#showError( | ||||||
|  |           "Please check your <strong>hashtagName</strong> value", | ||||||
|  |           "⚠️" | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } else if (this.mtSettings.timelineType === "local") { | ||||||
|  |       urls.timeline = `${i}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`; | ||||||
|  |     } else { | ||||||
|  |       this.#showError( | ||||||
|  |         "Please check your <strong>timelineType</strong> value", | ||||||
|  |         "⚠️" | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!this.mtSettings.hideEmojos) { | ||||||
|  |       urls.emojos = `${i}custom_emojis`; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return urls; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Fetch data from server | ||||||
|  |    * @param {string} u Url address to fetch | ||||||
|  |    * @param {boolean} h gets the link header | ||||||
|  |    * @returns {array} List of objects | ||||||
|  |    */ | ||||||
|  |   async #fetchData(u, h = false) { | ||||||
|  |     const response = await fetch(u); | ||||||
|  | 
 | ||||||
|  |     if (!response.ok) { | ||||||
|  |       throw new Error(` | ||||||
|  |         Failed to fetch the following Url:<br />${u}<hr />Error status: ${response.status}<hr />Error message: ${response.statusText} | ||||||
|  |         `);
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const data = await response.json(); | ||||||
|  | 
 | ||||||
|  |     // Get Link headers for pagination
 | ||||||
|  |     if (h && response.headers.get("Link")) { | ||||||
|  |       this.linkHeader = this.#parseLinkHeader(response.headers.get("Link")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Check if there are enough posts to reach the value of maxNbPostFetch | ||||||
|  |    * @returns {boolean} | ||||||
|  |    */ | ||||||
|  |   #isNbPostsFulfilled() { | ||||||
|  |     return ( | ||||||
|  |       this.fetchedData.timeline.length >= Number(this.mtSettings.maxNbPostFetch) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Fetch extra posts | ||||||
|  |    */ | ||||||
|  |   #fetchMorePosts() { | ||||||
|  |     return new Promise((resolve) => { | ||||||
|  |       if (this.linkHeader.next) { | ||||||
|  |         this.#fetchData(this.linkHeader.next, true).then((data) => { | ||||||
|  |           this.fetchedData.timeline = [...this.fetchedData.timeline, ...data]; | ||||||
|  |           resolve(); | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         resolve(); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Parse link header into an object | ||||||
|  |    * @param {string} l Link header | ||||||
|  |    * @returns {object} | ||||||
|  |    */ | ||||||
|  |   #parseLinkHeader(l) { | ||||||
|  |     const linkArray = l.split(", ").map((header) => header.split("; ")); | ||||||
|  |     const linkMap = linkArray.map((header) => { | ||||||
|  |       const linkRel = header[1].replace(/"/g, "").replace("rel=", ""); | ||||||
|  |       const linkURL = header[0].slice(1, -1); | ||||||
|  |       return [linkRel, linkURL]; | ||||||
|  |     }); | ||||||
|  |     return Object.fromEntries(linkMap); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Filter all fetched posts and append them on the timeline | ||||||
|  |    * @param {string} t Type of build (new or reload) | ||||||
|  |    */ | ||||||
|  |   async #buildTimeline(t) { | ||||||
|  |     await this.#getTimelineData(); | ||||||
|  | 
 | ||||||
|  |     // console.log("Mastodon timeline data fetched: ", this.fetchedData);
 | ||||||
|  | 
 | ||||||
|  |     const posts = this.fetchedData.timeline; | ||||||
|  |     let nbPostToShow = 0; | ||||||
|  | 
 | ||||||
|     this.mtBodyNode.replaceChildren(); |     this.mtBodyNode.replaceChildren(); | ||||||
| 
 | 
 | ||||||
|     // Set posts counter to 0
 |     posts.forEach((post) => { | ||||||
|     let nbPostShow = 0; |       const isPublicOrUnlisted = | ||||||
|  |         post.visibility === "public" || | ||||||
|  |         (!this.mtSettings.hideUnlisted && post.visibility === "unlisted"); | ||||||
|  |       const shouldHideReblog = this.mtSettings.hideReblog && post.reblog; | ||||||
|  |       const shouldHideReplies = | ||||||
|  |         this.mtSettings.hideReplies && post.in_reply_to_id; | ||||||
| 
 | 
 | ||||||
|     for (let i in posts) { |       // Filter by (Public / Unlisted)
 | ||||||
|       // First filter (Public / Unlisted)
 |       if (isPublicOrUnlisted && !shouldHideReblog && !shouldHideReplies) { | ||||||
|       if ( |         if (nbPostToShow < this.mtSettings.maxNbPostShow) { | ||||||
|         posts[i].visibility == "public" || |           this.#appendPost(post, nbPostToShow); | ||||||
|         (!this.mtSettings.hideUnlisted && posts[i].visibility == "unlisted") |           nbPostToShow++; | ||||||
|       ) { |  | ||||||
|         // Second filter (Reblog / Replies)
 |  | ||||||
|         if ( |  | ||||||
|           (this.mtSettings.hideReblog && posts[i].reblog) || |  | ||||||
|           (this.mtSettings.hideReplies && posts[i].in_reply_to_id) |  | ||||||
|         ) { |  | ||||||
|           // Nothing here (Don't append posts)
 |  | ||||||
|         } else { |         } else { | ||||||
|           if (nbPostShow < this.mtSettings.maxNbPostShow) { |           // Reached the limit of maximum number of posts to show
 | ||||||
|             this.#appendPost(posts[i], Number(i)); |  | ||||||
|             nbPostShow++; |  | ||||||
|           } else { |  | ||||||
|             // Nothing here (Reached the limit of maximum number of posts to show)
 |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|     // If there are no posts to display, show an error message
 |     // Check if there are posts to display or not
 | ||||||
|     if (this.mtBodyNode.innerHTML === "") { |     if (this.mtBodyNode.innerHTML !== "") { | ||||||
|       const errorMessage = `No posts to show<hr/>${ |  | ||||||
|         posts?.length || 0 |  | ||||||
|       } posts have been fetched from the server<hr/>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") { |       if (t === "newTimeline") { | ||||||
|         this.#manageSpinner(); |         this.#manageSpinner(); | ||||||
|         this.#setPostsInteracion(); |         this.#setCSSvariables(); | ||||||
|  |         this.#addAriaSetsize(nbPostToShow); | ||||||
|  |         this.#addPostListener(); | ||||||
|  |         if (this.mtSettings.btnSeeMore || this.mtSettings.btnReload) | ||||||
|           this.#buildFooter(); |           this.#buildFooter(); | ||||||
|       } else if (t === "updateTimeline") { |       } else if (t === "updateTimeline") { | ||||||
|         this.#manageSpinner(); |         this.#manageSpinner(); | ||||||
|       } else { |       } else { | ||||||
|         this.#showError("The function buildTimeline() was expecting a param"); |         this.#showError("The function buildTimeline() was expecting a param"); | ||||||
|       } |       } | ||||||
|  |     } else { | ||||||
|  |       const errorMessage = `No posts to show<hr />${ | ||||||
|  |         posts?.length || 0 | ||||||
|  |       } posts have been fetched from the server<hr />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, "📭"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Establishes the defined CSS variables | ||||||
|  |    */ | ||||||
|  |   #setCSSvariables() { | ||||||
|  |     if ( | ||||||
|  |       this.mtSettings.txtMaxLines !== "0" && | ||||||
|  |       this.mtSettings.txtMaxLines.length !== 0 | ||||||
|  |     ) { | ||||||
|  |       this.mtBodyNode.parentNode.style.setProperty( | ||||||
|  |         "--mt-txt-max-lines", | ||||||
|  |         this.mtSettings.txtMaxLines | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     if ( | ||||||
|  |       this.mtSettings.previewMaxLines !== "0" && | ||||||
|  |       this.mtSettings.previewMaxLines.length !== 0 | ||||||
|  |     ) { | ||||||
|  |       this.mtBodyNode.parentNode.style.setProperty( | ||||||
|  |         "--mt-preview-max-lines", | ||||||
|  |         this.mtSettings.previewMaxLines | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Add the attribute Aria-setsize to all posts | ||||||
|  |    * @param {number} n The total number of posts showed in the timeline | ||||||
|  |    */ | ||||||
|  |   #addAriaSetsize(n) { | ||||||
|  |     const articles = this.mtBodyNode.getElementsByTagName("article"); | ||||||
|  | 
 | ||||||
|  |     for (let i = 0; i < n; i++) { | ||||||
|  |       articles[i].setAttribute("aria-setsize", n); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -511,16 +633,11 @@ export class Init { | |||||||
|       </div>`; |       </div>`; | ||||||
| 
 | 
 | ||||||
|     // Main text
 |     // Main text
 | ||||||
|     let txtCss = ""; |  | ||||||
|     if (this.mtSettings.txtMaxLines !== "0") { |  | ||||||
|       txtCss = " truncate"; |  | ||||||
|       this.mtBodyNode.parentNode.style.setProperty( |  | ||||||
|         "--mt-txt-max-lines", |  | ||||||
|         this.mtSettings.txtMaxLines |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let content = ""; |     let content = ""; | ||||||
|  |     if (this.mtSettings.txtMaxLines !== "0") { | ||||||
|  |       const txtCss = | ||||||
|  |         this.mtSettings.txtMaxLines.length !== 0 ? " truncate" : ""; | ||||||
|  | 
 | ||||||
|       if (c.spoiler_text !== "") { |       if (c.spoiler_text !== "") { | ||||||
|         content = |         content = | ||||||
|           '<div class="mt-post-txt">' + |           '<div class="mt-post-txt">' + | ||||||
| @ -570,6 +687,7 @@ export class Init { | |||||||
|           "</div>" + |           "</div>" + | ||||||
|           "</div>"; |           "</div>"; | ||||||
|       } |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Media attachments
 |     // Media attachments
 | ||||||
|     let media = []; |     let media = []; | ||||||
| @ -643,8 +761,6 @@ export class Init { | |||||||
|     const post = |     const post = | ||||||
|       '<article class="mt-post" aria-posinset="' + |       '<article class="mt-post" aria-posinset="' + | ||||||
|       (i + 1) + |       (i + 1) + | ||||||
|       '" aria-setsize="' + |  | ||||||
|       this.mtSettings.maxNbPostFetch + |  | ||||||
|       '" data-location="' + |       '" data-location="' + | ||||||
|       url + |       url + | ||||||
|       '" tabindex="0">' + |       '" tabindex="0">' + | ||||||
| @ -795,8 +911,8 @@ export class Init { | |||||||
|    * @param {boolean} s Sensitive/spoiler status |    * @param {boolean} s Sensitive/spoiler status | ||||||
|    * @returns {string} Media in HTML format |    * @returns {string} Media in HTML format | ||||||
|    */ |    */ | ||||||
|   #createMedia(m, s) { |   #createMedia(m, s = false) { | ||||||
|     const spoiler = s || false; |     const spoiler = s; | ||||||
|     const type = m.type; |     const type = m.type; | ||||||
|     let media = ""; |     let media = ""; | ||||||
| 
 | 
 | ||||||
| @ -806,7 +922,7 @@ export class Init { | |||||||
|         (spoiler ? "mt-post-media-spoiler " : "") + |         (spoiler ? "mt-post-media-spoiler " : "") + | ||||||
|         this.mtSettings.spinnerClass + |         this.mtSettings.spinnerClass + | ||||||
|         '" data-media-type="' + |         '" data-media-type="' + | ||||||
|         m.type + |         type + | ||||||
|         '" data-media-url-hd="' + |         '" data-media-url-hd="' + | ||||||
|         m.url + |         m.url + | ||||||
|         '" data-media-alt-txt="' + |         '" data-media-alt-txt="' + | ||||||
| @ -838,7 +954,7 @@ export class Init { | |||||||
|           (spoiler ? "mt-post-media-spoiler " : "") + |           (spoiler ? "mt-post-media-spoiler " : "") + | ||||||
|           this.mtSettings.spinnerClass + |           this.mtSettings.spinnerClass + | ||||||
|           '" data-media-type="' + |           '" data-media-type="' + | ||||||
|           m.type + |           type + | ||||||
|           '" data-media-url-hd="' + |           '" data-media-url-hd="' + | ||||||
|           m.preview_url + |           m.preview_url + | ||||||
|           '" data-media-alt-txt="' + |           '" data-media-alt-txt="' + | ||||||
| @ -869,7 +985,7 @@ export class Init { | |||||||
|           '<div class="mt-post-media ' + |           '<div class="mt-post-media ' + | ||||||
|           (spoiler ? "mt-post-media-spoiler " : "") + |           (spoiler ? "mt-post-media-spoiler " : "") + | ||||||
|           '" data-media-type="' + |           '" data-media-type="' + | ||||||
|           m.type + |           type + | ||||||
|           '">' + |           '">' + | ||||||
|           (spoiler |           (spoiler | ||||||
|             ? '<button class="mt-btn-dark mt-btn-spoiler">' + |             ? '<button class="mt-btn-dark mt-btn-spoiler">' + | ||||||
| @ -890,7 +1006,7 @@ export class Init { | |||||||
|           (spoiler ? "mt-post-media-spoiler " : "") + |           (spoiler ? "mt-post-media-spoiler " : "") + | ||||||
|           this.mtSettings.spinnerClass + |           this.mtSettings.spinnerClass + | ||||||
|           '" data-media-type="' + |           '" data-media-type="' + | ||||||
|           m.type + |           type + | ||||||
|           '" data-media-url-hd="' + |           '" data-media-url-hd="' + | ||||||
|           m.url + |           m.url + | ||||||
|           '" data-media-alt-txt="' + |           '" data-media-alt-txt="' + | ||||||
| @ -919,7 +1035,7 @@ export class Init { | |||||||
|           '<div class="mt-post-media ' + |           '<div class="mt-post-media ' + | ||||||
|           (spoiler ? "mt-post-media-spoiler " : "") + |           (spoiler ? "mt-post-media-spoiler " : "") + | ||||||
|           '" data-media-type="' + |           '" data-media-type="' + | ||||||
|           m.type + |           type + | ||||||
|           '" data-media-url-hd="' + |           '" data-media-url-hd="' + | ||||||
|           m.url + |           m.url + | ||||||
|           '" data-media-alt-txt="' + |           '" data-media-alt-txt="' + | ||||||
| @ -968,7 +1084,7 @@ export class Init { | |||||||
|    * Build a carousel/lightbox with the media content in the post clicked |    * Build a carousel/lightbox with the media content in the post clicked | ||||||
|    * @param {event} e User interaction trigger |    * @param {event} e User interaction trigger | ||||||
|    */ |    */ | ||||||
|   #buildCarousel(e) { |   #showCarousel(e) { | ||||||
|     // List all medias in the post and remove sensitive/spoiler medias
 |     // List all medias in the post and remove sensitive/spoiler medias
 | ||||||
|     const mediaSiblings = Array.from( |     const mediaSiblings = Array.from( | ||||||
|       e.target.parentNode.parentNode.children |       e.target.parentNode.parentNode.children | ||||||
| @ -1214,6 +1330,19 @@ export class Init { | |||||||
|    * @returns {string} Preview link in HTML format |    * @returns {string} Preview link in HTML format | ||||||
|    */ |    */ | ||||||
|   #createPreviewLink(c) { |   #createPreviewLink(c) { | ||||||
|  |     let previewDescription = ""; | ||||||
|  |     if (this.mtSettings.previewMaxLines !== "0" && c.description) { | ||||||
|  |       const txtCss = | ||||||
|  |         this.mtSettings.previewMaxLines.length !== 0 ? " truncate" : ""; | ||||||
|  | 
 | ||||||
|  |       previewDescription = | ||||||
|  |         '<span class="mt-post-preview-description' + | ||||||
|  |         txtCss + | ||||||
|  |         '">' + | ||||||
|  |         this.#parseHTMLstring(c.description) + | ||||||
|  |         "</span>"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const card = |     const card = | ||||||
|       '<a href="' + |       '<a href="' + | ||||||
|       c.url + |       c.url + | ||||||
| @ -1237,6 +1366,7 @@ export class Init { | |||||||
|       '<span class="mt-post-preview-title">' + |       '<span class="mt-post-preview-title">' + | ||||||
|       c.title + |       c.title + | ||||||
|       "</span>" + |       "</span>" + | ||||||
|  |       previewDescription + | ||||||
|       (c.author_name |       (c.author_name | ||||||
|         ? '<span class="mt-post-preview-author">' + |         ? '<span class="mt-post-preview-author">' + | ||||||
|           this.#parseHTMLstring(c.author_name) + |           this.#parseHTMLstring(c.author_name) + | ||||||
| @ -1263,15 +1393,8 @@ export class Init { | |||||||
|    * Build footer after last post |    * Build footer after last post | ||||||
|    */ |    */ | ||||||
|   #buildFooter() { |   #buildFooter() { | ||||||
|     if (this.mtSettings.btnSeeMore || this.mtSettings.btnReload) { |     let btnSeeMoreHTML = ""; | ||||||
|       // Add footer container
 |     let btnReloadHTML = ""; | ||||||
|       this.mtBodyNode.parentNode.insertAdjacentHTML( |  | ||||||
|         "beforeend", |  | ||||||
|         '<div class="mt-footer"></div>' |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       const containerFooter = |  | ||||||
|         this.mtContainerNode.getElementsByClassName("mt-footer")[0]; |  | ||||||
| 
 | 
 | ||||||
|     // Create button to open Mastodon page
 |     // Create button to open Mastodon page
 | ||||||
|     if (this.mtSettings.btnSeeMore) { |     if (this.mtSettings.btnSeeMore) { | ||||||
| @ -1290,7 +1413,7 @@ export class Init { | |||||||
|       } else if (this.mtSettings.timelineType === "local") { |       } else if (this.mtSettings.timelineType === "local") { | ||||||
|         btnSeeMorePath = "public/local"; |         btnSeeMorePath = "public/local"; | ||||||
|       } |       } | ||||||
|         const btnSeeMoreHTML = ` |       btnSeeMoreHTML = ` | ||||||
|           <a class="mt-btn-violet btn-see-more" href="${ |           <a class="mt-btn-violet btn-see-more" href="${ | ||||||
|             this.mtSettings.instanceUrl |             this.mtSettings.instanceUrl | ||||||
|           }/${this.#escapeHTML( |           }/${this.#escapeHTML( | ||||||
| @ -1298,21 +1421,24 @@ export class Init { | |||||||
|       )}" rel="nofollow noopener noreferrer" target="_blank"> |       )}" rel="nofollow noopener noreferrer" target="_blank"> | ||||||
|             ${this.mtSettings.btnSeeMore} |             ${this.mtSettings.btnSeeMore} | ||||||
|           </a>`; |           </a>`; | ||||||
| 
 |  | ||||||
|         containerFooter.insertAdjacentHTML("beforeend", btnSeeMoreHTML); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Create button to refresh the timeline
 |     // Create button to refresh the timeline
 | ||||||
|     if (this.mtSettings.btnReload) { |     if (this.mtSettings.btnReload) { | ||||||
|         const btnReloadHTML = ` |       btnReloadHTML = ` | ||||||
|           <button class="mt-btn-violet btn-refresh"> |           <button class="mt-btn-violet btn-refresh"> | ||||||
|             <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M21 3v5m0 0h-5m5 0l-3-2.708C16.408 3.867 14.305 3 12 3a9 9 0 1 0 0 18c4.283 0 7.868-2.992 8.777-7" stroke="var(--mt-color-btn-txt)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> |             <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M21 3v5m0 0h-5m5 0l-3-2.708C16.408 3.867 14.305 3 12 3a9 9 0 1 0 0 18c4.283 0 7.868-2.992 8.777-7" stroke="var(--mt-color-btn-txt)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | ||||||
|             </svg> |             </svg> | ||||||
|               ${this.mtSettings.btnReload} |               ${this.mtSettings.btnReload} | ||||||
|           </button>`; |           </button>`; | ||||||
| 
 | 
 | ||||||
|         containerFooter.insertAdjacentHTML("beforeend", btnReloadHTML); |       // Add footer container
 | ||||||
|  |       this.mtBodyNode.parentNode.insertAdjacentHTML( | ||||||
|  |         "beforeend", | ||||||
|  |         '<div class="mt-footer">' + btnSeeMoreHTML + btnReloadHTML + "</div>" | ||||||
|  |       ); | ||||||
| 
 | 
 | ||||||
|  |       // Add event listener to the button "Refresh"
 | ||||||
|       const reloadBtn = |       const reloadBtn = | ||||||
|         this.mtContainerNode.getElementsByClassName("btn-refresh")[0]; |         this.mtContainerNode.getElementsByClassName("btn-refresh")[0]; | ||||||
|       reloadBtn.addEventListener("click", () => { |       reloadBtn.addEventListener("click", () => { | ||||||
| @ -1320,12 +1446,11 @@ export class Init { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Add EventListeners for timeline interactions and trigger functions |    * Add EventListeners for timeline interactions and trigger functions | ||||||
|    */ |    */ | ||||||
|   #setPostsInteracion() { |   #addPostListener() { | ||||||
|     this.mtBodyNode.addEventListener("click", (e) => { |     this.mtBodyNode.addEventListener("click", (e) => { | ||||||
|       const target = e.target; |       const target = e.target; | ||||||
|       const localName = target.localName; |       const localName = target.localName; | ||||||
| @ -1335,10 +1460,8 @@ export class Init { | |||||||
|       if ( |       if ( | ||||||
|         localName == "article" || |         localName == "article" || | ||||||
|         target.offsetParent?.localName == "article" || |         target.offsetParent?.localName == "article" || | ||||||
|         (localName == "img" && |         (this.mtSettings.disableCarousel && | ||||||
|           this.mtSettings.disableCarousel && |           parentNode.getAttribute("data-media-type") === "image") | ||||||
|           parentNode.getAttribute("data-media-type") !== "video" && |  | ||||||
|           parentNode.getAttribute("data-media-type") !== "gifv") |  | ||||||
|       ) { |       ) { | ||||||
|         this.#openPostUrl(e); |         this.#openPostUrl(e); | ||||||
|       } |       } | ||||||
| @ -1355,11 +1478,10 @@ export class Init { | |||||||
|       if ( |       if ( | ||||||
|         !this.mtSettings.disableCarousel && |         !this.mtSettings.disableCarousel && | ||||||
|         localName == "img" && |         localName == "img" && | ||||||
|         !parentNode.classList.contains("mt-post-preview-image") && |         (parentNode.getAttribute("data-media-type") === "image" || | ||||||
|         parentNode.getAttribute("data-media-type") !== "video" && |           parentNode.getAttribute("data-media-type") === "audio") | ||||||
|         parentNode.getAttribute("data-media-type") !== "gifv" |  | ||||||
|       ) { |       ) { | ||||||
|         this.#buildCarousel(e); |         this.#showCarousel(e); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       // Check if video preview image or play icon/button was clicked
 |       // Check if video preview image or play icon/button was clicked
 | ||||||
| @ -1400,6 +1522,7 @@ export class Init { | |||||||
|       e.target.className !== "mt-post-preview-noImage" && |       e.target.className !== "mt-post-preview-noImage" && | ||||||
|       e.target.parentNode.className !== "mt-post-avatar-image-big" && |       e.target.parentNode.className !== "mt-post-avatar-image-big" && | ||||||
|       e.target.parentNode.className !== "mt-post-avatar-image-small" && |       e.target.parentNode.className !== "mt-post-avatar-image-small" && | ||||||
|  |       e.target.parentNode.className !== "mt-post-header-user-name" && | ||||||
|       e.target.parentNode.className !== "mt-post-preview-image" && |       e.target.parentNode.className !== "mt-post-preview-image" && | ||||||
|       e.target.parentNode.className !== "mt-post-preview" && |       e.target.parentNode.className !== "mt-post-preview" && | ||||||
|       urlPost |       urlPost | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 i.j
						i.j