PageSourceSearch

https://cafedelrio.net/js/blog.js

js cafedelrio.net collected 2026-10-04 10:44:14 UTC 16,003 bytes, 455 lines download raw bytes

1import { determineProto } from './protoDeterminer.js';
2var proto = determineProto();
3
4const blogManager = {
5	config: {
6		postsPerPage: 12,
7		blogPostsEndpoint: `https://rails-admin.spillover.com/esites-api/v1/${proto}/site_posts?number_of_posts=1000`,
8		fetchedPostsLog: "Fetched blog posts:",
9		errorLoadingLog: "Error loading blog posts:",
10		postNotFoundLog: "Post not found.",
11		urlCopiedLog: "URL copied to clipboard:",
12		blogLinkNotification: "Blog Link Copied to Clipboard!",
13		shareHover: "Share blog to Facebook",
14		copyHover: "Copy blog link to clipboard",
15	},
16	blogPosts: [],
17	selectedTags: [],
18
19	async fetchBlogPosts() {
20		try {
21			const response = await fetch(this.config.blogPostsEndpoint);
22			if (!response.ok) throw new Error("Network response was not ok");
23			this.blogPosts = await response.json();
24			console.log(this.config.fetchedPostsLog, this.blogPosts);
25		} catch (error) {
26			console.error(this.config.errorLoadingLog, error);
27		}
28	},
29
30	getAllTags() {
31		const allTags = this.blogPosts.flatMap((post) => post.tags); // using this
32		return [...new Set(allTags)];
33	},
34
35	getTagsFromURL() {
36		const urlParams = new URLSearchParams(window.location.search);
37		const tagsParam = urlParams.get("tags");
38		return tagsParam ? tagsParam.split(",") : [];
39	},
40
41	updateCheckboxesBasedOnURLTags(tags) {
42		tags.forEach((tag) => {
43			const checkboxId = `tag${tag}Checkbox`;
44			const checkbox = document.getElementById(checkboxId);
45			if (checkbox) {
46				checkbox.checked = true;
47			}
48		});
49	},
50
51	renderBlogPosts(posts) {
52		const blogPostsContainer = document.getElementById("blogPostsContainer");
53		blogPostsContainer.innerHTML = posts.map(this.generateBlogPostCard).join("");
54	},
55
56	generateTagChecklist() {
57		const tagChecklist = document.getElementById("tagChecklist");
58		const uniqueTags = this.getAllTags().sort();
59
60		if (uniqueTags.length === 0) {
61			$('#blogTags').hide();
62			return;
63		}
64
65		tagChecklist ? tagChecklist.innerHTML = "" : null;
66
67		uniqueTags.forEach((tag) => {
68			const checkboxId = `tag${tag}Checkbox`;
69
70			const checkboxHtml = `
71            <input
72                type="checkbox"
73                class="form-check-input tag-checkbox"
74                value="${tag}"
75                id="${checkboxId}"
76                onchange="blogManager.filterBlogs()"
77            >
78        `;
79
80			const labelHtml = `
81            <label
82                class="form-check-label"
83                for="${checkboxId}"
84            >${tag}</label>
85        `;
86
87			const div = document.createElement("div");
88			div.className = "form-check";
89			div.innerHTML = checkboxHtml + labelHtml;
90			tagChecklist ? tagChecklist.appendChild(div) : null;
91		});
92	},
93
94	createSharePostLink(postID) {
95		const post = this.blogPosts.find((post) => post.id === postID);
96
97		if (!post) {
98			console.error("Post not found.");
99			return;
100		}
101
102		const encodedTitle = encodeURIComponent(post.headline);
103		const encodedUrl = encodeURIComponent(window.location.href.split("?")[0]);
104
105		const shareLink = `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}?post=${encodedTitle}`;
106		window.open(`${shareLink}`, "_blank");
107	},
108
109	copyBlogLinkToClipboard(postID) {
110		const post = this.blogPosts.find((post) => post.id === postID);
111		const encodedUrl = window.location.href.split("?")[0];
112		const completeUrl = `${encodedUrl}?post=${post.headline}`;
113
114		navigator.clipboard
115			.writeText(completeUrl)
116			.then(() => {
117				console.log(this.config.urlCopiedLog, completeUrl);
118
119				// Show copy success notification
120				this.showNotification(this.config.blogLinkNotification);
121			})
122			.catch((error) => {
123				console.error("Error copying URL to clipboard:", error);
124			});
125	},
126
127	showNotification(message) {
128		const createNotification = () => {
129			const notification = document.createElement("div");
130			notification.className = "alert alert-success alert-dismissible fade show";
131			notification.setAttribute("role", "alert");
132			notification.innerHTML = `
133            ${message}
134            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
135        `;
136			return notification;
137		};
138
139		const notificationContainer = document.getElementById("notificationContainer");
140		const modalNotificationContainer = document.getElementById("modalNotificationContainer");
141
142		const notification = createNotification();
143
144		if (notificationContainer) {
145			notificationContainer.appendChild(notification);
146			setTimeout(() => {
147				notificationContainer.removeChild(notification);
148			}, 3000); // Remove the notification after 3 seconds
149		}
150
151		if (modalNotificationContainer) {
152			const modalNotification = createNotification();
153			modalNotificationContainer.appendChild(modalNotification);
154			setTimeout(() => {
155				modalNotificationContainer.removeChild(modalNotification);
156			}, 3000); // Remove the notification after 3 seconds
157		}
158	},
159
160	generateBlogPostCard(post) {
161		const encodedTitle = encodeURIComponent(post.headline);
162		const shareHover = this.config.shareHover;
163		const copyHover = this.config.copyHover;
164		const sharePostLink = `blogManager.createSharePostLink(${post.id})`;
165		const copyBlogLink = `blogManager.copyBlogLinkToClipboard(${post.id})`;
166		const tagsHtml = post.tags.map((tag) => `<span class="blog-tag" onclick="blogManager.filterByTag('${tag}')">${tag}</span>`).join(", ");
167
168		return `
169        <div class="col-lg-3 col-md-6 col-12" tabindex="0">
170            <div class="uk-panel">
171                <img src="${post.post_image}" class="blog-img" width="100%" height="100%" alt="${post.headline}">
172				<div class="blog-top">
173                    <p class="blog-tags pointer">${tagsHtml}</p>
174                </div>
175				<div class="blog-bottom">
176                    <a href="?post=${encodedTitle}" class="title-wrapper"><p class="blog-title pointer truncate">${post.headline}</p></a>
177                    <a href="?post=${encodedTitle}" class="badge badge-template blog-button">Read More</a>
178                    <i class="fas fa-share ms-2 text-white pointer" data-bs-toggle="tooltip" data-bs-placement="bottom" title="${shareHover}" onclick="${sharePostLink}"></i>
179                    <i class="fas fa-copy ms-2 text-white pointer" data-bs-toggle="tooltip" data-bs-placement="bottom" title="${copyHover}" onclick="${copyBlogLink}"></i>
180                </div>
181            </div>
182        </div>
183    `;
184	},
185
186	showFullArticle(postTitle) {
187		const shareHover = this.config.shareHover;
188		const copyHover = this.config.copyHover;
189		const post = this.blogPosts.find((post) => post.headline === postTitle);
190		const fullArticleContainer = document.getElementById("fullArticleContent");
191		const encodedTitle = encodeURIComponent(postTitle);
192
193		if (!post) {
194			console.error("Post not found.");
195			return;
196		}
197
198		const sharePostLink = `blogManager.createSharePostLink(${post.id})`;
199		const copyBlogLink = `blogManager.copyBlogLinkToClipboard(${post.id})`;
200
201		fullArticleContainer.innerHTML = `
202			<div class="d-flex justify-content-end mt-3 mb-4">
203				<a class="fas fa-share ms-3 pointer fa-2xl" data-bs-toggle="tooltip" data-bs-placement="bottom" title="${shareHover}" onclick="${sharePostLink}"></a>
204				<a class="fas fa-copy ms-3 pointer fa-2xl" data-bs-toggle="tooltip" data-bs-placement="bottom" title="${copyHover}" onclick="${copyBlogLink}"></a>
205			</div>
206			<div class="row">
207				<div class="col-md-8 offset-md-2">
208					<div class="d-flex justify-content-center mb-3">
209						<img src="${post.post_image}" class="img-fluid" alt="${post.headline}" />
210					</div>
211					<div>${post.caption}</div>
212					${post.referral_link ? `
213						<div class="article-button">
214							<a href="${post.referral_link}" target="_blank" class="btn btn-template">See More</a>
215						</div>
216					` : ''}
217				</div>
218			</div>
219		`;
220
221		const modalTitle = document.getElementById("fullArticleModalLabel");
222		modalTitle.textContent = post.headline;
223
224		const fullArticleModal = new bootstrap.Modal(document.getElementById("fullArticleModal"));
225		fullArticleModal.show();
226	},
227
228	calculateTotalPages(filteredPosts) {
229		// Inside the calculateTotalPages function
230		const totalPages = Math.ceil(filteredPosts.length / this.config.postsPerPage);
231		console.log("Total Pages:", totalPages);
232
233		return Math.ceil(filteredPosts.length / this.config.postsPerPage);
234	},
235
236	filterBlogs() {
237		const selectedCheckboxes = document.querySelectorAll(".tag-checkbox:checked");
238		this.selectedTags = Array.from(selectedCheckboxes).map((checkbox) => checkbox.value);
239
240		let filteredPosts = [];
241		if (this.selectedTags.length === 0) {
242			filteredPosts = this.blogPosts;
243		} else {
244			filteredPosts = this.blogPosts.filter((post) => {
245				return this.selectedTags.some((tag) => post.tags.includes(tag));
246			});
247		}
248
249		const totalPages = this.calculateTotalPages(filteredPosts);
250		this.renderPaginatedBlogPosts(1, filteredPosts);
251		this.renderPagination(totalPages, 1);
252		this.updateRemoveFiltersButtonVisibility();
253		
254		// Inside the filterBlogs function
255		console.log("Selected Tags:", this.selectedTags);
256		console.log("Filtered Posts:", filteredPosts);
257	},
258	
259	filterByTag(tag) {
260		const checkboxId = `tag${tag}Checkbox`;
261		const checkbox = document.getElementById(checkboxId);
262
263		if (checkbox) {
264			checkbox.checked = !checkbox.checked;
265		}
266
267		if (!this.selectedTags.includes(tag)) {
268			this.selectedTags.push(tag);
269		} else {
270			const index = this.selectedTags.indexOf(tag);
271			if (index > -1) {
272				this.selectedTags.splice(index, 1);
273			}
274		}
275		this.filterBlogs();
276	},
277
278	updateRemoveFiltersButtonVisibility() {
279		const button = document.getElementById("removeFiltersButton");
280		if (this.selectedTags.length > 0) {
281			button.style.display = "inline-block";
282			setTimeout(() => {
283				// Using a timeout ensures the CSS transition takes effect after the display property change
284				button.classList.add("visible");
285			}, 50);
286		} else {
287			button.classList.remove("visible");
288			// After the fade-out transition completes, hide the button
289			setTimeout(() => {
290				if (!button.classList.contains("visible")) {
291					// Ensure the button shouldn't be visible when the timeout completes
292					button.style.display = "none";
293				}
294			}, 300); // This timeout duration should match the CSS transition duration
295		}
296	},
297
298	removeFilters() {
299		this.selectedTags = [];
300		const checkboxes = document.querySelectorAll(".tag-checkbox");
301		checkboxes.forEach((checkbox) => {
302			checkbox.checked = false;
303		});
304		this.filterBlogs();
305	},
306
307	renderPaginatedBlogPosts(pageNumber, filteredPosts) {
308		// Before calling renderPaginatedBlogPosts
309		console.log("Page Number before renderPaginatedBlogPosts:", pageNumber);
310		console.log("Filtered Posts before renderPaginatedBlogPosts:", filteredPosts);
311
312		const startIndex = (pageNumber - 1) * this.config.postsPerPage;
313		const endIndex = startIndex + this.config.postsPerPage;
314		const paginatedPosts = filteredPosts.slice(startIndex, endIndex);
315		this.renderBlogPosts(paginatedPosts);
316
317		// Inside the renderPaginatedBlogPosts function
318		console.log("Page Number in renderPaginatedBlogPosts:", pageNumber);
319		console.log("Paginated Posts:", paginatedPosts);
320	},
321
322	renderPagination(totalPages, currentPage) {
323        const paginationContainer = document.getElementById("pagination");
324
325        let paginationHTML = `
326            <ul class="pagination justify-content-center">
327                <li class="page-item ${currentPage === 1 ? "disabled" : ""}">
328                    <button class="page-link page-button" data-page="${currentPage - 1}" ${currentPage === 1 ? "disabled" : ""}>Previous</button>
329                </li>
330        `;
331
332        for (let i = 1; i <= totalPages; i++) {
333            paginationHTML += `
334                <li class="page-item ${currentPage === i ? "active" : ""}">
335                    <button class="page-link page-button" data-page="${i}">${i}</button>
336                </li>
337            `;
338        }
339
340        paginationHTML += `
341                <li class="page-item ${currentPage === totalPages ? "disabled" : ""}">
342                    <button class="page-link page-button" data-page="${currentPage + 1}" ${currentPage === totalPages ? "disabled" : ""}>Next</button>
343                </li>
344            </ul>
345        `;
346
347        paginationContainer.innerHTML = totalPages <= 1 ? `` : paginationHTML;
348
349        const paginationButtons = document.querySelectorAll(".page-button");
350        paginationButtons.forEach((button) => {
351            button.addEventListener("click", (event) => {
352                const pageNumber = parseInt(event.target.dataset.page);
353
354                // Update the paginated blog posts
355                this.filterAndPaginateBlogs(pageNumber);
356            });
357        });
358    },
359
360        // Modify the filterBlogs function to handle both filtering and pagination
361    filterAndPaginateBlogs(pageNumber = 1) {
362        const selectedCheckboxes = document.querySelectorAll(".tag-checkbox:checked");
363        this.selectedTags = Array.from(selectedCheckboxes).map((checkbox) => checkbox.value);
364
365        let filteredPosts = [];
366        if (this.selectedTags.length === 0) {
367            filteredPosts = this.blogPosts;
368        } else {
369            filteredPosts = this.blogPosts.filter((post) => {
370                return this.selectedTags.some((tag) => post.tags.includes(tag));
371            });
372        }
373
374        // Calculate the total pages for filtered posts
375        const totalPages = Math.ceil(filteredPosts.length / this.config.postsPerPage);
376
377        // Render the paginated blog posts and update pagination buttons
378        this.renderPaginatedBlogPosts(pageNumber, filteredPosts);
379        this.renderPagination(totalPages, pageNumber); // Update pagination buttons
380		this.setupReadFullArticleButtons();
381		
382		// Scroll back to the top of #blogPostsContainer
383		const blogPostsContainer = document.getElementById("blogPostsContainer");
384		const offset = 250;
385		console.log(offset)
386		const elementPosition = blogPostsContainer.getBoundingClientRect().top + window.scrollY;
387		window.scrollTo({
388			top: elementPosition - offset,
389			behavior: "smooth",
390		});
391    },
392
393    // Add an event listener to the checkboxes for filtering
394    addCheckboxEventListeners() {
395        const tagCheckboxes = document.querySelectorAll(".tag-checkbox");
396        tagCheckboxes.forEach((checkbox) => {
397            checkbox.addEventListener("change", () => {
398                this.filterAndPaginateBlogs(); // Update the filtered and paginated blogs when checkboxes change
399            });
400        });
401    },
402
403	setupReadFullArticleButtons() {
404		const readFullArticleButtons = document.querySelectorAll(".read-full-article");
405		readFullArticleButtons.forEach((button) => {
406			button.addEventListener("click", (event) => {
407				const postId = event.target.dataset.index;
408				this.showFullArticle(postId);
409			});
410		});
411	},
412
413	async initializeBlog() {
414		this.generateBlogPostCard = this.generateBlogPostCard.bind(this);
415        this.addCheckboxEventListeners();
416		await this.fetchBlogPosts();
417		const totalPages = this.calculateTotalPages(this.blogPosts);
418		this.generateTagChecklist();
419		this.renderPaginatedBlogPosts(1, this.blogPosts);
420		this.renderPagination(totalPages, 1);
421		this.setupReadFullArticleButtons();
422
423		// Check for URL parameters
424		const urlParams = new URLSearchParams(window.location.search);
425		const postId = urlParams.get("post");
426		const tags = urlParams.get("tags");
427
428		if (postId) {
429			this.showFullArticle(postId);
430		}
431
432		if (tags) {
433			// Split tags by comma and filter the blogs
434			const tagArray = tags.split(",");
435			tagArray.forEach((tag) => {
436				const checkboxId = `tag${tag}Checkbox`;
437				const checkbox = document.getElementById(checkboxId);
438				if (checkbox) {
439					checkbox.checked = true;
440				}
441			});
442			this.selectedTags = tagArray;
443			this.filterBlogs();
444		}
445
446		var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
447		var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
448			return new bootstrap.Tooltip(tooltipTriggerEl);
449		});
450	},
451};
452
453if ($('#blogPostsContainer').length > 0) {
454	blogManager.initializeBlog();
455}

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.