DEV Community

Cover image for Scientific News Ticker with Juris
ArtyProg
ArtyProg

Posted on

Scientific News Ticker with Juris

Juris allows you to express all you creativity.
Here is an idea I had in ming a long time ago.
I finally realised it with Juris:

You can play with it here: NSTicker

Here is all the code :

// Enhanced News Ticker with Professional Features

// Configuration constants
const CONFIG = {
  API: {
    POLLINATIONS_BASE_URL: 'https://image.pollinations.ai/prompt',
    IMAGE_PARAMS: {
      nologo: true,
      enhance: true,
      width: 800,
      height: 400,
      model: 'flux'
    },
    RETRY_ATTEMPTS: 3,
    RETRY_DELAY: 1000,
    REQUEST_TIMEOUT: 10000
  },
  UI: {
    MIN_DISPLAY_TIME: 5000,
    DEFAULT_INTERVAL: 3000,
    FAST_INTERVAL: 1000,
    SLOW_INTERVAL: 5000,
    IMAGE_LOAD_TIMEOUT: 8000,
    ANIMATION_DURATION: 300
  },
  STORAGE_KEY: 'newsTickerPreferences'
};

// Enhanced error handling
class NewsTickerError extends Error {
  constructor(message, code, context = {}) {
    super(message);
    this.name = 'NewsTickerError';
    this.code = code;
    this.context = context;
    this.timestamp = new Date().toISOString();
  }
}

// Logger utility
const Logger = {
  levels: { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 },
  currentLevel: 2, // INFO level by default

  log(level, message, data = null) {
    if (this.levels[level] <= this.currentLevel) {
      const timestamp = new Date().toISOString();
      const logEntry = { timestamp, level, message, data };
      console[level.toLowerCase()](
        `[${timestamp}] ${level}: ${message}`,
        data || ''
      );
      this.storeLog(logEntry);
    }
  },

  error(message, data) { this.log('ERROR', message, data); },
  warn(message, data) { this.log('WARN', message, data); },
  info(message, data) { this.log('INFO', message, data); },
  debug(message, data) { this.log('DEBUG', message, data); },

  storeLog(entry) {
    // Store last 100 log entries for debugging
    const logs = JSON.parse(localStorage.getItem('newsTickerLogs') || '[]');
    logs.push(entry);
    if (logs.length > 100) logs.shift();
    localStorage.setItem('newsTickerLogs', JSON.stringify(logs));
  },

  exportLogs() {
    return JSON.parse(localStorage.getItem('newsTickerLogs') || '[]');
  }
};

// Utility functions
const Utils = {
  delay: (ms) => new Promise(resolve => setTimeout(resolve, ms)),

  async withTimeout(promise, timeoutMs, errorMessage = 'Operation timed out') {
    const timeout = new Promise((_, reject) =>
      setTimeout(() => reject(new NewsTickerError(errorMessage, 'TIMEOUT')), timeoutMs)
    );
    return Promise.race([promise, timeout]);
  },

  async retry(fn, attempts = 3, delay = 1000) {
    for (let i = 0; i < attempts; i++) {
      try {
        return await fn();
      } catch (error) {
        Logger.warn(`Attempt ${i + 1} failed`, { error: error.message });
        if (i === attempts - 1) throw error;
        await this.delay(delay * Math.pow(2, i)); // Exponential backoff
      }
    }
  },

  debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  },

  sanitizeHtml(str) {
    const div = document.createElement('div');
    div.textContent = str;
    return div.innerHTML;
  },

  formatDate(dateString) {
    try {
      return new Date(dateString).toLocaleDateString('en-US', {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      });
    } catch {
      return dateString; // Fallback to original string
    }
  }
};

// Enhanced news data with validation
const NewsData = {
  categories: {
    PHYSICS: 'Physics & Astronomy',
    BIOLOGY: 'Biology & Medicine', 
    CHEMISTRY: 'Chemistry',
    TECHNOLOGY: 'Computing & Technology',
    ENGINEERING: 'Engineering & Energy'
  },

  items: [
    // Physics & Astronomy
    { 
      id: 'moon-landing-1969',
      title: "The man walks on the moon", 
      author: "Neil Armstrong", 
      date: "1969-07-20",
      category: 'PHYSICS',
      description: "First human lunar landing mission",
      tags: ['space', 'exploration', 'NASA'],
      importance: 10
    },
    { 
      id: 'einstein-nobel-1921',
      title: "Albert won Nobel Prize", 
      author: "Albert Einstein", 
      date: "1921-11-09",
      category: 'PHYSICS',
      description: "Awarded for photoelectric effect discovery",
      tags: ['physics', 'quantum', 'nobel'],
      importance: 9
    },
    { 
      id: 'planck-radiation-1900',
      title: "Black body radiation explained", 
      author: "Max Planck", 
      date: "1900-12-14",
      category: 'PHYSICS',
      description: "Birth of quantum theory",
      tags: ['quantum', 'theory', 'physics'],
      importance: 9
    },
    { 
      id: 'einstein-relativity-1905',
      title: "Relativity changes our view of space-time", 
      author: "Albert Einstein", 
      date: "1905-09-26",
      category: 'PHYSICS',
      description: "Revolutionary theory of space and time",
      tags: ['relativity', 'physics', 'spacetime'],
      importance: 10
    },
    { 
      id: 'quantum-entanglement-1981',
      title: "Quantum entanglement proves Einstein wrong", 
      author: "Alain Aspect", 
      date: "1981-12-01",
      category: 'PHYSICS',
      description: "Experimental proof of quantum entanglement",
      tags: ['quantum', 'entanglement', 'experiment'],
      importance: 9
    },
    { 
      id: 'higgs-boson-2012',
      title: "Higgs boson particle finally detected", 
      author: "CERN", 
      date: "2012-07-04",
      category: 'PHYSICS',
      description: "Discovery of the Higgs boson at the Large Hadron Collider",
      tags: ['particle', 'physics', 'CERN', 'higgs'],
      importance: 10
    },
    { 
      id: 'gravitational-waves-2015',
      title: "Gravitational waves confirmed Einstein's theory", 
      author: "LIGO Team", 
      date: "2015-09-14",
      category: 'PHYSICS',
      description: "First direct detection of gravitational waves",
      tags: ['gravity', 'waves', 'LIGO', 'Einstein'],
      importance: 10
    },
    { 
      id: 'sputnik-1957',
      title: "First artificial satellite launched", 
      author: "Sputnik 1", 
      date: "1957-10-04",
      category: 'PHYSICS',
      description: "Soviet Union launches first artificial satellite",
      tags: ['satellite', 'space', 'soviet', 'sputnik'],
      importance: 9
    },
    { 
      id: 'gagarin-space-1961',
      title: "First human in space", 
      author: "Yuri Gagarin", 
      date: "1961-04-12",
      category: 'PHYSICS',
      description: "First human orbital spaceflight",
      tags: ['space', 'human', 'orbit', 'soviet'],
      importance: 10
    },
    { 
      id: 'black-hole-photo-2019',
      title: "First photograph of a black hole", 
      author: "Event Horizon Telescope", 
      date: "2019-04-10",
      category: 'PHYSICS',
      description: "First direct image of a black hole's event horizon",
      tags: ['black hole', 'telescope', 'astronomy'],
      importance: 9
    },
    { 
      id: 'mars-curiosity-2012',
      title: "Mars rover Curiosity lands successfully", 
      author: "NASA JPL", 
      date: "2012-08-05",
      category: 'PHYSICS',
      description: "Successful landing of Curiosity rover on Mars",
      tags: ['mars', 'rover', 'NASA', 'exploration'],
      importance: 8
    },
    { 
      id: 'james-webb-2021',
      title: "James Webb Space Telescope launched", 
      author: "NASA & ESA", 
      date: "2021-12-25",
      category: 'PHYSICS',
      description: "Launch of the most powerful space telescope",
      tags: ['telescope', 'space', 'infrared', 'astronomy'],
      importance: 9
    },
    { 
      id: 'exoplanet-1995',
      title: "First exoplanet discovered", 
      author: "Michel Mayor & Didier Queloz", 
      date: "1995-10-06",
      category: 'PHYSICS',
      description: "Discovery of 51 Eridani b, first exoplanet around sun-like star",
      tags: ['exoplanet', 'discovery', 'astronomy'],
      importance: 9
    },
    { 
      id: 'plate-tectonics-1912',
      title: "Plate tectonics explains continental drift", 
      author: "Alfred Wegener", 
      date: "1912-01-06",
      category: 'PHYSICS',
      description: "Theory explaining continental drift through plate movement",
      tags: ['geology', 'tectonics', 'earth', 'continental drift'],
      importance: 8
    },
    { 
      id: 'big-bang-theory-1927',
      title: "Big Bang theory proposed", 
      author: "Georges Lemaître", 
      date: "1927-05-01",
      category: 'PHYSICS',
      description: "Theoretical foundation for the expansion of the universe",
      tags: ['cosmology', 'universe', 'big bang', 'theory'],
      importance: 10
    },
    { 
      id: 'pulsars-discovery-1967',
      title: "Discovery of pulsars", 
      author: "Jocelyn Bell Burnell", 
      date: "1967-11-28",
      category: 'PHYSICS',
      description: "Discovery of rapidly rotating neutron stars",
      tags: ['pulsar', 'neutron star', 'astronomy', 'radio'],
      importance: 8
    },
    { 
      id: 'cosmic-microwave-background-1964',
      title: "Discovery of cosmic microwave background radiation", 
      author: "Penzias & Wilson", 
      date: "1964-05-01",
      category: 'PHYSICS',
      description: "Discovery of relic radiation from the Big Bang",
      tags: ['CMB', 'radiation', 'big bang', 'cosmology'],
      importance: 9
    },
    { 
      id: 'space-shuttle-1981',
      title: "First reusable spacecraft launched", 
      author: "Space Shuttle Columbia", 
      date: "1981-04-12",
      category: 'PHYSICS',
      description: "First launch of NASA's Space Shuttle program",
      tags: ['shuttle', 'reusable', 'NASA', 'Columbia'],
      importance: 8
    },
    { 
      id: 'voyager-interstellar-2012',
      title: "Voyager probe enters interstellar space", 
      author: "NASA", 
      date: "2012-08-25",
      category: 'PHYSICS',
      description: "Voyager 1 becomes first human-made object to leave solar system",
      tags: ['voyager', 'interstellar', 'NASA', 'exploration'],
      importance: 8
    },

    // Biology & Medicine
    { 
      id: 'dna-double-helix-1953',
      title: "DNA double helix structure discovered", 
      author: "Watson & Crick", 
      date: "1953-04-25",
      category: 'BIOLOGY',
      description: "Discovery of the double helix structure of DNA",
      tags: ['DNA', 'genetics', 'structure', 'molecular biology'],
      importance: 10
    },
    { 
      id: 'dna-photo-1952',
      title: "First picture of DNA captured", 
      author: "Rosalind Franklin", 
      date: "1952-05-01",
      category: 'BIOLOGY',
      description: "X-ray crystallography image of DNA structure (Photo 51)",
      tags: ['DNA', 'crystallography', 'structure', 'photography'],
      importance: 9
    },
    { 
      id: 'penicillin-1928',
      title: "Penicillin saves millions of lives", 
      author: "Alexander Fleming", 
      date: "1928-09-28",
      category: 'BIOLOGY',
      description: "Discovery of the first widely used antibiotic",
      tags: ['antibiotic', 'medicine', 'bacteria', 'treatment'],
      importance: 10
    },
    { 
      id: 'vaccines-1885',
      title: "Vaccines eliminate deadly diseases", 
      author: "Louis Pasteur", 
      date: "1885-07-06",
      category: 'BIOLOGY',
      description: "Development of rabies vaccine and vaccination principles",
      tags: ['vaccine', 'immunization', 'disease', 'prevention'],
      importance: 10
    },
    { 
      id: 'evolution-1859',
      title: "Evolution explains diversity of life", 
      author: "Charles Darwin", 
      date: "1859-11-24",
      category: 'BIOLOGY',
      description: "Publication of 'On the Origin of Species'",
      tags: ['evolution', 'natural selection', 'biology', 'species'],
      importance: 10
    },
    { 
      id: 'human-genome-2003',
      title: "Human genome completely sequenced", 
      author: "Human Genome Project", 
      date: "2003-04-14",
      category: 'BIOLOGY',
      description: "Complete sequencing of human DNA",
      tags: ['genome', 'sequencing', 'genetics', 'DNA'],
      importance: 10
    },
    { 
      id: 'crispr-2012',
      title: "CRISPR gene editing revolutionizes medicine", 
      author: "Jennifer Doudna", 
      date: "2012-06-28",
      category: 'BIOLOGY',
      description: "Development of precise gene editing technology",
      tags: ['CRISPR', 'gene editing', 'biotechnology', 'medicine'],
      importance: 10
    },
    { 
      id: 'heart-transplant-1967',
      title: "First heart transplant performed", 
      author: "Christiaan Barnard", 
      date: "1967-12-03",
      category: 'BIOLOGY',
      description: "First successful human heart transplantation",
      tags: ['transplant', 'surgery', 'heart', 'medicine'],
      importance: 9
    },
    { 
      id: 'kidney-transplant-1954',
      title: "First successful kidney transplant", 
      author: "Joseph Murray", 
      date: "1954-12-23",
      category: 'BIOLOGY',
      description: "First successful organ transplant between identical twins",
      tags: ['transplant', 'kidney', 'surgery', 'organ'],
      importance: 8
    },
    { 
      id: 'insulin-discovery-1921',
      title: "Insulin discovered for diabetes treatment", 
      author: "Frederick Banting", 
      date: "1921-07-30",
      category: 'BIOLOGY',
      description: "Discovery of insulin as treatment for diabetes",
      tags: ['insulin', 'diabetes', 'hormone', 'treatment'],
      importance: 9
    },
    { 
      id: 'dolly-cloning-1996',
      title: "First cloning of a mammal (Dolly the sheep)", 
      author: "Ian Wilmut", 
      date: "1996-07-05",
      category: 'BIOLOGY',
      description: "First successful cloning of a mammal from adult cell",
      tags: ['cloning', 'genetics', 'biotechnology', 'sheep'],
      importance: 9
    },
    { 
      id: 'stem-cells-1961',
      title: "Stem cells discovered", 
      author: "James Till & Ernest McCulloch", 
      date: "1961-02-01",
      category: 'BIOLOGY',
      description: "Discovery of hematopoietic stem cells",
      tags: ['stem cells', 'regenerative medicine', 'biology', 'cells'],
      importance: 9
    },
    { 
      id: 'ivf-baby-1978',
      title: "First IVF baby born", 
      author: "Louise Brown", 
      date: "1978-07-25",
      category: 'BIOLOGY',
      description: "Birth of first baby conceived through in vitro fertilization",
      tags: ['IVF', 'fertility', 'reproduction', 'medicine'],
      importance: 8
    },
    { 
      id: 'face-transplant-2010',
      title: "First complete face transplant performed", 
      author: "Spanish surgeons", 
      date: "2010-03-20",
      category: 'BIOLOGY',
      description: "Full facial transplantation surgery breakthrough",
      tags: ['transplant', 'surgery', 'face', 'reconstructive'],
      importance: 7
    },
    { 
      id: 'mrna-vaccines-2005',
      title: "Messenger RNA vaccines pioneered", 
      author: "Katalin Karikó", 
      date: "2005-08-01",
      category: 'BIOLOGY',
      description: "Development of mRNA vaccine technology",
      tags: ['mRNA', 'vaccine', 'biotechnology', 'immunology'],
      importance: 9
    },
    { 
      id: 'covid-vaccine-2020',
      title: "First COVID-19 vaccine developed", 
      author: "Pfizer-BioNTech", 
      date: "2020-12-11",
      category: 'BIOLOGY',
      description: "First authorized COVID-19 vaccine using mRNA technology",
      tags: ['COVID-19', 'vaccine', 'pandemic', 'mRNA'],
      importance: 9
    },
    { 
      id: 'cancer-immunotherapy-2018',
      title: "Cancer immunotherapy breakthrough", 
      author: "James Allison", 
      date: "2018-10-01",
      category: 'BIOLOGY',
      description: "Nobel Prize for cancer immunotherapy research",
      tags: ['cancer', 'immunotherapy', 'treatment', 'oncology'],
      importance: 9
    },
    { 
      id: 'gene-therapy-2017',
      title: "First gene therapy approved", 
      author: "FDA", 
      date: "2017-08-30",
      category: 'BIOLOGY',
      description: "FDA approval of first gene therapy treatment",
      tags: ['gene therapy', 'treatment', 'genetics', 'medicine'],
      importance: 8
    },

    // Chemistry
    { 
      id: 'periodic-table-1869',
      title: "Periodic table created", 
      author: "Dmitri Mendeleev", 
      date: "1869-03-06",
      category: 'CHEMISTRY',
      description: "Organization of chemical elements by atomic properties",
      tags: ['periodic table', 'elements', 'chemistry', 'atomic'],
      importance: 10
    },
    { 
      id: 'radioactivity-becquerel-1896',
      title: "Radioactivity discovered", 
      author: "Henri Becquerel", 
      date: "1896-03-01",
      category: 'CHEMISTRY',
      description: "Discovery of natural radioactivity in uranium",
      tags: ['radioactivity', 'uranium', 'physics', 'nuclear'],
      importance: 9
    },
    { 
      id: 'radioactivity-curie-1898',
      title: "Radioactivity studied by Marie Curie", 
      author: "Marie Curie", 
      date: "1898-07-01",
      category: 'CHEMISTRY',
      description: "Isolation and study of radium and polonium",
      tags: ['radioactivity', 'radium', 'polonium', 'nuclear'],
      importance: 9
    },
    { 
      id: 'benzene-structure-1865',
      title: "Structure of benzene discovered", 
      author: "Kekulé", 
      date: "1865-01-01",
      category: 'CHEMISTRY',
      description: "Discovery of the ring structure of benzene",
      tags: ['benzene', 'organic chemistry', 'molecular structure'],
      importance: 8
    },
    { 
      id: 'teflon-discovery-1938',
      title: "Discovery of Teflon", 
      author: "Roy Plunkett", 
      date: "1938-04-06",
      category: 'CHEMISTRY',
      description: "Accidental discovery of polytetrafluoroethylene",
      tags: ['teflon', 'polymer', 'materials', 'chemistry'],
      importance: 7
    },
    { 
      id: 'plastic-invention-1907',
      title: "Plastic invented", 
      author: "Leo Baekeland", 
      date: "1907-01-01",
      category: 'CHEMISTRY',
      description: "Invention of Bakelite, the first synthetic plastic",
      tags: ['plastic', 'polymer', 'synthetic', 'materials'],
      importance: 8
    },
    { 
      id: 'superconductivity-1911',
      title: "Discovery of superconductivity", 
      author: "Heike Kamerlingh Onnes", 
      date: "1911-04-08",
      category: 'CHEMISTRY',
      description: "Discovery of zero electrical resistance in mercury",
      tags: ['superconductivity', 'physics', 'electrical', 'materials'],
      importance: 9
    },

    // Computing & Technology
    { 
      id: 'transistor-1947',
      title: "Transistor invention launches digital age", 
      author: "Bell Labs", 
      date: "1947-12-23",
      category: 'TECHNOLOGY',
      description: "Invention of the point-contact transistor",
      tags: ['transistor', 'electronics', 'computing', 'semiconductor'],
      importance: 10
    },
    { 
      id: 'first-computer-1941',
      title: "First programmable computer unveiled", 
      author: "Konrad Zuse", 
      date: "1941-05-12",
      category: 'TECHNOLOGY',
      description: "Z3 computer, first programmable digital computer",
      tags: ['computer', 'programming', 'digital', 'computing'],
      importance: 9
    },
    { 
      id: 'tcp-ip-1974',
      title: "Internet protocol TCP/IP standardized", 
      author: "Vint Cerf & Bob Kahn", 
      date: "1974-12-01",
      category: 'TECHNOLOGY',
      description: "Development of fundamental internet protocols",
      tags: ['internet', 'protocol', 'networking', 'TCP/IP'],
      importance: 10
    },
    { 
      id: 'world-wide-web-1989',
      title: "World Wide Web invented", 
      author: "Tim Berners-Lee", 
      date: "1989-03-12",
      category: 'TECHNOLOGY',
      description: "Creation of the World Wide Web at CERN",
      tags: ['web', 'internet', 'hypertext', 'browser'],
      importance: 10
    },
    { 
      id: 'first-email-1971',
      title: "First email sent", 
      author: "Ray Tomlinson", 
      date: "1971-10-01",
      category: 'TECHNOLOGY',
      description: "First network email using @ symbol",
      tags: ['email', 'communication', 'networking', 'ARPANET'],
      importance: 8
    },
    { 
      id: 'microprocessor-1971',
      title: "First microprocessor released", 
      author: "Intel 4004", 
      date: "1971-11-15",
      category: 'TECHNOLOGY',
      description: "Intel 4004, first commercial microprocessor",
      tags: ['microprocessor', 'Intel', 'computing', 'chip'],
      importance: 9
    },
    { 
      id: 'web-browser-1993',
      title: "First graphical web browser released", 
      author: "Mosaic", 
      date: "1993-04-22",
      category: 'TECHNOLOGY',
      description: "Mosaic browser popularizes the World Wide Web",
      tags: ['browser', 'web', 'graphical', 'internet'],
      importance: 8
    },
    { 
      id: 'smartphone-1992',
      title: "First smartphone introduced", 
      author: "IBM Simon", 
      date: "1992-08-16",
      category: 'TECHNOLOGY',
      description: "IBM Simon Personal Communicator, first smartphone",
      tags: ['smartphone', 'mobile', 'communication', 'PDA'],
      importance: 8
    },
    { 
      id: 'google-founded-1998',
      title: "Google founded", 
      author: "Larry Page & Sergey Brin", 
      date: "1998-09-04",
      category: 'TECHNOLOGY',
      description: "Founding of Google search engine company",
      tags: ['Google', 'search', 'internet', 'algorithm'],
      importance: 9
    },
    { 
      id: 'iphone-2007',
      title: "Apple introduces iPhone", 
      author: "Steve Jobs", 
      date: "2007-01-09",
      category: 'TECHNOLOGY',
      description: "Launch of revolutionary touchscreen smartphone",
      tags: ['iPhone', 'Apple', 'smartphone', 'touchscreen'],
      importance: 9
    },
    { 
      id: 'deep-blue-chess-1997',
      title: "First AI beats chess world champion", 
      author: "IBM Deep Blue", 
      date: "1997-05-11",
      category: 'TECHNOLOGY',
      description: "Deep Blue defeats Garry Kasparov in chess match",
      tags: ['AI', 'chess', 'IBM', 'artificial intelligence'],
      importance: 8
    },
    { 
      id: 'alphago-2016',
      title: "First AI beats Go world champion", 
      author: "DeepMind AlphaGo", 
      date: "2016-03-15",
      category: 'TECHNOLOGY',
      description: "AlphaGo defeats Lee Sedol in Go tournament",
      tags: ['AI', 'Go', 'DeepMind', 'machine learning'],
      importance: 9
    },
    { 
      id: 'chatgpt-2022',
      title: "ChatGPT released to public", 
      author: "OpenAI", 
      date: "2022-11-30",
      category: 'TECHNOLOGY',
      description: "Public release of conversational AI chatbot",
      tags: ['AI', 'chatbot', 'GPT', 'natural language'],
      importance: 9
    },
    { 
      id: 'bitcoin-2009',
      title: "Bitcoin blockchain launched", 
      author: "Satoshi Nakamoto", 
      date: "2009-01-03",
      category: 'TECHNOLOGY',
      description: "Launch of first decentralized cryptocurrency",
      tags: ['Bitcoin', 'blockchain', 'cryptocurrency', 'digital currency'],
      importance: 8
    },
    { 
      id: '3d-printer-1984',
      title: "First 3D printer invented", 
      author: "Chuck Hull", 
      date: "1984-03-09",
      category: 'TECHNOLOGY',
      description: "Invention of stereolithography 3D printing",
      tags: ['3D printing', 'manufacturing', 'prototyping', 'technology'],
      importance: 8
    },
    { 
      id: 'robot-surgery-2000',
      title: "First robot-assisted surgery performed", 
      author: "da Vinci System", 
      date: "2000-07-11",
      category: 'TECHNOLOGY',
      description: "First surgery using da Vinci robotic system",
      tags: ['robotics', 'surgery', 'medical technology', 'automation'],
      importance: 7
    },
    { 
      id: 'self-driving-car-2005',
      title: "First self-driving car tested", 
      author: "DARPA", 
      date: "2005-10-08",
      category: 'TECHNOLOGY',
      description: "DARPA Grand Challenge autonomous vehicle competition",
      tags: ['autonomous', 'self-driving', 'automotive', 'AI'],
      importance: 8
    },
    { 
      id: 'quantum-supremacy-2019',
      title: "Quantum computer reaches supremacy", 
      author: "Google", 
      date: "2019-10-23",
      category: 'TECHNOLOGY',
      description: "Google's quantum computer outperforms classical computers",
      tags: ['quantum computing', 'supremacy', 'computing', 'quantum'],
      importance: 9
    },

    // Engineering & Energy
    { 
      id: 'nuclear-power-1954',
      title: "First nuclear power plant opens", 
      author: "Obninsk, USSR", 
      date: "1954-06-27",
      category: 'ENGINEERING',
      description: "First commercial nuclear power plant begins operation",
      tags: ['nuclear', 'power', 'energy', 'electricity'],
      importance: 9
    },
    { 
      id: 'wright-brothers-1903',
      title: "First flight of the Wright brothers", 
      author: "Wright Brothers", 
      date: "1903-12-17",
      category: 'ENGINEERING',
      description: "First powered, controlled, sustained heavier-than-air flight",
      tags: ['flight', 'aviation', 'airplane', 'Wright brothers'],
      importance: 10
    },
    { 
      id: 'jet-airliner-1952',
      title: "First commercial jet airliner", 
      author: "de Havilland Comet", 
      date: "1952-05-02",
      category: 'ENGINEERING',
      description: "First commercial jet passenger service begins",
      tags: ['jet', 'aviation', 'commercial', 'passenger'],
      importance: 8
    },
    { 
      id: 'supersonic-flight-1969',
      title: "First supersonic passenger flight", 
      author: "Concorde", 
      date: "1969-03-02",
      category: 'ENGINEERING',
      description: "Concorde's first test flight breaks sound barrier",
      tags: ['supersonic', 'Concorde', 'aviation', 'passenger'],
      importance: 7
    },
    { 
      id: 'panama-canal-1914',
      title: "Panama Canal completed", 
      author: "US Engineers", 
      date: "1914-08-15",
      category: 'ENGINEERING',
      description: "Completion of Panama Canal connecting Atlantic and Pacific",
      tags: ['canal', 'engineering', 'transportation', 'shipping'],
      importance: 8
    },
    { 
      id: 'first-skyscraper-1885',
      title: "First skyscraper built", 
      author: "Chicago Home Insurance Building", 
      date: "1885-01-01",
      category: 'ENGINEERING',
      description: "First steel-frame skyscraper construction",
      tags: ['skyscraper', 'construction', 'architecture', 'steel'],
      importance: 7
    },
    { 
      id: 'photovoltaic-cell-1954',
      title: "First photovoltaic cell invented", 
      author: "Bell Labs", 
      date: "1954-04-25",
      category: 'ENGINEERING',
      description: "First practical silicon solar cell developed",
      tags: ['solar', 'photovoltaic', 'renewable energy', 'silicon'],
      importance: 8
    },
    { 
      id: 'electric-battery-1800',
      title: "First electric battery invented", 
      author: "Alessandro Volta", 
      date: "1800-03-20",
      category: 'ENGINEERING',
      description: "Invention of the voltaic pile, first electric battery",
      tags: ['battery', 'electricity', 'energy storage', 'volta'],
      importance: 9
    },
    { 
      id: 'electromagnetic-induction-1831',
      title: "Electricity powers the modern world", 
      author: "Michael Faraday", 
      date: "1831-08-29",
      category: 'ENGINEERING',
      description: "Discovery of electromagnetic induction principles",
      tags: ['electricity', 'electromagnetic', 'induction', 'power'],
      importance: 10
    }
  ],

  validate(item) {
    const required = ['id', 'title', 'author', 'date', 'category'];
    const missing = required.filter(field => !item[field]);
    if (missing.length > 0) {
      throw new NewsTickerError(`Missing required fields: ${missing.join(', ')}`, 'VALIDATION_ERROR');
    }
    return true;
  },

  getByCategory(category) {
    return this.items.filter(item => item.category === category);
  },

  getByImportance(minImportance = 5) {
    return this.items.filter(item => item.importance >= minImportance);
  },

  search(query) {
    const lowerQuery = query.toLowerCase();
    return this.items.filter(item => 
      item.title.toLowerCase().includes(lowerQuery) ||
      item.author.toLowerCase().includes(lowerQuery) ||
      item.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
    );
  }
};

// Enhanced image service with caching and error handling
const ImageService = {
  cache: new Map(),
  loadingPromises: new Map(),

  async fetchImage(prompt, options = {}) {
    const cacheKey = `${prompt}_${JSON.stringify(options)}`;

    // Return cached image if available
    if (this.cache.has(cacheKey)) {
      Logger.debug('Image cache hit', { prompt });
      return this.cache.get(cacheKey);
    }

    // Return existing promise if already loading
    if (this.loadingPromises.has(cacheKey)) {
      Logger.debug('Image already loading', { prompt });
      return this.loadingPromises.get(cacheKey);
    }

    const loadingPromise = this._loadImage(prompt, options, cacheKey);
    this.loadingPromises.set(cacheKey, loadingPromise);

    try {
      const result = await loadingPromise;
      this.cache.set(cacheKey, result);
      return result;
    } finally {
      this.loadingPromises.delete(cacheKey);
    }
  },

  async _loadImage(prompt, options, cacheKey) {
    const params = { ...CONFIG.API.IMAGE_PARAMS, ...options };
    const queryString = new URLSearchParams(params).toString();
    const imageUrl = `${CONFIG.API.POLLINATIONS_BASE_URL}/${encodeURIComponent(prompt)}?${queryString}`;

    return Utils.withTimeout(
      this._preloadImage(imageUrl),
      CONFIG.UI.IMAGE_LOAD_TIMEOUT,
      'Image loading timeout'
    );
  },

  _preloadImage(url) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.crossOrigin = 'anonymous';

      img.onload = () => {
        Logger.debug('Image loaded successfully', { url });
        resolve({
          url,
          width: img.naturalWidth,
          height: img.naturalHeight,
          loaded: true
        });
      };

      img.onerror = (error) => {
        Logger.error('Image failed to load', { url, error });
        reject(new NewsTickerError('Failed to load image', 'IMAGE_LOAD_ERROR', { url }));
      };

      img.src = url;
    });
  },

  clearCache() {
    this.cache.clear();
    this.loadingPromises.clear();
    Logger.info('Image cache cleared');
  },

  getCacheStats() {
    return {
      cacheSize: this.cache.size,
      loadingCount: this.loadingPromises.size,
      cacheEntries: Array.from(this.cache.keys())
    };
  }
};

// User preferences management
const PreferencesManager = {
  defaults: {
    updateInterval: CONFIG.UI.DEFAULT_INTERVAL,
    autoStart: true,
    categories: Object.keys(NewsData.categories),
    theme: 'auto',
    soundEnabled: false,
    minImportance: 5
  },

  load() {
    try {
      const stored = localStorage.getItem(CONFIG.STORAGE_KEY);
      return stored ? { ...this.defaults, ...JSON.parse(stored) } : this.defaults;
    } catch (error) {
      Logger.error('Failed to load preferences', error);
      return this.defaults;
    }
  },

  save(preferences) {
    try {
      localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(preferences));
      Logger.info('Preferences saved', preferences);
    } catch (error) {
      Logger.error('Failed to save preferences', error);
    }
  },

  update(updates) {
    const current = this.load();
    const updated = { ...current, ...updates };
    this.save(updated);
    return updated;
  }
};

// Enhanced NewsManager with better architecture
const NewsManager = (props, ctx) => {
  let intervalId = null;
  let currentNewsIndex = 0;
  let filteredNews = [];
  let isDestroyed = false;

  const preferences = PreferencesManager.load();

  // Initialize filtered news based on preferences
  const updateFilteredNews = () => {
    filteredNews = NewsData.items.filter(item => 
      preferences.categories.includes(item.category) &&
      item.importance >= preferences.minImportance
    );

    if (filteredNews.length === 0) {
      Logger.warn('No news items match current filters');
      filteredNews = NewsData.items; // Fallback to all news
    }

    Logger.info(`Filtered news updated: ${filteredNews.length} items`);
  };

  const fetchRandomNews = async () => {
    if (filteredNews.length === 0) updateFilteredNews();

    const randomIndex = Math.floor(Math.random() * filteredNews.length);
    const newsItem = filteredNews[randomIndex];

    try {
      NewsData.validate(newsItem);
      return newsItem;
    } catch (error) {
      Logger.error('Invalid news item', { newsItem, error });
      throw error;
    }
  };

  const updateNews = async () => {
    if (isDestroyed) return;

    const startTime = Date.now();
    ctx.setState('loading', true);

    try {
      const news = await Utils.retry(fetchRandomNews, 3);
      Logger.info('Fetched news item', { title: news.title, id: news.id });

      // Generate image prompt based on news content
      const imagePrompt = `${news.title} ${news.description || ''} scientific illustration`;

      const imageResult = await Utils.retry(
        () => ImageService.fetchImage(imagePrompt),
        CONFIG.API.RETRY_ATTEMPTS
      );

      // Enforce minimum display time
      const elapsed = Date.now() - startTime;
      const remaining = CONFIG.UI.MIN_DISPLAY_TIME - elapsed;
      if (remaining > 0) {
        await Utils.delay(remaining);
      }

      if (!isDestroyed) {
        ctx.setState('currentNews', { 
          ...news, 
          imageUrl: imageResult.url,
          imageMetadata: {
            width: imageResult.width,
            height: imageResult.height,
            loadTime: Date.now() - startTime
          }
        });
        ctx.setState('lastUpdated', new Date().toLocaleTimeString());
        ctx.setState('error', null);

        Logger.info('News updated successfully', { newsId: news.id });
      }

    } catch (error) {
      Logger.error('Failed to update news', error);
      ctx.setState('error', {
        message: error.message,
        code: error.code,
        timestamp: new Date().toISOString()
      });
    } finally {
      if (!isDestroyed) {
        ctx.setState('loading', false);
      }
    }
  };

  // Cleanup function
  const cleanup = () => {
    if (intervalId) {
      clearInterval(intervalId);
      intervalId = null;
    }
    isDestroyed = true;
    Logger.info('NewsManager cleaned up');
  };

  // Initialize
  updateFilteredNews();

  return {
    api: {
      async startPeriodicUpdates(intervalMs = preferences.updateInterval) {
        if (intervalId) this.stopPeriodicUpdates();

        ctx.setState('isUpdating', true);
        ctx.setState('updateInterval', intervalMs);
        Logger.info('Starting periodic updates', { intervalMs });

        // Initial update
        await updateNews();

        if (!isDestroyed) {
          intervalId = setInterval(async () => {
            if (!ctx.getState('isUpdating') || isDestroyed) {
              this.stopPeriodicUpdates();
              return;
            }
            await updateNews();
          }, intervalMs);
        }
      },

      stopPeriodicUpdates() {
        if (intervalId) {
          clearInterval(intervalId);
          intervalId = null;
          Logger.info('Stopped periodic updates');
        }
        ctx.setState('isUpdating', false);
      },

      async fetchRandomNews() {
        Logger.info('Manual news fetch requested');
        await updateNews();
      },

      updatePreferences(newPrefs) {
        Object.assign(preferences, newPrefs);
        PreferencesManager.save(preferences);
        updateFilteredNews();
        Logger.info('Preferences updated', newPrefs);
      },

      getStats() {
        return {
          totalNews: NewsData.items.length,
          filteredNews: filteredNews.length,
          imageCache: ImageService.getCacheStats(),
          preferences,
          isRunning: !!intervalId
        };
      },

      exportData() {
        return {
          currentNews: ctx.getState('currentNews'),
          preferences,
          stats: this.getStats(),
          logs: Logger.exportLogs().slice(-20) // Last 20 logs
        };
      },

      cleanup
    }
  };
};

// Enhanced Banner component with error handling
const Banner = (props, ctx) => ({
  div: {
    className: 'news-banner gradient-border',
    children: [
      // Error display
      ...(ctx.getState('error') ? [{
        div: {
          className: 'error-banner bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4',
          children: [
            { 
              span: { 
                className: 'font-bold',
                text: 'Error: '
              }
            },
            { 
              span: { 
                text: () => ctx.getState('error')?.message || 'Unknown error occurred'
              }
            }
          ]
        }
      }] : []),

      // Loading indicator
      ...(ctx.getState('loading') ? [{
        div: {
          className: 'loading-indicator flex items-center justify-center py-4',
          children: [
            {
              div: {
                className: 'animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600 mr-3'
              }
            },
            { span: { text: 'Loading news...', className: 'text-gray-600' } }
          ]
        }
      }] : []),

      // Main content
      {
        h1: {
          className: 'news-title',
          children: [
            {
              span: {
                text: () => {
                  const news = ctx.getState('currentNews');
                  return news ? Utils.sanitizeHtml(news.title) : 'Loading news...';
                }
              }
            }
          ]
        }
      },

      {
        img: {
          src: () => ctx.getState('currentNews')?.imageUrl || '',
          className: 'news-image',
          alt: () => ctx.getState('currentNews')?.title || 'News image',
          loading: 'lazy',
          onError: () => {
            Logger.error('Image failed to display');
            ctx.setState('imageError', true);
          }
        }
      },

      {
        div: {
          className: 'news-meta text-sm text-gray-600 mb-4',
          children: [
            {
              span: {
                className: 'author font-medium',
                text: () => {
                  const news = ctx.getState('currentNews');
                  return news ? `By ${news.author}` : '';
                }
              }
            },
            {
              span: {
                className: 'date ml-2',
                text: () => {
                  const news = ctx.getState('currentNews');
                  return news ? `• ${Utils.formatDate(news.date)}` : '';
                }
              }
            },
            {
              span: {
                className: 'category ml-2 inline-block bg-gray-200 rounded-full px-2 py-1 text-xs',
                text: () => {
                  const news = ctx.getState('currentNews');
                  return news ? NewsData.categories[news.category] : '';
                }
              }
            }
          ]
        }
      },

      {
        div: {
          className: 'info-bar',
          children: [
            {
              div: {
                className: () => `status-indicator ${ctx.getState('isUpdating') ? 'status-live' : 'status-paused'}`
              }
            },
            { 
              span: { 
                text: () => ctx.getState('isUpdating') ? 'LIVE' : 'PAUSED',
                className: 'font-semibold'
              }
            },
            { 
              span: { 
                text: () => {
                  const lastUpdated = ctx.getState('lastUpdated');
                  const interval = ctx.getState('updateInterval');
                  return lastUpdated ? `• ${lastUpdated}${interval ? ` (${interval/1000}s)` : ''}` : '';
                }
              }
            }
          ]
        }
      }
    ]
  }
});

// Enhanced Controls with more options
const Controls = (props, ctx) => {
  const { NewsManager } = ctx;

  return {
    div: {
      className: 'controls-container',
      children: [
        // Primary controls
        {
          div: {
            className: 'primary-controls flex gap-3 mb-4',
            children: [
              {
                button: {
                  className: 'btn-primary',
                  text: () => ctx.getState('isUpdating') ? 'Stop Updates' : 'Start Updates',
                  onclick: () => {
                    if (ctx.getState('isUpdating')) {
                      NewsManager.stopPeriodicUpdates();
                    } else {
                      const preferences = PreferencesManager.load();
                      NewsManager.startPeriodicUpdates(preferences.updateInterval);
                    }
                  },
                  disabled: () => ctx.getState('loading')
                }
              },
              {
                button: {
                  className: 'btn-secondary',
                  text: 'Random News',
                  onclick: () => NewsManager.fetchRandomNews(),
                  disabled: () => ctx.getState('loading')
                }
              }
            ]
          }
        },

        // Speed controls
        {
          div: {
            className: 'speed-controls flex gap-2 mb-4',
            children: [
              {
                button: {
                  className: 'btn-tertiary text-xs',
                  text: 'Fast (1s)',
                  onclick: () => {
                    if (ctx.getState('isUpdating')) {
                      NewsManager.stopPeriodicUpdates();
                      NewsManager.startPeriodicUpdates(CONFIG.UI.FAST_INTERVAL);
                    }
                  }
                }
              },
              {
                button: {
                  className: 'btn-tertiary text-xs',
                  text: 'Normal (3s)',
                  onclick: () => {
                    if (ctx.getState('isUpdating')) {
                      NewsManager.stopPeriodicUpdates();
                      NewsManager.startPeriodicUpdates(CONFIG.UI.DEFAULT_INTERVAL);
                    }
                  }
                }
              },
              {
                button: {
                  className: 'btn-tertiary text-xs',
                  text: 'Slow (5s)',
                  onclick: () => {
                    if (ctx.getState('isUpdating')) {
                      NewsManager.stopPeriodicUpdates();
                      NewsManager.startPeriodicUpdates(CONFIG.UI.SLOW_INTERVAL);
                    }
                  }
                }
              }
            ]
          }
        },

        // Utility controls
        {
          div: {
            className: 'utility-controls flex gap-2 text-xs',
            children: [
              {
                button: {
                  className: 'btn-tertiary',
                  text: 'Clear Cache',
                  onclick: () => {
                    ImageService.clearCache();
                    Logger.info('Cache cleared by user');
                  }
                }
              },
              {
                button: {
                  className: 'btn-tertiary',
                  text: 'Export Data',
                  onclick: () => {
                    const data = NewsManager.exportData();
                    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = `news-ticker-export-${Date.now()}.json`;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                  }
                }
              }
            ]
          }
        }
      ]
    }
  };
};

// Enhanced App component
const App = (props, ctx) => ({
  div: {
    className: 'news-container',
    children: [
      {
        div: {
          className: 'news-wrapper',
          children: [
            { 
              h2: { 
                className: 'news-header',
                text: 'Scientific Discovery Timeline'
              }
            },
            { Banner: {} },
            { Controls: {} },

            // Stats panel (development mode)
            ...(window.location.search.includes('debug=true') ? [{
              div: {
                className: 'debug-panel mt-8 p-4 bg-gray-100 rounded-lg text-sm',
                children: [
                  { h3: { text: 'Debug Information', className: 'font-bold mb-2' } },
                  { 
                    pre: { 
                      text: () => JSON.stringify(ctx.NewsManager?.getStats?.() || {}, null, 2),
                      className: 'text-xs overflow-auto max-h-40'
                    }
                  }
                ]
              }
            }] : [])
          ]
        }
      }
    ]
  }
});

// Initialize application with enhanced error handling
try {
  const juris = new Juris({
    states: {
      currentNews: null,
      isUpdating: false,
      loading: false,
      lastUpdated: null,
      updateInterval: CONFIG.UI.DEFAULT_INTERVAL,
      error: null,
      imageError: false
    },
    components: { Banner, Controls, App },
    headlessComponents: { 
      NewsManager: { 
        fn: NewsManager, 
        options: { autoInit: true } 
      } 
    },
    layout: [{ App: {} }]
  });

  // Global error handler
  window.addEventListener('error', (event) => {
    Logger.error('Global error caught', {
      message: event.error?.message,
      stack: event.error?.stack,
      filename: event.filename,
      lineno: event.lineno
    });
  });

  window.addEventListener('unhandledrejection', (event) => {
    Logger.error('Unhandled promise rejection', {
      reason: event.reason?.message || event.reason
    });
  });

  // Cleanup on page unload
  window.addEventListener('beforeunload', () => {
    if (juris.headlessAPIs?.NewsManager?.cleanup) {
      juris.headlessAPIs.NewsManager.cleanup();
    }
  });

  juris.render();

  // Auto-start if enabled in preferences
  const preferences = PreferencesManager.load();
  if (preferences.autoStart) {
    setTimeout(() => {
      juris.headlessAPIs.NewsManager?.startPeriodicUpdates?.(preferences.updateInterval);
    }, 100);
  }

  Logger.info('News Ticker initialized successfully');

} catch (error) {
  Logger.error('Failed to initialize News Ticker', error);
  document.body.innerHTML = `
    <div class="error-container">
      <h1>Failed to Initialize News Ticker</h1>
      <p>Error: ${error.message}</p>
      <button onclick="location.reload()">Reload Page</button>
    </div>
  `; 
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)