Spaces:
Sleeping
Sleeping
| // Main JavaScript functionality for the Dify Learning Platform | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize tooltips | |
| initializeTooltips(); | |
| // Initialize animations | |
| initializeAnimations(); | |
| // Initialize progress tracking | |
| initializeProgressTracking(); | |
| // Initialize search functionality | |
| initializeSearch(); | |
| }); | |
| /** | |
| * Initialize Bootstrap tooltips | |
| */ | |
| function initializeTooltips() { | |
| var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); | |
| var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { | |
| return new bootstrap.Tooltip(tooltipTriggerEl); | |
| }); | |
| } | |
| /** | |
| * Initialize scroll animations | |
| */ | |
| function initializeAnimations() { | |
| // Fade in elements on scroll | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver(function(entries) { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('fade-in'); | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe elements for animation | |
| document.querySelectorAll('.tutorial-card, .project-card, .feature-item').forEach(el => { | |
| observer.observe(el); | |
| }); | |
| } | |
| /** | |
| * Initialize progress tracking | |
| */ | |
| function initializeProgressTracking() { | |
| // Track page views | |
| trackPageView(); | |
| // Track tutorial interactions | |
| document.querySelectorAll('.tutorial-card .btn').forEach(btn => { | |
| btn.addEventListener('click', function(e) { | |
| const tutorialTitle = this.closest('.tutorial-card').querySelector('.card-title').textContent; | |
| trackEvent('tutorial_started', { tutorial: tutorialTitle }); | |
| }); | |
| }); | |
| // Track project interactions | |
| document.querySelectorAll('.project-card .btn').forEach(btn => { | |
| btn.addEventListener('click', function(e) { | |
| const projectTitle = this.closest('.project-card').querySelector('.card-title').textContent; | |
| trackEvent('project_started', { project: projectTitle }); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Initialize search functionality | |
| */ | |
| function initializeSearch() { | |
| const searchInput = document.getElementById('search-input'); | |
| if (searchInput) { | |
| searchInput.addEventListener('input', debounce(handleSearch, 300)); | |
| } | |
| } | |
| /** | |
| * Handle search functionality | |
| */ | |
| function handleSearch(event) { | |
| const query = event.target.value.toLowerCase().trim(); | |
| const tutorials = document.querySelectorAll('.tutorial-card'); | |
| const projects = document.querySelectorAll('.project-card'); | |
| // Filter tutorials | |
| tutorials.forEach(card => { | |
| const title = card.querySelector('.card-title').textContent.toLowerCase(); | |
| const description = card.querySelector('.card-text').textContent.toLowerCase(); | |
| const isVisible = title.includes(query) || description.includes(query) || query === ''; | |
| card.closest('.path-item, .col-md-6, .col-lg-4')?.style.setProperty('display', isVisible ? '' : 'none'); | |
| }); | |
| // Filter projects | |
| projects.forEach(card => { | |
| const title = card.querySelector('.card-title').textContent.toLowerCase(); | |
| const description = card.querySelector('.card-text').textContent.toLowerCase(); | |
| const isVisible = title.includes(query) || description.includes(query) || query === ''; | |
| card.closest('.col-md-6, .col-lg-4')?.style.setProperty('display', isVisible ? '' : 'none'); | |
| }); | |
| // Show/hide sections based on results | |
| updateSearchResults(query); | |
| } | |
| /** | |
| * Update search results display | |
| */ | |
| function updateSearchResults(query) { | |
| const tutorialSection = document.querySelector('.learning-path'); | |
| const projectSection = document.querySelector('#projects'); | |
| if (query) { | |
| const visibleTutorials = document.querySelectorAll('.tutorial-card:not([style*="display: none"])'); | |
| const visibleProjects = document.querySelectorAll('.project-card:not([style*="display: none"])'); | |
| // Show no results message if needed | |
| if (visibleTutorials.length === 0 && visibleProjects.length === 0) { | |
| showNoResultsMessage(); | |
| } else { | |
| hideNoResultsMessage(); | |
| } | |
| } else { | |
| hideNoResultsMessage(); | |
| } | |
| } | |
| /** | |
| * Show no results message | |
| */ | |
| function showNoResultsMessage() { | |
| let noResultsMsg = document.getElementById('no-results-message'); | |
| if (!noResultsMsg) { | |
| noResultsMsg = document.createElement('div'); | |
| noResultsMsg.id = 'no-results-message'; | |
| noResultsMsg.className = 'alert alert-info text-center'; | |
| noResultsMsg.innerHTML = ` | |
| <i data-feather="search" class="me-2"></i> | |
| No tutorials or projects found matching your search. | |
| `; | |
| document.querySelector('.container').appendChild(noResultsMsg); | |
| feather.replace(); | |
| } | |
| noResultsMsg.style.display = 'block'; | |
| } | |
| /** | |
| * Hide no results message | |
| */ | |
| function hideNoResultsMessage() { | |
| const noResultsMsg = document.getElementById('no-results-message'); | |
| if (noResultsMsg) { | |
| noResultsMsg.style.display = 'none'; | |
| } | |
| } | |
| /** | |
| * Track page views for analytics | |
| */ | |
| function trackPageView() { | |
| const pageData = { | |
| page: window.location.pathname, | |
| title: document.title, | |
| timestamp: new Date().toISOString() | |
| }; | |
| // Store in localStorage for demo purposes | |
| const visits = JSON.parse(localStorage.getItem('dify_visits') || '[]'); | |
| visits.push(pageData); | |
| localStorage.setItem('dify_visits', JSON.stringify(visits.slice(-100))); // Keep last 100 visits | |
| } | |
| /** | |
| * Track custom events | |
| */ | |
| function trackEvent(eventName, eventData = {}) { | |
| const event = { | |
| name: eventName, | |
| data: eventData, | |
| timestamp: new Date().toISOString(), | |
| page: window.location.pathname | |
| }; | |
| // Store in localStorage for demo purposes | |
| const events = JSON.parse(localStorage.getItem('dify_events') || '[]'); | |
| events.push(event); | |
| localStorage.setItem('dify_events', JSON.stringify(events.slice(-100))); // Keep last 100 events | |
| console.log('Event tracked:', event); | |
| } | |
| /** | |
| * Utility function to debounce function calls | |
| */ | |
| function debounce(func, wait) { | |
| let timeout; | |
| return function(...args) { | |
| const later = () => { | |
| clearTimeout(timeout); | |
| func.apply(this, args); | |
| }; | |
| clearTimeout(timeout); | |
| timeout = setTimeout(later, wait); | |
| }; | |
| } | |
| /** | |
| * Smooth scroll to element | |
| */ | |
| function smoothScrollTo(element, offset = 80) { | |
| const targetPosition = element.offsetTop - offset; | |
| window.scrollTo({ | |
| top: targetPosition, | |
| behavior: 'smooth' | |
| }); | |
| } | |
| /** | |
| * Show loading spinner | |
| */ | |
| function showLoader(container) { | |
| const loader = document.createElement('div'); | |
| loader.className = 'text-center py-4'; | |
| loader.innerHTML = ` | |
| <div class="spinner-border text-primary" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| `; | |
| container.appendChild(loader); | |
| return loader; | |
| } | |
| /** | |
| * Hide loading spinner | |
| */ | |
| function hideLoader(loader) { | |
| if (loader && loader.parentNode) { | |
| loader.parentNode.removeChild(loader); | |
| } | |
| } | |
| /** | |
| * Show toast notification | |
| */ | |
| function showToast(message, type = 'info') { | |
| const toast = document.createElement('div'); | |
| toast.className = `toast align-items-center text-white bg-${type} border-0`; | |
| toast.setAttribute('role', 'alert'); | |
| toast.setAttribute('aria-live', 'assertive'); | |
| toast.setAttribute('aria-atomic', 'true'); | |
| toast.innerHTML = ` | |
| <div class="d-flex"> | |
| <div class="toast-body"> | |
| ${message} | |
| </div> | |
| <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button> | |
| </div> | |
| `; | |
| // Add to toast container or create one | |
| let toastContainer = document.getElementById('toast-container'); | |
| if (!toastContainer) { | |
| toastContainer = document.createElement('div'); | |
| toastContainer.id = 'toast-container'; | |
| toastContainer.className = 'toast-container position-fixed top-0 end-0 p-3'; | |
| toastContainer.style.zIndex = '9999'; | |
| document.body.appendChild(toastContainer); | |
| } | |
| toastContainer.appendChild(toast); | |
| // Initialize and show toast | |
| const bsToast = new bootstrap.Toast(toast); | |
| bsToast.show(); | |
| // Remove from DOM after hiding | |
| toast.addEventListener('hidden.bs.toast', () => { | |
| toast.remove(); | |
| }); | |
| } | |
| /** | |
| * Copy text to clipboard | |
| */ | |
| async function copyToClipboard(text) { | |
| try { | |
| await navigator.clipboard.writeText(text); | |
| showToast('Copied to clipboard!', 'success'); | |
| } catch (err) { | |
| console.error('Failed to copy:', err); | |
| showToast('Failed to copy to clipboard', 'danger'); | |
| } | |
| } | |
| /** | |
| * Format time duration | |
| */ | |
| function formatDuration(minutes) { | |
| if (minutes < 60) { | |
| return `${minutes} min`; | |
| } else { | |
| const hours = Math.floor(minutes / 60); | |
| const remainingMinutes = minutes % 60; | |
| return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`; | |
| } | |
| } | |
| /** | |
| * Calculate reading time for content | |
| */ | |
| function calculateReadingTime(text, wordsPerMinute = 200) { | |
| const wordCount = text.split(/\s+/).length; | |
| const minutes = Math.ceil(wordCount / wordsPerMinute); | |
| return Math.max(1, minutes); | |
| } | |
| /** | |
| * Initialize progress bars with animation | |
| */ | |
| function animateProgressBar(progressBar, targetWidth) { | |
| let currentWidth = 0; | |
| const increment = targetWidth / 20; | |
| const interval = setInterval(() => { | |
| currentWidth += increment; | |
| if (currentWidth >= targetWidth) { | |
| currentWidth = targetWidth; | |
| clearInterval(interval); | |
| } | |
| progressBar.style.width = `${currentWidth}%`; | |
| }, 50); | |
| } | |
| // Export functions for use in other scripts | |
| window.DifyLearning = { | |
| trackEvent, | |
| showToast, | |
| copyToClipboard, | |
| formatDuration, | |
| calculateReadingTime, | |
| smoothScrollTo, | |
| showLoader, | |
| hideLoader, | |
| animateProgressBar | |
| }; | |