unified-video-framework 1.4.438 → 1.4.439

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,6 +34,17 @@ export class WebPlayer extends BasePlayer {
34
34
  this.flashTickerContainer = null;
35
35
  this.flashTickerTopElement = null;
36
36
  this.flashTickerBottomElement = null;
37
+ this.tickerCurrentItemIndex = 0;
38
+ this.tickerCycleTimer = null;
39
+ this.tickerConfig = null;
40
+ this.tickerHeadlineElement = null;
41
+ this.tickerDetailElement = null;
42
+ this.tickerIntroOverlay = null;
43
+ this.tickerProgressBar = null;
44
+ this.tickerProgressFill = null;
45
+ this.tickerIsPaused = false;
46
+ this.tickerPauseStartTime = 0;
47
+ this.tickerRemainingTime = 0;
37
48
  this.previewGateHit = false;
38
49
  this.paymentSuccessTime = 0;
39
50
  this.paymentSuccessful = false;
@@ -1939,6 +1950,10 @@ export class WebPlayer extends BasePlayer {
1939
1950
  }
1940
1951
  createTickerElement(config, position) {
1941
1952
  if (config.styleVariant === 'broadcast') {
1953
+ const layoutStyle = config.broadcastStyle?.layoutStyle || 'broadcast';
1954
+ if (layoutStyle === 'two-line' || layoutStyle === 'professional') {
1955
+ return this.createProfessionalTickerElement(config, position);
1956
+ }
1942
1957
  return this.createBroadcastTickerElement(config, position);
1943
1958
  }
1944
1959
  return this.createSimpleTickerElement(config, position);
@@ -2241,14 +2256,19 @@ export class WebPlayer extends BasePlayer {
2241
2256
  const style = document.createElement('style');
2242
2257
  style.id = 'uvf-ticker-animation';
2243
2258
  style.textContent = `
2259
+ /* Basic ticker scroll */
2244
2260
  @keyframes ticker-scroll {
2245
2261
  0% { transform: translateX(0%); }
2246
2262
  100% { transform: translateX(-100%); }
2247
2263
  }
2264
+
2265
+ /* Globe rotation */
2248
2266
  @keyframes globe-rotate {
2249
2267
  0% { transform: rotate(0deg); }
2250
2268
  100% { transform: rotate(360deg); }
2251
2269
  }
2270
+
2271
+ /* LIVE badge animations */
2252
2272
  @keyframes live-pulse {
2253
2273
  0%, 100% { opacity: 1; transform: scale(1); }
2254
2274
  50% { opacity: 0.85; transform: scale(1.02); }
@@ -2257,6 +2277,85 @@ export class WebPlayer extends BasePlayer {
2257
2277
  0%, 100% { opacity: 1; }
2258
2278
  50% { opacity: 0.3; }
2259
2279
  }
2280
+
2281
+ /* Intro Animations */
2282
+ @keyframes intro-slide-in {
2283
+ 0% { transform: translateX(-100%); opacity: 0; }
2284
+ 20% { transform: translateX(0); opacity: 1; }
2285
+ 80% { transform: translateX(0); opacity: 1; }
2286
+ 100% { transform: translateX(100%); opacity: 0; }
2287
+ }
2288
+
2289
+ @keyframes intro-flash {
2290
+ 0%, 100% { opacity: 0; }
2291
+ 10%, 30%, 50%, 70%, 90% { opacity: 1; background: rgba(255,255,255,0.3); }
2292
+ 20%, 40%, 60%, 80% { opacity: 1; background: transparent; }
2293
+ }
2294
+
2295
+ @keyframes intro-scale {
2296
+ 0% { transform: scale(0); opacity: 0; }
2297
+ 20% { transform: scale(1.2); opacity: 1; }
2298
+ 30% { transform: scale(1); }
2299
+ 80% { transform: scale(1); opacity: 1; }
2300
+ 100% { transform: scale(0); opacity: 0; }
2301
+ }
2302
+
2303
+ @keyframes intro-pulse {
2304
+ 0% { transform: scale(0.8); opacity: 0; }
2305
+ 25% { transform: scale(1.1); opacity: 1; }
2306
+ 50% { transform: scale(1); opacity: 1; }
2307
+ 75% { transform: scale(1); opacity: 1; }
2308
+ 100% { transform: scale(0.8); opacity: 0; }
2309
+ }
2310
+
2311
+ @keyframes intro-shake {
2312
+ 0% { transform: translateX(0); opacity: 0; }
2313
+ 10% { opacity: 1; }
2314
+ 15%, 35%, 55%, 75% { transform: translateX(-5px); }
2315
+ 25%, 45%, 65%, 85% { transform: translateX(5px); }
2316
+ 90% { transform: translateX(0); opacity: 1; }
2317
+ 100% { transform: translateX(0); opacity: 0; }
2318
+ }
2319
+
2320
+ @keyframes intro-none {
2321
+ 0% { opacity: 1; }
2322
+ 80% { opacity: 1; }
2323
+ 100% { opacity: 0; }
2324
+ }
2325
+
2326
+ /* Item Transitions */
2327
+ @keyframes item-fade-out {
2328
+ from { opacity: 1; }
2329
+ to { opacity: 0; }
2330
+ }
2331
+ @keyframes item-fade-in {
2332
+ from { opacity: 0; }
2333
+ to { opacity: 1; }
2334
+ }
2335
+ @keyframes item-slide-out {
2336
+ from { transform: translateY(0); opacity: 1; }
2337
+ to { transform: translateY(-100%); opacity: 0; }
2338
+ }
2339
+ @keyframes item-slide-in {
2340
+ from { transform: translateY(100%); opacity: 0; }
2341
+ to { transform: translateY(0); opacity: 1; }
2342
+ }
2343
+
2344
+ /* Decorative Separator Animations */
2345
+ @keyframes separator-pulse {
2346
+ 0%, 100% { opacity: 0.8; transform: scaleY(1); }
2347
+ 50% { opacity: 1; transform: scaleY(1.1); }
2348
+ }
2349
+
2350
+ @keyframes chevron-pulse {
2351
+ 0%, 100% { transform: translateX(0); opacity: 0.8; }
2352
+ 50% { transform: translateX(3px); opacity: 1; }
2353
+ }
2354
+
2355
+ @keyframes diamond-spin {
2356
+ from { transform: rotate(0deg); }
2357
+ to { transform: rotate(360deg); }
2358
+ }
2260
2359
  `;
2261
2360
  document.head.appendChild(style);
2262
2361
  }
@@ -2273,6 +2372,516 @@ export class WebPlayer extends BasePlayer {
2273
2372
  const totalWidth = (itemsLength * avgCharWidth) + (config.items.length * gap) + (config.items.length * separatorWidth);
2274
2373
  return Math.max(totalWidth / speed, 15);
2275
2374
  }
2375
+ createProfessionalTickerElement(config, position) {
2376
+ const broadcastStyle = config.broadcastStyle || {};
2377
+ const theme = broadcastStyle.theme || 'breaking-red';
2378
+ const twoLineConfig = broadcastStyle.twoLineDisplay || {};
2379
+ const itemCycling = config.itemCycling || {};
2380
+ this.tickerConfig = config;
2381
+ this.tickerCurrentItemIndex = 0;
2382
+ const themeColors = this.getBroadcastThemeColors(theme, broadcastStyle);
2383
+ const ticker = document.createElement('div');
2384
+ ticker.className = `uvf-flash-ticker ticker-${position} ticker-professional`;
2385
+ const bottomOffset = config.bottomOffset || 0;
2386
+ const topOffset = config.topOffset || 0;
2387
+ const headerHeight = broadcastStyle.headerHeight || 28;
2388
+ const topLineConfig = twoLineConfig.topLine || {};
2389
+ const bottomLineConfig = twoLineConfig.bottomLine || {};
2390
+ const topLineHeight = topLineConfig.minHeight || 32;
2391
+ const bottomLineHeight = bottomLineConfig.height || 28;
2392
+ const bodyHeight = twoLineConfig.enabled !== false ? topLineHeight + bottomLineHeight : (config.height || 36);
2393
+ const progressHeight = itemCycling.showProgress ? (itemCycling.progressHeight || 3) : 0;
2394
+ const totalHeight = headerHeight + bodyHeight + progressHeight;
2395
+ ticker.style.cssText = `
2396
+ position: absolute;
2397
+ left: 0;
2398
+ right: 0;
2399
+ height: ${totalHeight}px;
2400
+ ${position === 'top' ? `top: ${topOffset}px;` : `bottom: ${bottomOffset}px;`}
2401
+ overflow: hidden;
2402
+ pointer-events: ${itemCycling.pauseOnHover !== false ? 'auto' : 'none'};
2403
+ display: flex;
2404
+ flex-direction: column;
2405
+ `;
2406
+ const header = this.createProfessionalHeader(broadcastStyle, themeColors, headerHeight);
2407
+ ticker.appendChild(header);
2408
+ const body = this.createTwoLineBody(config, twoLineConfig, themeColors.bodyBg, bodyHeight);
2409
+ ticker.appendChild(body);
2410
+ if (itemCycling.showProgress) {
2411
+ const progressBar = this.createProgressBar(itemCycling);
2412
+ ticker.appendChild(progressBar);
2413
+ }
2414
+ const introOverlay = this.createIntroOverlay(broadcastStyle);
2415
+ ticker.appendChild(introOverlay);
2416
+ this.tickerIntroOverlay = introOverlay;
2417
+ if (itemCycling.pauseOnHover !== false && itemCycling.enabled) {
2418
+ ticker.addEventListener('mouseenter', () => this.pauseItemCycling());
2419
+ ticker.addEventListener('mouseleave', () => this.resumeItemCycling());
2420
+ }
2421
+ this.ensureTickerAnimations();
2422
+ if (itemCycling.enabled && config.items && config.items.length > 1) {
2423
+ const firstItem = config.items[0];
2424
+ if (firstItem.showIntro) {
2425
+ this.playIntroAnimation(firstItem).then(() => {
2426
+ this.startItemCycling();
2427
+ });
2428
+ }
2429
+ else {
2430
+ this.startItemCycling();
2431
+ }
2432
+ }
2433
+ return ticker;
2434
+ }
2435
+ createProfessionalHeader(broadcastStyle, themeColors, headerHeight) {
2436
+ const header = document.createElement('div');
2437
+ header.className = 'uvf-ticker-header-row';
2438
+ header.style.cssText = `
2439
+ display: flex;
2440
+ align-items: center;
2441
+ height: ${headerHeight}px;
2442
+ background: ${themeColors.headerBg};
2443
+ padding: 0 12px;
2444
+ position: relative;
2445
+ `;
2446
+ if (broadcastStyle.showGlobe !== false) {
2447
+ const globe = this.createGlobeElement(broadcastStyle.animateGlobe !== false);
2448
+ header.appendChild(globe);
2449
+ }
2450
+ if (broadcastStyle.decorativeShapes?.headerSeparator) {
2451
+ const separator = this.createDecorativeSeparator(broadcastStyle.decorativeShapes.headerSeparator);
2452
+ header.appendChild(separator);
2453
+ }
2454
+ else if (broadcastStyle.showGlobe !== false) {
2455
+ const defaultSeparator = document.createElement('div');
2456
+ defaultSeparator.className = 'uvf-ticker-separator';
2457
+ defaultSeparator.style.cssText = `
2458
+ width: 2px;
2459
+ height: 18px;
2460
+ background: rgba(255,255,255,0.6);
2461
+ margin: 0 10px;
2462
+ `;
2463
+ header.appendChild(defaultSeparator);
2464
+ }
2465
+ const headerText = document.createElement('span');
2466
+ headerText.className = 'uvf-ticker-header-text';
2467
+ headerText.textContent = broadcastStyle.headerText || 'BREAKING NEWS';
2468
+ headerText.style.cssText = `
2469
+ color: ${broadcastStyle.headerTextColor || '#ffffff'};
2470
+ font-size: ${broadcastStyle.headerFontSize || 16}px;
2471
+ font-weight: 800;
2472
+ text-transform: uppercase;
2473
+ letter-spacing: 1px;
2474
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
2475
+ `;
2476
+ header.appendChild(headerText);
2477
+ if (broadcastStyle.showLiveBadge !== false) {
2478
+ const liveBadge = this.createLiveBadgeElement(broadcastStyle.pulseLiveBadge !== false);
2479
+ header.appendChild(liveBadge);
2480
+ }
2481
+ return header;
2482
+ }
2483
+ createDecorativeSeparator(config) {
2484
+ const separator = document.createElement('div');
2485
+ separator.className = 'uvf-ticker-separator';
2486
+ const width = config.width || 2;
2487
+ const height = config.height || 20;
2488
+ const color = config.color || '#ffffff';
2489
+ const animated = config.animated !== false;
2490
+ const type = config.type || 'line';
2491
+ let animationStyle = '';
2492
+ let content = '';
2493
+ switch (type) {
2494
+ case 'pulse-line':
2495
+ animationStyle = animated ? 'animation: separator-pulse 1.5s ease-in-out infinite;' : '';
2496
+ break;
2497
+ case 'chevron':
2498
+ content = '›';
2499
+ animationStyle = animated ? 'animation: chevron-pulse 1s ease-in-out infinite;' : '';
2500
+ break;
2501
+ case 'diamond':
2502
+ content = '◆';
2503
+ animationStyle = animated ? 'animation: diamond-spin 3s linear infinite;' : '';
2504
+ break;
2505
+ case 'dot':
2506
+ content = '•';
2507
+ animationStyle = animated ? 'animation: dot-blink 1s ease-in-out infinite;' : '';
2508
+ break;
2509
+ default:
2510
+ break;
2511
+ }
2512
+ if (type === 'line' || type === 'pulse-line') {
2513
+ separator.style.cssText = `
2514
+ width: ${width}px;
2515
+ height: ${height}px;
2516
+ background: ${color};
2517
+ margin: 0 10px;
2518
+ opacity: 0.8;
2519
+ ${animationStyle}
2520
+ `;
2521
+ }
2522
+ else {
2523
+ separator.textContent = content;
2524
+ separator.style.cssText = `
2525
+ color: ${color};
2526
+ font-size: ${height}px;
2527
+ margin: 0 10px;
2528
+ opacity: 0.8;
2529
+ display: flex;
2530
+ align-items: center;
2531
+ justify-content: center;
2532
+ ${animationStyle}
2533
+ `;
2534
+ }
2535
+ return separator;
2536
+ }
2537
+ createTwoLineBody(config, twoLineConfig, bodyBg, bodyHeight) {
2538
+ const body = document.createElement('div');
2539
+ body.className = 'uvf-ticker-body-row';
2540
+ body.style.cssText = `
2541
+ display: flex;
2542
+ flex-direction: column;
2543
+ height: ${bodyHeight}px;
2544
+ background: ${bodyBg};
2545
+ overflow: hidden;
2546
+ position: relative;
2547
+ `;
2548
+ const topLineConfig = twoLineConfig.topLine || {};
2549
+ const bottomLineConfig = twoLineConfig.bottomLine || {};
2550
+ const firstItem = config.items?.[0];
2551
+ const headlineLine = document.createElement('div');
2552
+ headlineLine.className = 'uvf-ticker-headline-line';
2553
+ this.tickerHeadlineElement = headlineLine;
2554
+ const topLineHeight = topLineConfig.minHeight || 32;
2555
+ const topLineFontSize = topLineConfig.fontSize || 16;
2556
+ const topLineLineHeight = topLineConfig.lineHeight || 1.3;
2557
+ const topLineMultiLine = topLineConfig.multiLine !== false;
2558
+ const topLineMaxLines = topLineConfig.maxLines || 2;
2559
+ headlineLine.style.cssText = `
2560
+ display: flex;
2561
+ align-items: center;
2562
+ min-height: ${topLineHeight}px;
2563
+ padding: ${topLineConfig.padding || 8}px 12px;
2564
+ background: ${topLineConfig.backgroundColor || 'transparent'};
2565
+ overflow: hidden;
2566
+ ${topLineMultiLine ? '' : 'white-space: nowrap;'}
2567
+ `;
2568
+ const headlineText = document.createElement('span');
2569
+ headlineText.className = 'uvf-ticker-headline-text';
2570
+ headlineText.style.cssText = `
2571
+ color: ${topLineConfig.textColor || config.textColor || '#ffffff'};
2572
+ font-size: ${topLineFontSize}px;
2573
+ font-weight: 700;
2574
+ line-height: ${topLineLineHeight};
2575
+ ${topLineMultiLine ? `
2576
+ display: -webkit-box;
2577
+ -webkit-line-clamp: ${topLineMaxLines};
2578
+ -webkit-box-orient: vertical;
2579
+ overflow: hidden;
2580
+ text-overflow: ellipsis;
2581
+ white-space: normal;
2582
+ word-wrap: break-word;
2583
+ ` : `
2584
+ white-space: nowrap;
2585
+ overflow: hidden;
2586
+ text-overflow: ellipsis;
2587
+ `}
2588
+ `;
2589
+ if (firstItem) {
2590
+ if (firstItem.headlineHtml) {
2591
+ headlineText.innerHTML = firstItem.headlineHtml;
2592
+ }
2593
+ else if (firstItem.headline) {
2594
+ headlineText.textContent = firstItem.headline;
2595
+ }
2596
+ else {
2597
+ headlineText.textContent = firstItem.text;
2598
+ }
2599
+ }
2600
+ headlineLine.appendChild(headlineText);
2601
+ body.appendChild(headlineLine);
2602
+ if (twoLineConfig.showSeparator !== false) {
2603
+ const separatorLine = document.createElement('div');
2604
+ separatorLine.style.cssText = `
2605
+ height: 1px;
2606
+ background: ${twoLineConfig.separatorColor || 'rgba(255,255,255,0.2)'};
2607
+ margin: 0 12px;
2608
+ `;
2609
+ body.appendChild(separatorLine);
2610
+ }
2611
+ const detailLine = document.createElement('div');
2612
+ detailLine.className = 'uvf-ticker-detail-line';
2613
+ this.tickerDetailElement = detailLine;
2614
+ const bottomLineHeight = bottomLineConfig.height || 28;
2615
+ const bottomLineFontSize = bottomLineConfig.fontSize || 14;
2616
+ const bottomLineSpeed = bottomLineConfig.speed || 80;
2617
+ detailLine.style.cssText = `
2618
+ display: flex;
2619
+ align-items: center;
2620
+ height: ${bottomLineHeight}px;
2621
+ background: ${bottomLineConfig.backgroundColor || 'transparent'};
2622
+ overflow: hidden;
2623
+ position: relative;
2624
+ `;
2625
+ const track = document.createElement('div');
2626
+ track.className = 'uvf-ticker-track';
2627
+ const detailText = firstItem?.text || '';
2628
+ const textWidth = detailText.length * 10;
2629
+ const duration = Math.max(textWidth / bottomLineSpeed, 10);
2630
+ track.style.cssText = `
2631
+ display: flex;
2632
+ white-space: nowrap;
2633
+ animation: ticker-scroll ${duration}s linear infinite;
2634
+ will-change: transform;
2635
+ padding-left: 100%;
2636
+ `;
2637
+ const renderDetail = (text, html) => {
2638
+ for (let i = 0; i < 10; i++) {
2639
+ const span = document.createElement('span');
2640
+ if (html) {
2641
+ span.innerHTML = html;
2642
+ }
2643
+ else {
2644
+ span.textContent = text;
2645
+ }
2646
+ span.style.cssText = `
2647
+ color: ${bottomLineConfig.textColor || config.textColor || '#ffffff'};
2648
+ font-size: ${bottomLineFontSize}px;
2649
+ font-weight: 500;
2650
+ margin-right: 100px;
2651
+ display: inline-flex;
2652
+ align-items: center;
2653
+ `;
2654
+ track.appendChild(span);
2655
+ }
2656
+ };
2657
+ if (firstItem) {
2658
+ renderDetail(firstItem.text, firstItem.html);
2659
+ }
2660
+ detailLine.appendChild(track);
2661
+ body.appendChild(detailLine);
2662
+ return body;
2663
+ }
2664
+ createProgressBar(itemCycling) {
2665
+ const progressBar = document.createElement('div');
2666
+ progressBar.className = 'uvf-ticker-progress';
2667
+ this.tickerProgressBar = progressBar;
2668
+ const height = itemCycling.progressHeight || 3;
2669
+ progressBar.style.cssText = `
2670
+ height: ${height}px;
2671
+ background: rgba(0,0,0,0.3);
2672
+ position: relative;
2673
+ overflow: hidden;
2674
+ `;
2675
+ const progressFill = document.createElement('div');
2676
+ progressFill.className = 'uvf-progress-fill';
2677
+ this.tickerProgressFill = progressFill;
2678
+ progressFill.style.cssText = `
2679
+ height: 100%;
2680
+ width: 0%;
2681
+ background: ${itemCycling.progressColor || '#ffffff'};
2682
+ transition: width 0.1s linear;
2683
+ `;
2684
+ progressBar.appendChild(progressFill);
2685
+ return progressBar;
2686
+ }
2687
+ createIntroOverlay(broadcastStyle) {
2688
+ const overlay = document.createElement('div');
2689
+ overlay.className = 'uvf-ticker-intro-overlay';
2690
+ overlay.style.cssText = `
2691
+ position: absolute;
2692
+ top: 0;
2693
+ left: 0;
2694
+ right: 0;
2695
+ bottom: 0;
2696
+ display: none;
2697
+ align-items: center;
2698
+ justify-content: center;
2699
+ background: rgba(200, 0, 0, 0.95);
2700
+ z-index: 10;
2701
+ `;
2702
+ const introText = document.createElement('span');
2703
+ introText.className = 'uvf-intro-text';
2704
+ introText.style.cssText = `
2705
+ color: #ffffff;
2706
+ font-size: 24px;
2707
+ font-weight: 900;
2708
+ text-transform: uppercase;
2709
+ letter-spacing: 3px;
2710
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
2711
+ `;
2712
+ introText.textContent = 'BREAKING';
2713
+ overlay.appendChild(introText);
2714
+ return overlay;
2715
+ }
2716
+ playIntroAnimation(item) {
2717
+ return new Promise((resolve) => {
2718
+ if (!this.tickerIntroOverlay || !item.showIntro) {
2719
+ resolve();
2720
+ return;
2721
+ }
2722
+ const broadcastStyle = this.tickerConfig?.broadcastStyle || {};
2723
+ const animation = item.introAnimation || broadcastStyle.defaultIntroAnimation || 'slide-in';
2724
+ const duration = item.introDuration || broadcastStyle.defaultIntroDuration || 2000;
2725
+ const introText = item.introText || 'BREAKING';
2726
+ const textElement = this.tickerIntroOverlay.querySelector('.uvf-intro-text');
2727
+ if (textElement) {
2728
+ textElement.textContent = introText;
2729
+ }
2730
+ this.tickerIntroOverlay.style.display = 'flex';
2731
+ this.tickerIntroOverlay.style.animation = `intro-${animation} ${duration}ms ease-out forwards`;
2732
+ setTimeout(() => {
2733
+ if (this.tickerIntroOverlay) {
2734
+ this.tickerIntroOverlay.style.display = 'none';
2735
+ this.tickerIntroOverlay.style.animation = '';
2736
+ }
2737
+ resolve();
2738
+ }, duration);
2739
+ });
2740
+ }
2741
+ startItemCycling() {
2742
+ if (!this.tickerConfig?.items || this.tickerConfig.items.length <= 1)
2743
+ return;
2744
+ const itemCycling = this.tickerConfig.itemCycling || {};
2745
+ if (!itemCycling.enabled)
2746
+ return;
2747
+ const currentItem = this.tickerConfig.items[this.tickerCurrentItemIndex];
2748
+ const duration = (currentItem.duration || itemCycling.defaultDuration || 10) * 1000;
2749
+ this.animateProgress(duration);
2750
+ this.tickerCycleTimer = window.setTimeout(() => {
2751
+ this.transitionToNextItem();
2752
+ }, duration);
2753
+ }
2754
+ stopItemCycling() {
2755
+ if (this.tickerCycleTimer) {
2756
+ clearTimeout(this.tickerCycleTimer);
2757
+ this.tickerCycleTimer = null;
2758
+ }
2759
+ }
2760
+ pauseItemCycling() {
2761
+ if (!this.tickerCycleTimer || this.tickerIsPaused)
2762
+ return;
2763
+ this.tickerIsPaused = true;
2764
+ this.tickerPauseStartTime = Date.now();
2765
+ const itemCycling = this.tickerConfig?.itemCycling || {};
2766
+ const currentItem = this.tickerConfig?.items?.[this.tickerCurrentItemIndex];
2767
+ const totalDuration = (currentItem?.duration || itemCycling.defaultDuration || 10) * 1000;
2768
+ clearTimeout(this.tickerCycleTimer);
2769
+ this.tickerCycleTimer = null;
2770
+ if (this.tickerProgressFill) {
2771
+ const computedWidth = window.getComputedStyle(this.tickerProgressFill).width;
2772
+ this.tickerProgressFill.style.transition = 'none';
2773
+ this.tickerProgressFill.style.width = computedWidth;
2774
+ }
2775
+ }
2776
+ resumeItemCycling() {
2777
+ if (!this.tickerIsPaused)
2778
+ return;
2779
+ this.tickerIsPaused = false;
2780
+ const itemCycling = this.tickerConfig?.itemCycling || {};
2781
+ const currentItem = this.tickerConfig?.items?.[this.tickerCurrentItemIndex];
2782
+ const totalDuration = (currentItem?.duration || itemCycling.defaultDuration || 10) * 1000;
2783
+ let remainingTime = totalDuration;
2784
+ if (this.tickerProgressFill) {
2785
+ const currentWidth = parseFloat(this.tickerProgressFill.style.width) || 0;
2786
+ remainingTime = totalDuration * (1 - currentWidth / 100);
2787
+ }
2788
+ this.animateProgress(remainingTime);
2789
+ this.tickerCycleTimer = window.setTimeout(() => {
2790
+ this.transitionToNextItem();
2791
+ }, remainingTime);
2792
+ }
2793
+ animateProgress(duration) {
2794
+ if (!this.tickerProgressFill)
2795
+ return;
2796
+ this.tickerProgressFill.style.transition = 'none';
2797
+ this.tickerProgressFill.style.width = '0%';
2798
+ this.tickerProgressFill.offsetHeight;
2799
+ this.tickerProgressFill.style.transition = `width ${duration}ms linear`;
2800
+ this.tickerProgressFill.style.width = '100%';
2801
+ }
2802
+ async transitionToNextItem() {
2803
+ if (!this.tickerConfig?.items || this.tickerConfig.items.length <= 1)
2804
+ return;
2805
+ const itemCycling = this.tickerConfig.itemCycling || {};
2806
+ const transitionType = itemCycling.transitionType || 'fade';
2807
+ const transitionDuration = itemCycling.transitionDuration || 500;
2808
+ this.tickerCurrentItemIndex = (this.tickerCurrentItemIndex + 1) % this.tickerConfig.items.length;
2809
+ const nextItem = this.tickerConfig.items[this.tickerCurrentItemIndex];
2810
+ if (nextItem.showIntro) {
2811
+ await this.playIntroAnimation(nextItem);
2812
+ }
2813
+ await this.applyItemTransition(transitionType, transitionDuration, nextItem);
2814
+ this.startItemCycling();
2815
+ }
2816
+ applyItemTransition(transitionType, duration, item) {
2817
+ return new Promise((resolve) => {
2818
+ const headline = this.tickerHeadlineElement?.querySelector('.uvf-ticker-headline-text');
2819
+ const detailTrack = this.tickerDetailElement?.querySelector('.uvf-ticker-track');
2820
+ if (!headline || !detailTrack) {
2821
+ this.updateItemContent(item);
2822
+ resolve();
2823
+ return;
2824
+ }
2825
+ if (transitionType === 'none') {
2826
+ this.updateItemContent(item);
2827
+ resolve();
2828
+ return;
2829
+ }
2830
+ const outAnimation = transitionType === 'slide' ? 'item-slide-out' : 'item-fade-out';
2831
+ const inAnimation = transitionType === 'slide' ? 'item-slide-in' : 'item-fade-in';
2832
+ headline.style.animation = `${outAnimation} ${duration / 2}ms ease-out forwards`;
2833
+ setTimeout(() => {
2834
+ this.updateItemContent(item);
2835
+ headline.style.animation = `${inAnimation} ${duration / 2}ms ease-out forwards`;
2836
+ setTimeout(() => {
2837
+ headline.style.animation = '';
2838
+ resolve();
2839
+ }, duration / 2);
2840
+ }, duration / 2);
2841
+ });
2842
+ }
2843
+ updateItemContent(item) {
2844
+ const headline = this.tickerHeadlineElement?.querySelector('.uvf-ticker-headline-text');
2845
+ if (headline) {
2846
+ if (item.headlineHtml) {
2847
+ headline.innerHTML = item.headlineHtml;
2848
+ }
2849
+ else if (item.headline) {
2850
+ headline.textContent = item.headline;
2851
+ }
2852
+ else {
2853
+ headline.textContent = item.text;
2854
+ }
2855
+ }
2856
+ const detailTrack = this.tickerDetailElement?.querySelector('.uvf-ticker-track');
2857
+ if (detailTrack) {
2858
+ detailTrack.innerHTML = '';
2859
+ const bottomLineConfig = this.tickerConfig?.broadcastStyle?.twoLineDisplay?.bottomLine || {};
2860
+ const bottomLineFontSize = bottomLineConfig.fontSize || 14;
2861
+ for (let i = 0; i < 10; i++) {
2862
+ const span = document.createElement('span');
2863
+ if (item.html) {
2864
+ span.innerHTML = item.html;
2865
+ }
2866
+ else {
2867
+ span.textContent = item.text;
2868
+ }
2869
+ span.style.cssText = `
2870
+ color: ${bottomLineConfig.textColor || this.tickerConfig?.textColor || '#ffffff'};
2871
+ font-size: ${bottomLineFontSize}px;
2872
+ font-weight: 500;
2873
+ margin-right: 100px;
2874
+ display: inline-flex;
2875
+ align-items: center;
2876
+ `;
2877
+ detailTrack.appendChild(span);
2878
+ }
2879
+ const bottomLineSpeed = bottomLineConfig.speed || 80;
2880
+ const textWidth = item.text.length * 10;
2881
+ const duration = Math.max(textWidth / bottomLineSpeed, 10);
2882
+ detailTrack.style.animation = `ticker-scroll ${duration}s linear infinite`;
2883
+ }
2884
+ }
2276
2885
  setAutoQuality(enabled) {
2277
2886
  this.autoQuality = enabled;
2278
2887
  if (this.hls) {