298 lines
11 KiB
JavaScript
298 lines
11 KiB
JavaScript
/**
|
|
* 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 = '<div class="ticker-item">Loading news...</div>';
|
|
}
|
|
|
|
// 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;
|