diff options
| author | Danilo M. <danix@danix.xyz> | 2026-04-22 12:43:22 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-04-22 12:43:22 +0200 |
| commit | 5b476f8905f411768e23cb01d577a60e5a5fd725 (patch) | |
| tree | 0a08cc83d809dbea714f52826e822501ee7c0165 /themes/danix-xyz-hacker/assets/js | |
| parent | 082e9246ffe453031894d32d3cee9d5d1bf2b67a (diff) | |
| download | danixxyz-5b476f8905f411768e23cb01d577a60e5a5fd725.tar.gz danixxyz-5b476f8905f411768e23cb01d577a60e5a5fd725.zip | |
chore: extract theme into git submodule (danix2-hugo-theme)
Diffstat (limited to 'themes/danix-xyz-hacker/assets/js')
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/article-lazy.js | 34 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/code-copy.js | 79 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/contact-form.js | 45 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/form-components.js | 127 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/fortune.js | 9 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/matrix-rain.js | 157 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/menu.js | 112 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/not-found-page.js | 9 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/reading-progress.js | 29 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/search.js | 134 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/tag-cloud-spiral.js | 122 | ||||
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/theme-toggle.js | 53 |
12 files changed, 0 insertions, 910 deletions
diff --git a/themes/danix-xyz-hacker/assets/js/article-lazy.js b/themes/danix-xyz-hacker/assets/js/article-lazy.js deleted file mode 100644 index 64ca862..0000000 --- a/themes/danix-xyz-hacker/assets/js/article-lazy.js +++ /dev/null @@ -1,34 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - var timeline = document.querySelector('ol.timeline'); - if (!timeline) return; - - // Progressive enhancement: activates CSS hidden state - timeline.classList.add('js-lazy-timeline'); - - var items = Array.prototype.slice.call( - timeline.querySelectorAll('.timeline-item') - ); - - function reveal(item) { - item.classList.add('is-visible'); - } - - var observer = new IntersectionObserver( - function (entries) { - entries.forEach(function (entry) { - if (entry.isIntersecting) { - reveal(entry.target); - observer.unobserve(entry.target); - } - }); - }, - { - rootMargin: '-80px 0px 0px 0px', - threshold: 0.12, - } - ); - - items.forEach(function (item) { - observer.observe(item); - }); -}); diff --git a/themes/danix-xyz-hacker/assets/js/code-copy.js b/themes/danix-xyz-hacker/assets/js/code-copy.js deleted file mode 100644 index 8591436..0000000 --- a/themes/danix-xyz-hacker/assets/js/code-copy.js +++ /dev/null @@ -1,79 +0,0 @@ -(function () { - var LANG_NAMES = { - bash: 'Shell', sh: 'Shell', shell: 'Shell', zsh: 'Shell', - js: 'JavaScript', javascript: 'JavaScript', - ts: 'TypeScript', typescript: 'TypeScript', - go: 'Go', - py: 'Python', python: 'Python', - rs: 'Rust', rust: 'Rust', - html: 'HTML', - css: 'CSS', - toml: 'TOML', - yaml: 'YAML', yml: 'YAML', - json: 'JSON', - sql: 'SQL', - md: 'Markdown', markdown: 'Markdown', - c: 'C', - cpp: 'C++', 'c++': 'C++', - java: 'Java', - php: 'PHP', - ruby: 'Ruby', rb: 'Ruby', - swift: 'Swift', - kotlin: 'Kotlin', kt: 'Kotlin', - dockerfile: 'Dockerfile', - makefile: 'Makefile', - text: 'Text', txt: 'Text', - }; - - function prettyName(lang) { - if (!lang) return ''; - var key = lang.toLowerCase(); - return LANG_NAMES[key] || (lang.charAt(0).toUpperCase() + lang.slice(1)); - } - - function getCodeText(wrapper) { - var el = wrapper.querySelector('.lntd:last-child code') - || wrapper.querySelector('.code-body code') - || wrapper.querySelector('.code-body pre'); - return el ? el.innerText : ''; - } - - function initBlock(wrapper) { - var header = wrapper.querySelector('.code-header'); - if (header) { - var label = wrapper.querySelector('.code-lang-label'); - if (label) label.textContent = prettyName(header.getAttribute('data-lang') || ''); - } - - var btn = wrapper.querySelector('[data-copy-target]'); - if (!btn) return; - - btn.addEventListener('click', function () { - var text = getCodeText(wrapper); - if (!text) return; - - navigator.clipboard.writeText(text).then(function () { - var copyIcon = btn.querySelector('.icon-copy'); - var checkIcon = btn.querySelector('.icon-check'); - var liveRegion = wrapper.querySelector('.code-copy-status'); - if (copyIcon) copyIcon.classList.add('hidden'); - if (checkIcon) checkIcon.classList.remove('hidden'); - btn.classList.add('is-copied'); - if (liveRegion) liveRegion.textContent = 'Code copied to clipboard.'; - - setTimeout(function () { - if (copyIcon) copyIcon.classList.remove('hidden'); - if (checkIcon) checkIcon.classList.add('hidden'); - btn.classList.remove('is-copied'); - if (liveRegion) liveRegion.textContent = ''; - }, 2000); - }).catch(function () { - // silent fail for insecure contexts - }); - }); - } - - document.addEventListener('DOMContentLoaded', function () { - document.querySelectorAll('.code-block-wrapper').forEach(initBlock); - }); -})(); diff --git a/themes/danix-xyz-hacker/assets/js/contact-form.js b/themes/danix-xyz-hacker/assets/js/contact-form.js deleted file mode 100644 index 4fa8f55..0000000 --- a/themes/danix-xyz-hacker/assets/js/contact-form.js +++ /dev/null @@ -1,45 +0,0 @@ -document.addEventListener('alpine:init', () => { - Alpine.data('contactForm', () => ({ - formData: { - name: '', - email: '', - message: '' - }, - isSubmitting: false, - statusMessage: '', - statusClass: '', - - async submitContactForm() { - this.isSubmitting = true; - this.statusMessage = ''; - this.statusClass = ''; - - try { - const response = await fetch('/contact.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(this.formData) - }); - - const data = await response.json(); - - if (response.ok) { - this.statusMessage = 'Message sent successfully!'; - this.statusClass = 'bg-green-100 text-green-800 border border-green-300'; - this.formData = { name: '', email: '', message: '' }; - } else { - this.statusMessage = data.error || 'An error occurred. Please try again.'; - this.statusClass = 'bg-red-100 text-red-800 border border-red-300'; - } - } catch (error) { - this.statusMessage = 'An error occurred. Please try again.'; - this.statusClass = 'bg-red-100 text-red-800 border border-red-300'; - console.error('Form submission error:', error); - } finally { - this.isSubmitting = false; - } - } - })); -}); diff --git a/themes/danix-xyz-hacker/assets/js/form-components.js b/themes/danix-xyz-hacker/assets/js/form-components.js deleted file mode 100644 index ffa4260..0000000 --- a/themes/danix-xyz-hacker/assets/js/form-components.js +++ /dev/null @@ -1,127 +0,0 @@ -// Form component utilities and Alpine.js data - -export function formComponentsData() { - return { - // Modal states - showAlertModal: false, - showConfirmModal: false, - showContentModal: false, - - // Toast notification state - toasts: [], - - // Handle confirm modal action - handleConfirm() { - this.showConfirmModal = false; - this.showToast('success', 'Action confirmed!'); - }, - - // Show toast notification - showToast(type = 'success', message = null) { - const messages = { - success: 'Operation completed successfully!', - error: 'An error occurred. Please try again.', - info: 'Here is some information.', - warning: 'Please be careful with this action.' - }; - - const toastMessage = message || messages[type] || messages.success; - const toastId = Date.now(); - - // Add toast to list - this.toasts.push({ - id: toastId, - type: type, - message: toastMessage - }); - - // Auto-remove after 5 seconds - setTimeout(() => { - this.toasts = this.toasts.filter(t => t.id !== toastId); - }, 5000); - }, - - // Remove toast manually - removeToast(id) { - this.toasts = this.toasts.filter(t => t.id !== id); - }, - - // Form validation utilities - validateEmail(email) { - const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - return regex.test(email); - }, - - validatePassword(password) { - return password.length >= 8; - }, - - // Auto-expand textarea - autoExpandTextarea(event) { - const textarea = event.target; - textarea.style.height = 'auto'; - textarea.style.height = (textarea.scrollHeight) + 'px'; - } - }; -} - -// Toast container component for Alpine.js -export function renderToastContainer(Alpine) { - if (!Alpine) return; - - // This can be used in templates via Alpine - window.formUtils = { - formatCharCount(current, max) { - if (max) { - return `${current}/${max}`; - } - return current; - }, - - isCharCountWarning(current, max) { - if (!max) return false; - return current > (max * 0.8); - }, - - isCharCountError(current, max) { - if (!max) return false; - return current >= max; - } - }; -} - -// Focus Trap for Modals - Week 5 -function createFocusTrap(modalElement) { - const focusableElements = modalElement.querySelectorAll( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); - - if (focusableElements.length === 0) return; - - const firstElement = focusableElements[0]; - const lastElement = focusableElements[focusableElements.length - 1]; - - modalElement.addEventListener('keydown', (e) => { - if (e.key !== 'Tab') return; - - if (e.shiftKey) { - // Shift + Tab - if (document.activeElement === firstElement) { - e.preventDefault(); - lastElement.focus(); - } - } else { - // Tab - if (document.activeElement === lastElement) { - e.preventDefault(); - firstElement.focus(); - } - } - }); - - // Set initial focus - firstElement.focus(); -} - -// Export for use in Alpine.js -window.createFocusTrap = createFocusTrap; diff --git a/themes/danix-xyz-hacker/assets/js/fortune.js b/themes/danix-xyz-hacker/assets/js/fortune.js deleted file mode 100644 index d4f981b..0000000 --- a/themes/danix-xyz-hacker/assets/js/fortune.js +++ /dev/null @@ -1,9 +0,0 @@ -(function() { - const el = document.getElementById('fortune-quote'); - if (!el) return; - const quotes = JSON.parse(el.dataset.quotes); - if (!quotes || quotes.length === 0) return; - const q = quotes[Math.floor(Math.random() * quotes.length)]; - el.querySelector('.fortune-text').textContent = '"' + q.text + '"'; - el.querySelector('.fortune-author').textContent = '— ' + q.author; -})(); diff --git a/themes/danix-xyz-hacker/assets/js/matrix-rain.js b/themes/danix-xyz-hacker/assets/js/matrix-rain.js deleted file mode 100644 index 53c55d8..0000000 --- a/themes/danix-xyz-hacker/assets/js/matrix-rain.js +++ /dev/null @@ -1,157 +0,0 @@ -// Matrix rain background effect -(function() { - // Bail out if user prefers reduced motion - if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { - return; - } - - // Canvas and context - let canvas = document.getElementById('matrix-rain'); - if (!canvas) return; - const ctx = canvas.getContext('2d'); - - // State - let columns = []; - let frameCount = 0; - let colors = { accent: '#a855f7', accent2: '#00ff88', bg: '#060b10', head: '#ffffff' }; - - // Character set: 30% ASCII, 70% katakana - const ASCII = Array.from({ length: 94 }, (_, i) => String.fromCharCode(33 + i)); - const KATA = Array.from({ length: 96 }, (_, i) => String.fromCodePoint(0x30a0 + i)); - const CHARS = shuffle([...ASCII, ...KATA, ...KATA, ...KATA]); - - // Utility: shuffle array - function shuffle(arr) { - for (let i = arr.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [arr[i], arr[j]] = [arr[j], arr[i]]; - } - return arr; - } - - // Utility: convert hex or rgb color to rgba string - function hexToRgba(color, alpha) { - const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); - if (rgbMatch) { - return `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${alpha})`; - } - const hex = color.replace('#', ''); - const r = parseInt(hex.substring(0, 2), 16); - const g = parseInt(hex.substring(2, 4), 16); - const b = parseInt(hex.substring(4, 6), 16); - return `rgba(${r}, ${g}, ${b}, ${alpha})`; - } - - // Sample CSS variables based on current theme - function sampleColors() { - const style = getComputedStyle(document.documentElement); - const isDark = document.documentElement.classList.contains('theme-dark'); - colors.accent = style.getPropertyValue('--accent').trim(); - colors.accent2 = style.getPropertyValue('--accent2').trim(); - colors.bg = style.getPropertyValue('--bg').trim(); - // Head char: bright white in dark mode, deep purple-black in light mode - colors.head = isDark ? '#ffffff' : '#1a0533'; - } - - // Resize canvas to window dimensions - function resizeCanvas() { - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - ctx.font = '14px "JetBrains Mono", monospace'; - ctx.textBaseline = 'top'; - initColumns(); - } - - // Initialize columns for the current canvas width - function initColumns() { - columns = []; - const columnWidth = 14; - const columnCount = Math.floor(canvas.width / columnWidth); - - for (let i = 0; i < columnCount; i++) { - columns.push({ - x: i * columnWidth, - y: -Math.floor(Math.random() * 40), // stagger start above viewport - speed: 2 + Math.floor(Math.random() * 3), // 2-4 frames between drops - color: Math.random() < 0.6 ? 'accent2' : 'accent', // 60% green, 40% purple - charIndex: Math.floor(Math.random() * CHARS.length), - length: 8 + Math.floor(Math.random() * 13), // trail length 8-20 - }); - } - } - - // Set up MutationObserver for theme switching - function setupThemeObserver() { - const observer = new MutationObserver(function(mutations) { - for (const m of mutations) { - if (m.attributeName === 'class') { - sampleColors(); - break; - } - } - }); - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ['class'], - }); - } - - // Main animation loop - function drawFrame() { - frameCount++; - - // Fade layer: semi-transparent background fill - ctx.fillStyle = hexToRgba(colors.bg, 0.085); - ctx.fillRect(0, 0, canvas.width, canvas.height); - - // Draw each column - for (const col of columns) { - // Skip if not time to drop yet (per-column throttle) - if (frameCount % col.speed !== 0) continue; - - // Draw explicit trail in column color - ctx.fillStyle = colors[col.color]; - for (let i = 1; i <= col.length; i++) { - const trailY = (col.y - i) * 14; - if (trailY < 0) continue; - const trailCharIndex = (col.charIndex - i + CHARS.length) % CHARS.length; - ctx.fillText(CHARS[trailCharIndex], col.x, trailY); - } - - // Draw head character (bright) - ctx.fillStyle = colors.head; - const headCharIndex = col.charIndex % CHARS.length; - ctx.fillText(CHARS[headCharIndex], col.x, col.y * 14); - - // Advance column - col.y++; - col.charIndex = (col.charIndex + 1) % CHARS.length; - - // Reset when scrolled off screen - if (col.y * 14 > canvas.height + col.length * 14) { - col.y = -Math.floor(Math.random() * 20); - col.charIndex = Math.floor(Math.random() * CHARS.length); - col.color = Math.random() < 0.6 ? 'accent2' : 'accent'; - } - } - - requestAnimationFrame(drawFrame); - } - - // Initialize - sampleColors(); - resizeCanvas(); - setupThemeObserver(); - - // Debounced resize handler - let resizeTimer; - window.addEventListener('resize', function() { - clearTimeout(resizeTimer); - resizeTimer = setTimeout(resizeCanvas, 150); - }); - - // Start animation when fonts are ready - document.fonts.ready.then(function() { - requestAnimationFrame(drawFrame); - }); -})(); diff --git a/themes/danix-xyz-hacker/assets/js/menu.js b/themes/danix-xyz-hacker/assets/js/menu.js deleted file mode 100644 index 3f32642..0000000 --- a/themes/danix-xyz-hacker/assets/js/menu.js +++ /dev/null @@ -1,112 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - const menuToggle = document.getElementById('menu-toggle'); - const menuOverlay = document.getElementById('menu-overlay'); - const menuPanel = document.getElementById('hamburger-menu'); - - function openMenu() { - if (!menuOverlay || !menuPanel) return; - - // Show overlay - menuOverlay.classList.remove('opacity-0'); - menuOverlay.classList.remove('invisible'); - - // Slide menu in - menuPanel.classList.remove('translate-x-full'); - - // Manage accessibility - menuToggle.setAttribute('aria-expanded', 'true'); - menuPanel.removeAttribute('aria-hidden'); - - // Control body overflow - document.body.style.overflow = 'hidden'; - - // Focus first focusable element in menu - const firstFocusable = menuPanel.querySelector('a, button'); - if (firstFocusable) { - setTimeout(() => firstFocusable.focus(), 50); - } - } - - function closeMenu() { - if (!menuOverlay || menuOverlay.classList.contains('opacity-0')) return; - - // Hide overlay - menuOverlay.classList.add('opacity-0'); - menuOverlay.classList.add('invisible'); - - // Slide menu out - menuPanel.classList.add('translate-x-full'); - - // Manage accessibility - menuToggle.setAttribute('aria-expanded', 'false'); - menuPanel.setAttribute('aria-hidden', 'true'); - - // Restore body overflow - document.body.style.overflow = ''; - - // Return focus to toggle button - menuToggle.focus(); - } - - function toggleMenu() { - if (menuOverlay && menuOverlay.classList.contains('opacity-0')) { - openMenu(); - } else { - closeMenu(); - } - } - - // Toggle menu when clicking the hamburger button - if (menuToggle) { - menuToggle.addEventListener('click', toggleMenu); - } - - // Close menu when clicking on the overlay - if (menuOverlay) { - menuOverlay.addEventListener('click', (e) => { - if (e.target === menuOverlay) { - closeMenu(); - } - }); - } - - // Close menu when clicking menu items - const menuLinks = document.querySelectorAll('#hamburger-menu a, #hamburger-menu button'); - menuLinks.forEach(link => { - link.addEventListener('click', closeMenu); - }); - - // Close menu on Escape key - document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && menuOverlay && !menuOverlay.classList.contains('opacity-0')) { - closeMenu(); - } - }); - - // Focus trap: keep tab within menu when open - if (menuPanel) { - menuPanel.addEventListener('keydown', (e) => { - if (e.key !== 'Tab') return; - - const focusableElements = menuPanel.querySelectorAll('a, button, [tabindex]:not([tabindex="-1"])'); - if (focusableElements.length === 0) return; - - const firstElement = focusableElements[0]; - const lastElement = focusableElements[focusableElements.length - 1]; - const isMenuOpen = !menuOverlay.classList.contains('opacity-0'); - - if (!isMenuOpen) return; - - // Shift+Tab on first element: move to last - if (e.shiftKey && document.activeElement === firstElement) { - e.preventDefault(); - lastElement.focus(); - } - // Tab on last element: move to first - else if (!e.shiftKey && document.activeElement === lastElement) { - e.preventDefault(); - firstElement.focus(); - } - }); - } -}); diff --git a/themes/danix-xyz-hacker/assets/js/not-found-page.js b/themes/danix-xyz-hacker/assets/js/not-found-page.js deleted file mode 100644 index 2b4f676..0000000 --- a/themes/danix-xyz-hacker/assets/js/not-found-page.js +++ /dev/null @@ -1,9 +0,0 @@ -// 404 page: initialize shared notFoundPage Alpine component -document.addEventListener('alpine:init', () => { - // Ensure search index is preloaded on 404 page - const notFoundElement = document.querySelector('[x-data*="notFoundPage"]'); - if (notFoundElement && notFoundElement.__x) { - notFoundElement.__x.$data.init(); - } - console.log('404 page initialized with shared search functionality'); -}); diff --git a/themes/danix-xyz-hacker/assets/js/reading-progress.js b/themes/danix-xyz-hacker/assets/js/reading-progress.js deleted file mode 100644 index ee1192f..0000000 --- a/themes/danix-xyz-hacker/assets/js/reading-progress.js +++ /dev/null @@ -1,29 +0,0 @@ -// Reading progress bar for single pages/articles -(function() { - const progressBar = document.getElementById('reading-progress'); - - if (!progressBar) return; - - function updateProgress() { - const windowHeight = window.innerHeight; - const documentHeight = document.documentElement.scrollHeight - windowHeight; - const scrollProgress = documentHeight > 0 ? (window.scrollY / documentHeight) * 100 : 0; - progressBar.style.width = scrollProgress + '%'; - } - - // Throttle the scroll event for better performance - let ticking = false; - - window.addEventListener('scroll', function() { - if (!ticking) { - window.requestAnimationFrame(function() { - updateProgress(); - ticking = false; - }); - ticking = true; - } - }, false); - - // Initial call - updateProgress(); -})(); diff --git a/themes/danix-xyz-hacker/assets/js/search.js b/themes/danix-xyz-hacker/assets/js/search.js deleted file mode 100644 index 8fb6262..0000000 --- a/themes/danix-xyz-hacker/assets/js/search.js +++ /dev/null @@ -1,134 +0,0 @@ -// Lazy-load search index from JSON file (language-aware) -async function loadSearchIndex() { - if (window.searchIndex) { - return window.searchIndex; - } - try { - // Detect current language from URL - const isItalian = window.location.pathname.startsWith('/it/'); - const indexPath = isItalian ? '/it/search-index.json' : '/search-index.json'; - - const response = await fetch(indexPath); - if (!response.ok) throw new Error('Failed to load search index'); - window.searchIndex = await response.json(); - return window.searchIndex; - } catch (error) { - console.error('Error loading search index:', error); - return []; - } -} - -// Filter articles by query (case-insensitive, max 5 results) -function filterArticles(query, articles) { - if (!query.trim()) { - return []; - } - const lowerQuery = query.toLowerCase(); - return articles - .filter(article => - article.title.toLowerCase().includes(lowerQuery) || - article.summary.toLowerCase().includes(lowerQuery) - ) - .slice(0, 5); -} - -// Register Alpine.js components -document.addEventListener('alpine:init', () => { - // Desktop search modal component - Alpine.data('searchOverlay', () => ({ - isOpen: false, - searchQuery: '', - filteredArticles: [], - allArticles: [], - indexLoaded: false, - - async open() { - this.isOpen = true; - await this.ensureIndexLoaded(); - this.$nextTick(() => { - const input = this.$el.querySelector('#search-input-desktop'); - if (input) input.focus(); - }); - }, - - close() { - this.isOpen = false; - this.searchQuery = ''; - this.filteredArticles = []; - }, - - async ensureIndexLoaded() { - if (!this.indexLoaded) { - this.allArticles = await loadSearchIndex(); - this.indexLoaded = true; - } - }, - - filterArticles(query) { - this.searchQuery = query; - this.filteredArticles = filterArticles(query, this.allArticles); - }, - - handleEscape(event) { - if (event.key === 'Escape') { - this.close(); - } - } - })); - - // Mobile search component (integrated into hamburger menu) - Alpine.data('mobileSearch', () => ({ - searchQuery: '', - filteredArticles: [], - allArticles: [], - indexLoaded: false, - - async ensureIndexLoaded() { - if (!this.indexLoaded) { - this.allArticles = await loadSearchIndex(); - this.indexLoaded = true; - } - }, - - filterArticles(query) { - this.searchQuery = query; - this.filteredArticles = filterArticles(query, this.allArticles); - } - })); - - // Refactored 404 page component - Alpine.data('notFoundPage', () => ({ - showEasterEgg: false, - searchQuery: '', - filteredArticles: [], - allArticles: [], - indexLoaded: false, - - async init() { - await this.ensureIndexLoaded(); - }, - - async ensureIndexLoaded() { - if (!this.indexLoaded) { - this.allArticles = await loadSearchIndex(); - this.indexLoaded = true; - } - }, - - filterArticles(query) { - this.searchQuery = query; - this.filteredArticles = filterArticles(query, this.allArticles); - }, - - toggleEasterEgg() { - this.showEasterEgg = !this.showEasterEgg; - }, - - goToRandomArticle() { - if (this.allArticles.length > 0) { - const randomArticle = this.allArticles[Math.floor(Math.random() * this.allArticles.length)]; - window.location.href = randomArticle.url; - } - } - })); -}); diff --git a/themes/danix-xyz-hacker/assets/js/tag-cloud-spiral.js b/themes/danix-xyz-hacker/assets/js/tag-cloud-spiral.js deleted file mode 100644 index bed4645..0000000 --- a/themes/danix-xyz-hacker/assets/js/tag-cloud-spiral.js +++ /dev/null @@ -1,122 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - var containers = document.querySelectorAll('[data-tag-cloud]'); - if (!containers.length) return; - - Array.prototype.forEach.call(containers, function (container) { - if (container.offsetWidth < 400) return; - - var links = Array.prototype.slice.call( - container.querySelectorAll('.tag-cloud-link') - ); - if (!links.length) return; - - // Sort descending by weight (biggest first = placed near center) - links.sort(function (a, b) { - return parseFloat(b.dataset.weight) - parseFloat(a.dataset.weight); - }); - - // String hash → deterministic angle seed (0..2π) - function hashAngle(str) { - var h = 0; - for (var i = 0; i < str.length; i++) { - h = (h * 31 + str.charCodeAt(i)) & 0xffffffff; - } - return ((h >>> 0) / 0xffffffff) * 2 * Math.PI; - } - - // AABB collision check - function overlaps(a, b) { - return !( - a.right < b.left || - a.left > b.right || - a.bottom < b.top || - a.top > b.bottom - ); - } - - var placed = []; - var containerWidth = container.offsetWidth; - var cx = containerWidth / 2; - - // Measure each tag before repositioning - var sizes = links.map(function (link) { - var rect = link.getBoundingClientRect(); - return { w: rect.width, h: rect.height }; - }); - - // Switch container to relative positioning and remove flex layout - container.style.position = 'relative'; - container.style.display = 'block'; - container.classList.remove('flex', 'flex-wrap'); - - var padding = -2; // px gap between tags (negative allows ~2px edge overlap) - var aStep = 0.2; // radians per spiral step - var rScale = (containerWidth * 0.013); // spiral tightness - - var minTop = Infinity, maxBottom = -Infinity; - - links.forEach(function (link, i) { - var w = sizes[i].w; - var h = sizes[i].h; - var seed = hashAngle(link.href); - var theta = seed; - var placed_rect; - - // Step along spiral until no collision - for (var attempt = 0; attempt < 3000; attempt++) { - var r = rScale * theta; - var x = cx + r * Math.cos(theta) - w / 2; - var y = r * Math.sin(theta) - h / 2; - - var candidate = { left: x, top: y, right: x + w, bottom: y + h }; - var collision = false; - - for (var j = 0; j < placed.length; j++) { - var p = placed[j]; - var padded = { - left: p.left - padding, - top: p.top - padding, - right: p.right + padding, - bottom: p.bottom + padding - }; - if (overlaps(candidate, padded)) { - collision = true; - break; - } - } - - if (!collision) { - placed_rect = candidate; - break; - } - theta += aStep; - } - - if (!placed_rect) { - // Fallback: just append to flow if spiral exhausted - link.style.position = 'static'; - return; - } - - placed.push(placed_rect); - - link.style.position = 'absolute'; - link.style.left = Math.round(placed_rect.left) + 'px'; - link.style.top = Math.round(placed_rect.top) + 'px'; - - if (placed_rect.top < minTop) minTop = placed_rect.top; - if (placed_rect.bottom > maxBottom) maxBottom = placed_rect.bottom; - }); - - // Normalize: shift all tags so topmost is at y=16px - var offset = 16 - minTop; - links.forEach(function (link) { - if (link.style.position === 'absolute') { - link.style.top = (parseInt(link.style.top) + offset) + 'px'; - } - }); - - // Set container height to fit all tags + 2rem bottom padding (32px) - container.style.height = (maxBottom - minTop + 48) + 'px'; - }); -}); diff --git a/themes/danix-xyz-hacker/assets/js/theme-toggle.js b/themes/danix-xyz-hacker/assets/js/theme-toggle.js deleted file mode 100644 index bb95b2a..0000000 --- a/themes/danix-xyz-hacker/assets/js/theme-toggle.js +++ /dev/null @@ -1,53 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - const themeToggle = document.getElementById('theme-toggle'); - const sunIcon = document.getElementById('theme-icon-sun'); - const moonIcon = document.getElementById('theme-icon-moon'); - - function updateThemeIcon() { - const isDark = document.documentElement.classList.contains('theme-dark'); - if (sunIcon && moonIcon) { - if (isDark) { - sunIcon.style.display = 'block'; - moonIcon.style.display = 'none'; - } else { - sunIcon.style.display = 'none'; - moonIcon.style.display = 'block'; - } - } - } - - // Update icon on initial load - if (sunIcon && moonIcon) { - updateThemeIcon(); - } - - if (!themeToggle) { - return; - } - - themeToggle.addEventListener('click', function(e) { - e.preventDefault(); - - // Get current theme from html element - const htmlElement = document.documentElement; - const isDark = htmlElement.classList.contains('theme-dark'); - const newTheme = isDark ? 'light' : 'dark'; - - // Remove both theme classes - htmlElement.classList.remove('theme-light', 'theme-dark'); - - // Add the new theme class - htmlElement.classList.add(`theme-${newTheme}`); - - // Persist to localStorage - localStorage.setItem('theme', newTheme); - - // Update icon display - updateThemeIcon(); - - // Update Feather Icons if available - if (window.feather) { - window.feather.replace(); - } - }); -}); |
