PageSourceSearch

https://jeunessejecoute.ca/wp-content/themes/understrap/js/youth-stories/TagCloud.js

js jeunessejecoute.ca collected 2026-10-01 13:57:33 UTC 10,682 bytes, 315 lines download raw bytes

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.