/** * ticker.js - A modular ticker component for displaying RSS feed content * Fetches and displays content from an RSS feed in a scrolling ticker at the bottom of the page */ class TickerManager { constructor(options = {}) { // Default options this.options = { elementId: 'ticker-container', scrollSpeed: 60, // Animation duration in seconds maxItems: 10, // Maximum number of items to display ...options }; // Create ticker container immediately this.createTickerContainer(); // State this.items = []; this.isScrolling = false; this.animationFrameId = null; // Initialize RSS manager this.rssManager = new RssManager(); // Subscribe to RSS updates this.rssManager.onUpdate(items => { this.items = items.slice(0, this.options.maxItems); this.updateTicker(); }); // Initialize this.init(); } /** * Create the ticker container */ createTickerContainer() { // Create container if it doesn't exist if (!document.getElementById(this.options.elementId)) { console.log('Creating ticker container'); const container = document.createElement('div'); container.id = this.options.elementId; container.className = 'ticker-container'; // Create ticker content const tickerContent = document.createElement('div'); tickerContent.className = 'ticker-content'; container.appendChild(tickerContent); // Add to document document.body.appendChild(container); // Add styles this.addTickerStyles(); } } /** * Initialize the ticker */ init() { console.log('Initializing TickerManager...'); try { // Set initial scroll speed this.setScrollSpeed(this.options.scrollSpeed); // Add initial loading message const tickerContent = document.querySelector(`#${this.options.elementId} .ticker-content`); if (tickerContent) { tickerContent.innerHTML = '
Loading news...
'; } // Ensure ticker is visible const container = document.getElementById(this.options.elementId); if (container) { container.style.display = 'block'; } console.log('TickerManager initialized successfully'); } catch (error) { console.error('Error initializing TickerManager:', error); } } /** * Add ticker styles */ addTickerStyles() { // Check if styles already exist if (!document.getElementById('ticker-styles')) { const styleElement = document.createElement('style'); styleElement.id = 'ticker-styles'; // Define styles styleElement.textContent = ` .ticker-container { position: fixed; bottom: 0; left: 0; width: 100%; background: linear-gradient(to right, #3c3b6e, #b22234, #ffffff); color: white; overflow: hidden; height: 40px; z-index: 100; box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.2); } body.vertical .ticker-container { transform: rotate(90deg); transform-origin: left bottom; width: 100vh; position: fixed; bottom: 0; left: 0; height: 40px; } body.vertical-reverse .ticker-container { transform: rotate(-90deg); transform-origin: right bottom; width: 100vh; position: fixed; bottom: 0; right: 0; height: 40px; } body.upsidedown .ticker-container { transform: rotate(180deg); } .ticker-content { display: flex; align-items: center; height: 100%; white-space: nowrap; position: absolute; left: 0; transform: translateX(100%); } .ticker-item { display: inline-block; padding: 0 30px; color: white; font-weight: bold; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); } .ticker-item:nth-child(3n+1) { background-color: rgba(178, 34, 52, 0.7); /* Red */ } .ticker-item:nth-child(3n+2) { background-color: rgba(255, 255, 255, 0.7); /* White */ color: #3c3b6e; /* Dark blue text for readability */ text-shadow: none; } .ticker-item:nth-child(3n+3) { background-color: rgba(60, 59, 110, 0.7); /* Blue */ } @keyframes ticker-scroll { 0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } } /* Animation direction for different orientations */ body.vertical .ticker-content { animation-direction: reverse; /* Reverse for vertical to maintain readability */ } body.upsidedown .ticker-content { animation-direction: reverse; /* Reverse for upside down to maintain readability */ } /* Dark mode styles */ body.dark-mode .ticker-container { background: linear-gradient(to right, #1a1a4f, #8b1a29, #e6e6e6); } body.dark-mode .ticker-item:nth-child(3n+1) { background-color: rgba(139, 26, 41, 0.7); /* Darker Red */ } body.dark-mode .ticker-item:nth-child(3n+2) { background-color: rgba(230, 230, 230, 0.7); /* Off-White */ color: #1a1a4f; /* Darker blue text */ } body.dark-mode .ticker-item:nth-child(3n+3) { background-color: rgba(26, 26, 79, 0.7); /* Darker Blue */ } `; // Add to document document.head.appendChild(styleElement); } } /** * Set the ticker scroll speed * @param {number} speed - Speed in seconds for one complete scroll cycle */ setScrollSpeed(speed) { this.options.scrollSpeed = speed; const container = document.getElementById(this.options.elementId); if (container) { // Reset animation by removing and re-adding content const content = container.querySelector('.ticker-content'); if (content) { const clone = content.cloneNode(true); container.style.setProperty('--ticker-speed', `${speed}s`); content.remove(); container.appendChild(clone); } console.log(`Ticker speed set to ${speed} seconds`); } } /** * Update ticker content */ updateTicker() { console.log('Updating ticker content...'); const tickerContent = document.querySelector(`#${this.options.elementId} .ticker-content`); if (tickerContent) { // Clear existing content tickerContent.innerHTML = ''; if (this.items.length === 0) { console.log('No items to display in ticker'); const tickerItem = document.createElement('div'); tickerItem.className = 'ticker-item'; tickerItem.textContent = 'Loading news...'; tickerContent.appendChild(tickerItem); return; } console.log(`Adding ${this.items.length} items to ticker`); // Add items this.items.forEach((item, index) => { const tickerItem = document.createElement('div'); tickerItem.className = 'ticker-item'; // Create link if available, using displayText or falling back to title if (item.link) { const link = document.createElement('a'); link.href = item.link; link.target = '_blank'; link.textContent = item.displayText || item.title; link.style.color = 'inherit'; link.style.textDecoration = 'none'; tickerItem.appendChild(link); } else { tickerItem.textContent = item.displayText || item.title; } tickerContent.appendChild(tickerItem); }); console.log('Ticker content updated successfully'); // Calculate total width of content const totalWidth = Array.from(tickerContent.children) .reduce((width, item) => width + item.offsetWidth, 0); // Calculate animation duration based on content width const duration = Math.max(totalWidth / 100, this.options.scrollSpeed); // Reset and start animation tickerContent.style.animation = 'none'; tickerContent.offsetHeight; // Force reflow tickerContent.style.animation = `ticker-scroll ${duration}s linear infinite`; console.log(`Animation duration set to ${duration}s based on content width ${totalWidth}px`); } else { console.error('Ticker content element not found'); } } } // Export the TickerManager class for use in other modules window.TickerManager = TickerManager;