1/** 2 * @file plugins/themes/mycustom/js/main.js 3 * 4 * @brief Handle JavaScript functionality unique to My Custom Theme, including tab toggling, dark mode toggle, menu toggle, and copy-to-clipboard. 5 */ 6(function($) { 7 8 // Existing code for dropdowns and nav menu... 9 10 // Tab functionality for About, Online First, and Current Issue 11 function switchTab(targetTab) { 12 $('.tab-content').hide(); // Hide all tab content 13 $('#' + targetTab).show(); // Show the selected tab content 14 15 $('.tabs li').removeClass('active'); 16 $('.tabs a[href="#' + targetTab + '"]').parent().addClass('active'); 17 } 18 19 $(document).ready(function() { 20 21 /* ---------------- Tabs: open from hash ONLY (paginator), otherwise default About ---------------- */ 22 const $tabLinks = $('.tabs a[href^="#"]'); 23 24 function setActiveByHref(href) { 25 if (typeof href !== 'string' || href.length < 2 || href.charAt(0) !== '#') return false; 26 const targetTab = href.substring(1); 27 if (!document.getElementById(targetTab)) return false; 28 switchTab(targetTab); 29 return true; 30 } 31 32 function clearHashKeepQuery() { 33 try { 34 if (history && history.replaceState) { 35 history.replaceState(null, '', window.location.pathname + window.location.search); 36 } else { 37 // fallback: best-effort 38 window.location.hash = ''; 39 } 40 } catch (e) {} 41 } 42 43 if ($tabLinks.length) { 44 45 // Click tabs: just switch UI. Do NOT persist across reload. 46 $tabLinks.on('click', function(e) { 47 e.preventDefault(); 48 const href = $(this).attr('href');
49 setActiveByHref(href); 50 51 // If the user landed here via paginator hash, remove it after they interact 52 clearHashKeepQuery(); 53 }); 54 55 // On load: if paginator hash exists, open it ONCE, then clear hash so reload returns to About 56 const openedFromHash = setActiveByHref(window.location.hash); 57 if (openedFromHash) { 58 clearHashKeepQuery(); 59 } else { 60 // Default behavior on any normal load/reload 61 switchTab('about'); 62 } 63 } 64 /* ---------------- end tabs ---------------- */ 65 66 // URL modification for issue links 67 const issueLinks = document.querySelectorAll('.issue_list a'); 68 issueLinks.forEach(link => { 69 try { 70 const url = new URL(link.href); 71 const pathSegments = url.pathname.split('/'); 72 73 // Check if URL structure matches the pattern `.../Web3/1` 74 if (pathSegments.length > 4 && pathSegments[4] === '1') { 75 // Modify the path to `.../Web3/issue/view/1` 76 pathSegments.splice(4, 0, 'issue', 'view'); 77 url.pathname = pathSegments.join('/'); 78 79 // Update the link href 80 link.href = url.toString(); 81 } 82 } catch (e) { 83 // ignore invalid URLs 84 } 85 }); 86 87 // Dark Mode Toggle Functionality 88 const body = document.body; 89 const themeToggleButton = document.getElementById("themeToggleButton"); 90 91 if (themeToggleButton) { 92 // Check local storage for theme preference 93 const savedTheme = localStorage.getItem("theme"); 94 if (savedTheme === "dark") { 95 body.classList.add("dark-mode"); 96 themeToggleButton.textContent = "âï¸"; // Sun icon for light mode 97 } else { 98 themeToggleButton.textContent = "ð"; // Moon icon for dark mode 99 } 100 101 // Toggle theme on button click 102 themeToggleButton.addEventListener("click", function() { 103 body.classList.toggle("dark-mode"); 104 const isDarkMode = body.classList.contains("dark-mode"); 105 themeToggleButton.textContent = isDarkMode ? "âï¸" : "ð"; // Toggle icons 106 localStorage.setItem("theme", isDarkMode ? "dark" : "light"); 107 }); 108 } 109 110 // Burger Menu Toggle Functionality 111 const toggleButton = document.querySelector('.pkp_site_nav_toggle'); 112 const navMenu = document.querySelector('.pkp_navigation_primary_row'); 113 114 // Add a click event to toggle menu visibility 115 if (toggleButton && navMenu) { 116 toggleButton.addEventListener('click', function() { 117 navMenu.classList.toggle('active'); 118 }); 119 } 120 121 // Copy to Clipboard Functionality 122 function enableCopyToClipboard() { 123 const copyElements = document.querySelectorAll("[data-copy-target]"); 124 125 copyElements.forEach(element => { 126 const copyText = element.textContent || element.innerText; 127 128 element.style.cursor = "pointer"; // Indicate clickable 129 element.addEventListener("click", function() { 130 navigator.clipboard.writeText(copyText).then(() => { 131 alert("Copied to clipboard: " + copyText); 132 }).catch((err) => { 133 console.error("Failed to copy text: ", err); 134 }); 135 }); 136 }); 137 } 138 139 enableCopyToClipboard();
139 // Initialize copy-to-clipboard functionality on page load 140 }); 141 142 document.addEventListener('DOMContentLoaded', function () { 143 function fetchTotalViews() { 144 const countElement = document.getElementById('count-all'); 145 if (!countElement) { 146 console.log("count-all element not found on this page; skipping fetch."); 147 return; 148 } 149 150 fetch('/img/total_views.php') 151 .then(response => { 152 if (!response.ok) throw new Error('Network response was not ok'); 153 return response.json(); 154 }) 155 .then(data => { 156 countElement.innerText = data.totalViews; 157 console.log("Updated count to:", data.totalViews); 158 }) 159 .catch(error => { 160 console.error('Error fetching total views:', error); 161 const totalViewsContainer = document.getElementById('total-article-views'); 162 if (totalViewsContainer) { 163 totalViewsContainer.innerHTML = '<h2>Error loading total views.</h2>'; 164 } 165 }); 166 } 167 168 fetchTotalViews(); 169 }); 170 171 document.addEventListener('DOMContentLoaded', function () { 172 function fetchTotalComments() { 173 const countElement = document.getElementById('total-comments'); 174 if (!countElement) { 175 console.log("total-comments element not found on this page; skipping fetch."); 176 return; 177 } 178 179 fetch('/img/total_comments.php') 180 .then(response => { 181 if (!response.ok) throw new Error('Network response was not ok'); 182 return response.json(); 183 }) 184 .then(data => { 185 countElement.innerText = data.totalComments; 186 console.log("Updated total comments to:", data.totalComments); 187 }) 188 .catch(error => { 189 console.error('Error fetching total comments:', error); 190 countElement.innerText = 'Error loading total comments.'; 191 }); 192 } 193 194 fetchTotalComments(); 195 }); 196 197 document.addEventListener("DOMContentLoaded", function() { 198 const searchToggleButton = document.getElementById("searchToggleButton"); 199 const searchComponentContainer = document.querySelector(".search-component-container"); 200 const searchForm = document.getElementById("searchForm"); 201 const searchResults = document.getElementById("searchResults"); 202 203 if (searchToggleButton && searchComponentContainer) { 204 searchToggleButton.addEventListener("click", function(event) { 205 event.stopPropagation(); 206 searchComponentContainer.classList.toggle("show"); 207 }); 208 209 document.addEventListener("click", function(event) { 210 if (!searchComponentContainer.contains(event.target) && !searchToggleButton.contains(event.target)) { 211 searchComponentContainer.classList.remove("show"); 212 } 213 }); 214 } 215 216 if (searchForm && searchResults) { 217 searchForm.addEventListener("submit", function(event) { 218 event.preventDefault(); 219 220 const formData = new FormData(searchForm); 221 const query = (formData.get('query') || '').toString().trim(); 222 223 if (query === "") { 224 alert("Please enter a search query."); 225 return; 226 } 227 228 searchResults.innerHTML = '<div class="spinner"></div>'; 229 searchResults.style.display = "block"; 230 231 const ajaxUrl = `https://mlhs.ink/Journal/index.php/Web3/search?${new URLSearchParams(formData).toString()}`; 232 console.log('AJAX URL:', ajaxUrl); 233 234 fetch(ajaxUrl, { 235 method: "GET", 236 headers: { 'X-Requested-With': 'XMLHttpRequest' }, 237 credentials: 'same-origin', 238 }) 239 .then(response => { 240 if (!response.ok) throw new Error("Network response was not ok"); 241 return response.text(); 242 }) 243 .then(data => { 244 searchResults.innerHTML = data; 245 }) 246 .catch(error => { 247 console.error("Error fetching search results:", error); 248 searchResults.innerHTML = ` 249 <p>An error occurred while fetching search results.</p> 250 <button id="retrySearch">Retry</button> 251 `; 252 const btn = document.getElementById('retrySearch'); 253 if (btn) { 254 btn.addEventListener('click', () => { 255 searchForm.dispatchEvent(new Event('submit')); 256 }); 257 } 258 }); 259 }); 260 } 261 });
262 263 document.addEventListener("DOMContentLoaded", function() { 264 const searchToggleButton = document.getElementById("searchToggleButton"); 265 const searchComponentContainer = document.getElementById("searchComponentContainer"); 266 267 if (searchToggleButton && searchComponentContainer) { 268 searchToggleButton.addEventListener("click", function() { 269 if (searchComponentContainer.style.display === "none" || searchComponentContainer.style.display === "") { 270 searchComponentContainer.style.display = "block"; 271 } else { 272 searchComponentContainer.style.display = "none"; 273 } 274 }); 275 } 276 }); 277 278})(jQuery);
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.