1/** 2 * Youth Story Tag Cloud Class - modified version of global Tag Cloud 3 * 4 * @class YouthStoriesTagCloud 5 */ 6 7import { showVisually, hideVisually } from "../helpers.js"; 8 9export default class YouthStoriesTagCloud { 10 constructor() { 11 this.selectors = { 12 storiesWrapper: ".js-ys-tag-cloud-stories-wrapper", 13 tagButton: ".js-tag-cloud-button", 14 loadMoreButton: ".js-ys-tag-cloud-load-more", 15 resetButton: ".js-ys-tag-cloud-reset", 16 noPostsInfo: ".js-ys-tag-cloud-no-results", 17 error: ".js-ys-tag-cloud-error", 18 singleArticlePreview: ".single-article-preview__title", 19 }; 20 this.loadingClass = "ys-tag-cloud__stories-wrapper--is-loading"; 21 this.activeTagClass = "tag-cloud__button--active"; 22 this.nextPage = 2; 23 } 24 25 /** 26 * Toggle active class on button 27 */ 28 toggleActive(button, className = "is-active") { 29 if (button) { 30 button.classList.toggle(className); 31 } else { 32 console.error("Passing button is required."); 33 } 34 } 35 36 /** 37 * Function that triggers on button click 38 * @param {*} button 39 */ 40 tagButtonClick(button) { 41 if (button) { 42 this.toggleActive(button, this.activeTagClass); 43 this.nextPage = 1; 44 const pageToFetch = this.getPageToFetch();
45 this.getStories(pageToFetch, true, true); 46 } else { 47 console.error("Passing button is required."); 48 } 49 } 50 51 /** 52 * Method to get array of selected tag cloud items 53 * @returns {array} 54 */ 55 getSelectedTags() { 56 return document.querySelectorAll(".tag-cloud__button--active"); 57 } 58 59 /** 60 * Method to get array of selected tags' ids 61 * @returns {array} 62 */ 63 getSelectedTagIds() { 64 return [...this.getSelectedTags()].map(tag => tag.dataset.tagId); 65 } 66 67 /** 68 * Method that check if there are any tags selected which is used to determine whether fetch recent posts or not 69 * @returns {boolean} 70 */ 71 getRecentPosts() { 72 return this.getSelectedTags().length === 0; 73 } 74 75 /** 76 * Function that crate object which is passed as axios.get parameter 77 * @returns {object} 78 */ 79 getParamsObject(pageToFetch) { 80 const params = {}; 81 82 if (!this.getRecentPosts()) { 83 params.terms = this.getSelectedTagIds().join(","); 84 params.page = pageToFetch; 85 } else { 86 params.page = pageToFetch ? pageToFetch : 1; 87 params.terms = []; 88 } 89 90 return params; 91 } 92 93 /** 94 * Method to reinitialize lazy load library, need to run it on every HTML change 95 * @returns {void} 96 */ 97 getLazyLoadInstance() { 98 // eslint-disable-next-line no-undef 99 return new LazyLoad({ 100 elements_selector: ".cta-box-holder" 101 }); 102 } 103 104 /** 105 * Determines which page should be fetched inside WP_Query 106 * @returns {number} 107 */ 108 getPageToFetch() { 109 return this.nextPage; 110 } 111 112 /** 113 * Toggle 'no results found' information when there are no posts returned from the query 114 */ 115 noPostsVisibility() {
116 const loadMoreButton = document.querySelector(this.selectors.loadMoreButton); 117 const noPostsInfo = document.querySelector(this.selectors.noPostsInfo); 118 119 if (loadMoreButton && noPostsInfo) { 120 if (loadMoreButton.dataset.foundPosts == 0) { 121 showVisually(noPostsInfo); 122 } else { 123 hideVisually(noPostsInfo); 124 } 125 } 126 } 127 128 /** 129 * Hide show more button when no results to display left or show it (keep visible) when there are still articles to show 130 */ 131 loadMoreVisibility() { 132 const storiesWrapper = document.querySelector(this.selectors.storiesWrapper); 133 const loadMoreButton = document.querySelector(this.selectors.loadMoreButton); 134 135 if (storiesWrapper) { 136 if (loadMoreButton.dataset.foundPosts > storiesWrapper.childElementCount) { 137 showVisually(loadMoreButton); 138 } else { 139 hideVisually(loadMoreButton); 140 } 141 } 142 } 143 144 /** 145 * Check if the error wrapper on the results page is visible 146 * @return {boolean} Is error visible 147 */ 148 isErrorVisible() { 149 const errorWrapper = document.querySelector(this.selectors.error); 150 return !errorWrapper.classList.contains("visually-hidden"); 151 } 152 153 /** 154 * Hide or show error wrapper on the results page 155 * @returns {void} 156 */ 157 toggleErrorVisibility() { 158 const errorWrapper = document.querySelector(this.selectors.error); 159 this.isErrorVisible() ? hideVisually(errorWrapper) : showVisually(errorWrapper); 160 } 161 162 /** 163 * A function triggered on the "Load More" button click event which get page that need to be fetched and run stories fetch 164 * @returns {void} 165 */ 166 loadMoreStories() {
167 const loadMoreButton = document.querySelector(this.selectors.loadMoreButton); 168 const storiesWrapper = document.querySelector(this.selectors.storiesWrapper); 169 170 if (loadMoreButton && storiesWrapper) { 171 loadMoreButton.addEventListener("click", () => { 172 const pageToFetch = this.getPageToFetch(); 173 174 if (pageToFetch > 1) { 175 const emptyWrapper = false; 176 this.getStories(pageToFetch, emptyWrapper); 177 } 178 }); 179 } 180 } 181 182 /** 183 * Fetch Youth Stories post type with given paramters. 184 * Update the UI 185 * @param {number} page Which page should be fetched, default 1 186 * @param {boolean} emptyWrapper Should the stories wrapper be cleared - useful when new tag is selected 187 */ 188 189 getStories(page = 1, emptyWrapper = true, forceFirstPage = false) { 190 const fetchParams = this.getParamsObject(page); 191 const loadMoreButton = document.querySelector(this.selectors.loadMoreButton); 192 193 if (fetchParams) { 194 const storiesWrapper = document.querySelector(this.selectors.storiesWrapper); 195 storiesWrapper.classList.add(this.loadingClass); 196 // eslint-disable-next-line no-undef 197 axios.get("/wp-json/khp/youth-stories/posts", { 198 params: fetchParams 199 }) 200 .then((response) => { 201 if (response.status === 200) { 202 loadMoreButton.dataset.foundPosts = response.data.found_posts; 203 loadMoreButton.dataset.maxNumPages = response.data.max_num_pages; 204 205 if (this.nextPage < loadMoreButton.dataset.maxNumPages) { 206 this.nextPage++; 207 } 208 209 if (forceFirstPage) { 210 this.nextPage = 2; 211 } 212 213 if (emptyWrapper) { 214 storiesWrapper.innerHTML = ""; 215 } 216 217 if (response.data.post_count !== 0) { 218 storiesWrapper.insertAdjacentHTML("beforeend", response.data.html); 219 220 221 const existingPostCount = storiesWrapper.children.length; 222 223 224 setTimeout(() => { 225 // Re-run lazyload instance to make images lazy-loaded 226 this.getLazyLoadInstance().update(); 227 228 // Identify newly added posts 229 const newPosts = Array.from(storiesWrapper.children).slice(-3); 230 231 // Select the title tag of the first post of the newly added 232 const newFirstPost = newPosts[0].querySelector(this.selectors.singleArticlePreview); 233 234 // Focus the first of the newly added posts 235 if(newFirstPost) { 236 newFirstPost.focus(); 237 } 238 239 }, 20); 240 } 241 242 storiesWrapper.classList.remove(this.loadingClass); 243 244 this.loadMoreVisibility(); 245 this.noPostsVisibility(); 246 } 247 }) 248 // Catch and display errors if any 249 .catch((error) => { 250 console.dir(error); 251 this.toggleErrorVisibility(); 252 storiesWrapper.classList.remove(this.loadingClass); 253 }); 254 } else { 255 console.error("Missing form data object."); 256 } 257 } 258 259 /** 260 * Assign function that runs on a tag cloud item click event. 261 * @returns {void} 262 */ 263 eventHandlers() { 264 const tagButtons = document.querySelectorAll(this.selectors.tagButton); 265 266 if (tagButtons) { 267 tagButtons.forEach(button => { 268 button.addEventListener("click", (e) => { 269 e.preventDefault(); 270 this.tagButtonClick(e.target); 271 }); 272 }); 273 } 274 } 275 276 /** 277 * Reset tags button click event listener that remove 'active' class from all of the selected tag and update the URL query string. 278 * @returns {void} 279 */ 280 resetTags() { 281 const resetButton = document.querySelector(this.selectors.resetButton); 282 const tagButtons = Array.from(document.querySelectorAll(".tag-cloud__button")); 283 284 if (resetButton) { 285 resetButton.addEventListener("click", (e) => { 286 e.preventDefault(); 287 const selectedTags = document.querySelectorAll(".tag-cloud__button--active"); 288 289 if (selectedTags.length > 0) { 290 tagButtons.forEach(button => { 291 if (button.classList.contains("tag-cloud__button--active")) { 292 button.classList.remove("tag-cloud__button--active"); 293 } 294 }); 295 296 // Run dynamic URL update only on the search page. 297 this.nextPage = 1; 298 const pageToFetch = this.getPageToFetch();
299 this.getStories(pageToFetch, true); 300 } 301 }); 302 } 303 } 304 305 /** 306 * Initialize class functions. 307 * @returns {void} 308 */ 309 init() { 310 this.eventHandlers(); 311 this.resetTags(); 312 this.loadMoreStories(); 313 this.loadMoreVisibility(); 314 } 315}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.