| | |
| | | // Theme switching functionality |
| | | document.addEventListener('DOMContentLoaded', function() { |
| | | console.log('Theme switch initiated'); |
| | | const themeSwitch = document.getElementById('theme-switch'); |
| | | |
| | | if (!themeSwitch) return; |
| | | |
| | | // Initialize theme from localStorage or system preference |
| | | const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); |
| | | const storedTheme = localStorage.getItem('theme'); |
| | | |
| | | if (storedTheme) { |
| | | document.documentElement.classList.toggle('dark', storedTheme === 'dark'); |
| | | themeSwitch.checked = storedTheme === 'dark'; |
| | | } else { |
| | | document.documentElement.classList.toggle('dark', prefersDark.matches); |
| | | themeSwitch.checked = prefersDark.matches; |
| | | } |
| | | |
| | | // Handle theme switch changes |
| | | themeSwitch.addEventListener('change', async function () { |
| | | const isDark = this.checked; |
| | | document.documentElement.classList.toggle('dark', isDark); |
| | | localStorage.setItem('theme', isDark ? 'dark' : 'light'); |
| | | |
| | | // If user is logged in, save preference |
| | | if (jvbSettings.currentUser !== null) { |
| | | try { |
| | | await fetch(`${jvbSettings.api}settings`, { |
| | | method: 'POST', |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | 'X-WP-Nonce': jvbSettings.nonce, |
| | | 'action_nonce': jvbSettings.dash, |
| | | }, |
| | | body: JSON.stringify({ |
| | | dark_mode: isDark |
| | | }) |
| | | }); |
| | | } catch (error) { |
| | | console.error('Failed to save theme preference:', error); |
| | | } |
| | | } |
| | | |
| | | // Update label |
| | | const label = document.getElementById('theme-switch'); |
| | | if (label) { |
| | | label.title = isDark ? 'Toggle Light Mode' : 'Toggle Dark Mode'; |
| | | } |
| | | }); |
| | | |
| | | // Handle system theme changes |
| | | prefersDark.addEventListener('change', (e) => { |
| | | if (!localStorage.getItem('theme')) { |
| | | const isDark = e.matches; |
| | | document.documentElement.classList.toggle('dark', isDark); |
| | | themeSwitch.checked = isDark; |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | /** |
| | | * |
| | |
| | | */ |
| | | window.getTemplate = function (template){ |
| | | if (window.templates.size === 0) { |
| | | loadTemplates(); |
| | | window.loadTemplates(); |
| | | } |
| | | if(window.templates.has(template)){ |
| | | return window.templates.get(template).cloneNode(true); |
| | |
| | | * @param icon |
| | | * @returns {Node | ActiveX.IXMLDOMNode} |
| | | */ |
| | | window.getIcon = function getIcon(icon){ |
| | | window.icon = null; |
| | | window.getIcon = function getIcon(icon, style = ''){ |
| | | if (typeof icon === 'undefined') { |
| | | return ''; |
| | | } |
| | | |
| | | |
| | | if(!window.jvbIcons){ |
| | | window.jvbIcons = new Map(); |
| | | if (!window.icon) { |
| | | window.icon = document.createElement('i'); |
| | | window.icon.className = 'icon'; |
| | | window.icon.ariaHidden = true; |
| | | } |
| | | if(!window.jvbIcons.has(icon) && jvbSettings.icons[icon]){ |
| | | let temp = document.createElement('div'); |
| | | temp.innerHTML = jvbSettings.icons[icon]; |
| | | window.jvbIcons.set(icon, temp.firstElementChild.cloneNode(true)); |
| | | temp.remove(); |
| | | let theIcon = window.icon.cloneNode(true); |
| | | if (style !== '' && ['regular', 'bold', 'duotone', 'fill', 'light', 'thin' ].includes('style')) { |
| | | style = `-${style.slice(0, 2)}`; |
| | | } else { |
| | | style = ''; |
| | | } |
| | | return window.jvbIcons.get(icon)?.cloneNode(true); |
| | | theIcon.classList.add(`icon-${icon}${style}`); |
| | | return theIcon; |
| | | } |
| | | |
| | | /** |
| | |
| | | window.showToast = function(message, type='success', actions={}){ |
| | | window.jvbNotifications.showToast(message, type, actions); |
| | | } |
| | | window.dateFormatter = new Intl.DateTimeFormat('en-CA', { |
| | | year: 'numeric', |
| | | month: 'long', |
| | | day: 'numeric', |
| | | hour: '2-digit', |
| | | minute: '2-digit', |
| | | second: '2-digit', |
| | | timeZoneName: 'short' |
| | | }); |
| | | |
| | | window.formatDate = function(date) { |
| | | if (!(date instanceof Date && !isNaN(date))) { |
| | | date = new Date(date); |
| | | } |
| | | return window.dateFormatter.format(date); |
| | | } |
| | | |
| | | /** |
| | | * Outputs the set text as if a typewriter were writing it |
| | |
| | | } |
| | | |
| | | window.targetCheck = function (e, selector) { |
| | | if (Array.isArray(selector)) { |
| | | selector = selector.join(','); |
| | | } |
| | | if (typeof selector !== 'string') { |
| | | return false; |
| | | } |
| | |
| | | |
| | | schedule(key, callback, delay = 1000) { |
| | | this.cancel(key); |
| | | console.log('Scheduling action: ', key); |
| | | console.log('With callback', callback); |
| | | this.timeouts.set(key, setTimeout(() => { |
| | | callback(); |
| | | this.timeouts.delete(key); |
| | |
| | | |
| | | cancel(key) { |
| | | if (this.timeouts.has(key)) { |
| | | console.log('Cancelling ', key); |
| | | clearTimeout(this.timeouts.get(key)); |
| | | this.timeouts.delete(key); |
| | | } |
| | |
| | | |
| | | cleanup() { |
| | | for (let timeout of this.timeouts.values()) { |
| | | console.log('clearing timeout: ', timeout); |
| | | clearTimeout(timeout); |
| | | } |
| | | this.timeouts.clear(); |