retold-remote 0.0.5 → 0.0.7

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.
Files changed (71) hide show
  1. package/css/retold-remote.css +3 -0
  2. package/docs/README.md +181 -0
  3. package/docs/_cover.md +14 -0
  4. package/docs/_sidebar.md +10 -0
  5. package/docs/_topbar.md +3 -0
  6. package/docs/audio-viewer.md +133 -0
  7. package/docs/ebook-reader.md +90 -0
  8. package/docs/image-viewer.md +90 -0
  9. package/docs/server-setup.md +262 -0
  10. package/docs/video-viewer.md +134 -0
  11. package/html/docs.html +59 -0
  12. package/html/index.html +1 -1
  13. package/package.json +20 -6
  14. package/source/Pict-Application-RetoldRemote.js +122 -2
  15. package/source/RetoldRemote-ExtensionMaps.js +33 -0
  16. package/source/cli/RetoldRemote-Server-Setup.js +211 -67
  17. package/source/cli/commands/RetoldRemote-Command-Serve.js +5 -26
  18. package/source/providers/Pict-Provider-AISortManager.js +456 -0
  19. package/source/providers/Pict-Provider-CollectionManager.js +1200 -0
  20. package/source/providers/Pict-Provider-FormattingUtilities.js +109 -0
  21. package/source/providers/Pict-Provider-GalleryFilterSort.js +2 -11
  22. package/source/providers/Pict-Provider-GalleryNavigation.js +187 -409
  23. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +52 -0
  24. package/source/providers/Pict-Provider-ToastNotification.js +96 -0
  25. package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +88 -0
  26. package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +190 -0
  27. package/source/providers/keyboard-handlers/KeyHandler-Sidebar.js +65 -0
  28. package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +57 -0
  29. package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +197 -0
  30. package/source/server/RetoldRemote-AISortService.js +879 -0
  31. package/source/server/RetoldRemote-ArchiveService.js +2 -12
  32. package/source/server/RetoldRemote-AudioWaveformService.js +7 -16
  33. package/source/server/RetoldRemote-CollectionService.js +843 -0
  34. package/source/server/RetoldRemote-EbookService.js +7 -16
  35. package/source/server/RetoldRemote-FileOperationService.js +560 -0
  36. package/source/server/RetoldRemote-MediaService.js +15 -14
  37. package/source/server/RetoldRemote-MetadataCache.js +411 -0
  38. package/source/server/RetoldRemote-ParimeCache.js +349 -0
  39. package/source/server/RetoldRemote-ThumbnailCache.js +52 -20
  40. package/source/server/RetoldRemote-VideoFrameService.js +7 -15
  41. package/source/views/PictView-Remote-AudioExplorer.js +10 -43
  42. package/source/views/PictView-Remote-CollectionsPanel.js +1486 -0
  43. package/source/views/PictView-Remote-Gallery.js +237 -44
  44. package/source/views/PictView-Remote-ImageViewer.js +1 -34
  45. package/source/views/PictView-Remote-Layout.js +411 -20
  46. package/source/views/PictView-Remote-MediaViewer.js +311 -28
  47. package/source/views/PictView-Remote-SettingsPanel.js +275 -133
  48. package/source/views/PictView-Remote-TopBar.js +701 -14
  49. package/source/views/PictView-Remote-VLCSetup.js +8 -39
  50. package/source/views/PictView-Remote-VideoExplorer.js +20 -54
  51. package/web-application/css/docuserve.css +73 -0
  52. package/web-application/css/retold-remote.css +3 -0
  53. package/web-application/docs/README.md +181 -0
  54. package/web-application/docs/_cover.md +14 -0
  55. package/web-application/docs/_sidebar.md +10 -0
  56. package/web-application/docs/_topbar.md +3 -0
  57. package/web-application/docs/audio-viewer.md +133 -0
  58. package/web-application/docs/ebook-reader.md +90 -0
  59. package/web-application/docs/image-viewer.md +90 -0
  60. package/web-application/docs/server-setup.md +262 -0
  61. package/web-application/docs/video-viewer.md +134 -0
  62. package/web-application/docs.html +59 -0
  63. package/web-application/index.html +1 -1
  64. package/web-application/js/pict-docuserve.min.js +58 -0
  65. package/web-application/js/pict.min.js +2 -2
  66. package/web-application/js/pict.min.js.map +1 -1
  67. package/web-application/retold-remote.js +2652 -970
  68. package/web-application/retold-remote.js.map +1 -1
  69. package/web-application/retold-remote.min.js +60 -13
  70. package/web-application/retold-remote.min.js.map +1 -1
  71. package/server.js +0 -43
@@ -433,6 +433,69 @@ const _ViewConfiguration =
433
433
  color: var(--retold-text-dim);
434
434
  font-size: 0.85rem;
435
435
  }
436
+ /* Distraction-free toggle */
437
+ .retold-remote-df-toggle
438
+ {
439
+ position: absolute;
440
+ top: 8px;
441
+ left: 8px;
442
+ z-index: 15;
443
+ padding: 4px 8px;
444
+ border: 1px solid var(--retold-border);
445
+ border-radius: 3px;
446
+ background: rgba(0, 0, 0, 0.5);
447
+ color: var(--retold-text-muted);
448
+ font-size: 0.72rem;
449
+ cursor: pointer;
450
+ opacity: 0;
451
+ transition: opacity 0.2s ease, color 0.15s, border-color 0.15s;
452
+ font-family: inherit;
453
+ white-space: nowrap;
454
+ }
455
+ .retold-remote-viewer-body:hover .retold-remote-df-toggle,
456
+ .retold-remote-df-toggle:focus
457
+ {
458
+ opacity: 1;
459
+ }
460
+ .retold-remote-df-toggle:hover
461
+ {
462
+ color: var(--retold-text-primary);
463
+ border-color: var(--retold-accent);
464
+ }
465
+ .retold-remote-df-toggle.active
466
+ {
467
+ color: var(--retold-accent);
468
+ border-color: var(--retold-accent);
469
+ }
470
+ /* Distraction-free exit hotspot (top-left corner) */
471
+ .retold-remote-df-exit-hotspot
472
+ {
473
+ position: absolute;
474
+ top: 0;
475
+ left: 0;
476
+ width: 80px;
477
+ height: 80px;
478
+ z-index: 16;
479
+ cursor: pointer;
480
+ display: none;
481
+ }
482
+ .retold-remote-df-exit-hotspot::after
483
+ {
484
+ content: '';
485
+ position: absolute;
486
+ top: 12px;
487
+ left: 12px;
488
+ width: 8px;
489
+ height: 8px;
490
+ border-radius: 50%;
491
+ background: var(--retold-accent);
492
+ opacity: 0;
493
+ transition: opacity 0.3s ease;
494
+ }
495
+ .retold-remote-df-exit-hotspot:hover::after
496
+ {
497
+ opacity: 0.6;
498
+ }
436
499
  `
437
500
  };
438
501
 
@@ -441,6 +504,12 @@ class RetoldRemoteMediaViewerView extends libPictView
441
504
  constructor(pFable, pOptions, pServiceHash)
442
505
  {
443
506
  super(pFable, pOptions, pServiceHash);
507
+
508
+ this._swipeStartX = 0;
509
+ this._swipeStartY = 0;
510
+ this._swipeTouchCount = 0;
511
+ this._swipeHandlers = null;
512
+ this._dfExitHandlers = null;
444
513
  }
445
514
 
446
515
  /**
@@ -475,7 +544,7 @@ class RetoldRemoteMediaViewerView extends libPictView
475
544
  tmpHTML += '<div class="retold-remote-viewer-header">';
476
545
  tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].closeViewer()" title="Back (Esc)">&larr; Back</button>';
477
546
  tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].prevFile()" title="Previous (k)">&lsaquo; Prev</button>';
478
- tmpHTML += '<div class="retold-remote-viewer-title">' + this._escapeHTML(tmpFileName) + '</div>';
547
+ tmpHTML += '<div class="retold-remote-viewer-title">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpFileName) + '</div>';
479
548
  tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].nextFile()" title="Next (j)">Next &rsaquo;</button>';
480
549
  tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFileInfo()" title="Info (i)">&#9432;</button>';
481
550
  tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFullscreen()" title="Fullscreen (f)">&#9634;</button>';
@@ -484,6 +553,14 @@ class RetoldRemoteMediaViewerView extends libPictView
484
553
  // Body with media content
485
554
  tmpHTML += '<div class="retold-remote-viewer-body">';
486
555
 
556
+ // Distraction-free toggle (top-left corner)
557
+ let tmpDFActive = tmpRemote._distractionFreeMode ? ' active' : '';
558
+ tmpHTML += '<button class="retold-remote-df-toggle' + tmpDFActive + '" id="RetoldRemote-DF-Toggle" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleDistractionFree()" title="Distraction-Free (d)">&#9673; DF</button>';
559
+
560
+ // Exit hotspot for distraction-free mode (double-click/tap top-left to exit)
561
+ let tmpDFHotspotDisplay = tmpRemote._distractionFreeMode ? '' : ' style="display:none"';
562
+ tmpHTML += '<div class="retold-remote-df-exit-hotspot" id="RetoldRemote-DF-ExitHotspot"' + tmpDFHotspotDisplay + '></div>';
563
+
487
564
  switch (pMediaType)
488
565
  {
489
566
  case 'image':
@@ -538,6 +615,12 @@ class RetoldRemoteMediaViewerView extends libPictView
538
615
  }
539
616
  }
540
617
 
618
+ // Set up swipe navigation for touch devices
619
+ this._setupSwipeNavigation();
620
+
621
+ // Set up distraction-free exit hotspot (double-click/tap top-left)
622
+ this._setupDFExitHotspot();
623
+
541
624
  // Update topbar
542
625
  let tmpTopBar = this.pict.views['ContentEditor-TopBar'];
543
626
  if (tmpTopBar)
@@ -546,9 +629,224 @@ class RetoldRemoteMediaViewerView extends libPictView
546
629
  }
547
630
  }
548
631
 
632
+ /**
633
+ * Attach touch event listeners to the viewer body for swipe-based
634
+ * prev/next navigation. Only single-finger horizontal swipes are
635
+ * recognised; pinch gestures and vertical scrolling are ignored.
636
+ * Swipes are also suppressed when the image is zoomed and the
637
+ * container is scrollable, so the user can pan freely.
638
+ */
639
+ _setupSwipeNavigation()
640
+ {
641
+ // Clean up any previous listeners
642
+ this._cleanupSwipe();
643
+
644
+ let tmpBody = document.querySelector('.retold-remote-viewer-body');
645
+ if (!tmpBody)
646
+ {
647
+ return;
648
+ }
649
+
650
+ let tmpSelf = this;
651
+ let tmpSwipeThreshold = 50; // minimum px to count as a swipe
652
+
653
+ let tmpOnTouchStart = function (pEvent)
654
+ {
655
+ tmpSelf._swipeTouchCount = pEvent.touches.length;
656
+ if (pEvent.touches.length !== 1)
657
+ {
658
+ return;
659
+ }
660
+ tmpSelf._swipeStartX = pEvent.touches[0].clientX;
661
+ tmpSelf._swipeStartY = pEvent.touches[0].clientY;
662
+ };
663
+
664
+ let tmpOnTouchEnd = function (pEvent)
665
+ {
666
+ // Ignore multi-touch (pinch zoom, etc.)
667
+ if (tmpSelf._swipeTouchCount !== 1)
668
+ {
669
+ return;
670
+ }
671
+
672
+ let tmpEndX = pEvent.changedTouches[0].clientX;
673
+ let tmpEndY = pEvent.changedTouches[0].clientY;
674
+ let tmpDeltaX = tmpEndX - tmpSelf._swipeStartX;
675
+ let tmpDeltaY = tmpEndY - tmpSelf._swipeStartY;
676
+
677
+ // Must be primarily horizontal
678
+ if (Math.abs(tmpDeltaX) < tmpSwipeThreshold || Math.abs(tmpDeltaY) > Math.abs(tmpDeltaX))
679
+ {
680
+ return;
681
+ }
682
+
683
+ // If the viewer body is scrollable (zoomed image), don't
684
+ // swipe — let the user pan instead.
685
+ let tmpContainer = document.querySelector('.retold-remote-viewer-body');
686
+ if (tmpContainer && (tmpContainer.scrollWidth > tmpContainer.clientWidth + 2))
687
+ {
688
+ return;
689
+ }
690
+
691
+ let tmpNav = tmpSelf.pict.providers['RetoldRemote-GalleryNavigation'];
692
+ if (!tmpNav)
693
+ {
694
+ return;
695
+ }
696
+
697
+ if (tmpDeltaX < 0)
698
+ {
699
+ tmpNav.nextFile();
700
+ }
701
+ else
702
+ {
703
+ tmpNav.prevFile();
704
+ }
705
+ };
706
+
707
+ tmpBody.addEventListener('touchstart', tmpOnTouchStart, { passive: true });
708
+ tmpBody.addEventListener('touchend', tmpOnTouchEnd, { passive: true });
709
+
710
+ this._swipeHandlers =
711
+ {
712
+ element: tmpBody,
713
+ touchstart: tmpOnTouchStart,
714
+ touchend: tmpOnTouchEnd
715
+ };
716
+ }
717
+
718
+ /**
719
+ * Remove swipe touch listeners from the viewer body.
720
+ */
721
+ _cleanupSwipe()
722
+ {
723
+ if (this._swipeHandlers)
724
+ {
725
+ this._swipeHandlers.element.removeEventListener('touchstart', this._swipeHandlers.touchstart);
726
+ this._swipeHandlers.element.removeEventListener('touchend', this._swipeHandlers.touchend);
727
+ this._swipeHandlers = null;
728
+ }
729
+ }
730
+
731
+ /**
732
+ * Toggle distraction-free mode from the viewer overlay button.
733
+ * Delegates to the gallery navigation provider, then updates
734
+ * the toggle button and exit hotspot state.
735
+ */
736
+ toggleDistractionFree()
737
+ {
738
+ let tmpNav = this.pict.providers['RetoldRemote-GalleryNavigation'];
739
+ if (tmpNav)
740
+ {
741
+ tmpNav._toggleDistractionFree();
742
+ }
743
+ this._updateDFControls();
744
+ }
745
+
746
+ /**
747
+ * Sync the distraction-free toggle button and exit hotspot
748
+ * with the current mode state.
749
+ */
750
+ _updateDFControls()
751
+ {
752
+ let tmpRemote = this.pict.AppData.RetoldRemote;
753
+ let tmpActive = tmpRemote._distractionFreeMode || false;
754
+
755
+ let tmpToggle = document.getElementById('RetoldRemote-DF-Toggle');
756
+ if (tmpToggle)
757
+ {
758
+ if (tmpActive)
759
+ {
760
+ tmpToggle.classList.add('active');
761
+ }
762
+ else
763
+ {
764
+ tmpToggle.classList.remove('active');
765
+ }
766
+ }
767
+
768
+ let tmpHotspot = document.getElementById('RetoldRemote-DF-ExitHotspot');
769
+ if (tmpHotspot)
770
+ {
771
+ tmpHotspot.style.display = tmpActive ? '' : 'none';
772
+ }
773
+ }
774
+
775
+ /**
776
+ * Set up the double-click / double-tap handler on the
777
+ * exit hotspot so the user can leave distraction-free mode
778
+ * from the top-left corner of the viewer.
779
+ */
780
+ _setupDFExitHotspot()
781
+ {
782
+ this._cleanupDFExitHotspot();
783
+
784
+ let tmpHotspot = document.getElementById('RetoldRemote-DF-ExitHotspot');
785
+ if (!tmpHotspot)
786
+ {
787
+ return;
788
+ }
789
+
790
+ let tmpSelf = this;
791
+
792
+ // Double-click for mouse
793
+ let tmpOnDblClick = function ()
794
+ {
795
+ let tmpRemote = tmpSelf.pict.AppData.RetoldRemote;
796
+ if (tmpRemote._distractionFreeMode)
797
+ {
798
+ tmpSelf.toggleDistractionFree();
799
+ }
800
+ };
801
+
802
+ // Double-tap for touch: detect two taps within 300ms
803
+ let tmpLastTap = 0;
804
+ let tmpOnTouchEnd = function (pEvent)
805
+ {
806
+ let tmpNow = Date.now();
807
+ if (tmpNow - tmpLastTap < 300)
808
+ {
809
+ pEvent.preventDefault();
810
+ let tmpRemote = tmpSelf.pict.AppData.RetoldRemote;
811
+ if (tmpRemote._distractionFreeMode)
812
+ {
813
+ tmpSelf.toggleDistractionFree();
814
+ }
815
+ tmpLastTap = 0;
816
+ }
817
+ else
818
+ {
819
+ tmpLastTap = tmpNow;
820
+ }
821
+ };
822
+
823
+ tmpHotspot.addEventListener('dblclick', tmpOnDblClick);
824
+ tmpHotspot.addEventListener('touchend', tmpOnTouchEnd);
825
+
826
+ this._dfExitHandlers =
827
+ {
828
+ element: tmpHotspot,
829
+ dblclick: tmpOnDblClick,
830
+ touchend: tmpOnTouchEnd
831
+ };
832
+ }
833
+
834
+ /**
835
+ * Remove exit hotspot event listeners.
836
+ */
837
+ _cleanupDFExitHotspot()
838
+ {
839
+ if (this._dfExitHandlers)
840
+ {
841
+ this._dfExitHandlers.element.removeEventListener('dblclick', this._dfExitHandlers.dblclick);
842
+ this._dfExitHandlers.element.removeEventListener('touchend', this._dfExitHandlers.touchend);
843
+ this._dfExitHandlers = null;
844
+ }
845
+ }
846
+
549
847
  _buildImageHTML(pURL, pFileName)
550
848
  {
551
- return '<img src="' + pURL + '" alt="' + this._escapeHTML(pFileName) + '" '
849
+ return '<img src="' + pURL + '" alt="' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName) + '" '
552
850
  + 'style="max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;" '
553
851
  + 'id="RetoldRemote-ImageViewer-Img" '
554
852
  + 'onload="pict.views[\'RetoldRemote-ImageViewer\'].initImage()" '
@@ -563,7 +861,7 @@ class RetoldRemoteMediaViewerView extends libPictView
563
861
 
564
862
  // Build the action menu (shown by default instead of the player)
565
863
  let tmpHTML = '<div class="retold-remote-video-action-menu" id="RetoldRemote-VideoActionMenu">';
566
- tmpHTML += '<div class="retold-remote-video-action-menu-title">' + this._escapeHTML(pFileName) + '</div>';
864
+ tmpHTML += '<div class="retold-remote-video-action-menu-title">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName) + '</div>';
567
865
 
568
866
  // Frame preview container (loaded on demand via t key or automatically)
569
867
  if (tmpCapabilities.ffmpeg)
@@ -703,7 +1001,7 @@ class RetoldRemoteMediaViewerView extends libPictView
703
1001
  let tmpFrame = pData.Frames[0];
704
1002
  let tmpFrameURL = '/api/media/video-frame/' + pData.CacheKey + '/' + tmpFrame.Filename;
705
1003
  tmpWrap.innerHTML = '<img src="' + tmpFrameURL + '" '
706
- + 'alt="' + this._escapeHTML(tmpFilePath.replace(/^.*\//, '')) + '" '
1004
+ + 'alt="' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpFilePath.replace(/^.*\//, '')) + '" '
707
1005
  + 'onerror="this.parentNode.innerHTML=\'\'">';
708
1006
  }
709
1007
  else
@@ -725,7 +1023,7 @@ class RetoldRemoteMediaViewerView extends libPictView
725
1023
 
726
1024
  let tmpHTML = '<div style="text-align: center; padding: 40px;">'
727
1025
  + '<div style="margin-bottom: 24px;">' + tmpIconHTML + '</div>'
728
- + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this._escapeHTML(pFileName) + '</div>'
1026
+ + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName) + '</div>'
729
1027
  + '<audio controls' + (this.pict.AppData.RetoldRemote.AutoplayAudio ? ' autoplay' : '') + ' preload="metadata" id="RetoldRemote-AudioPlayer" style="width: 100%; max-width: 500px;">'
730
1028
  + '<source src="' + pURL + '">'
731
1029
  + 'Your browser does not support the audio tag.'
@@ -779,7 +1077,7 @@ class RetoldRemoteMediaViewerView extends libPictView
779
1077
  let tmpDocIconHTML = tmpIconProvider ? '<span class="retold-remote-icon retold-remote-icon-lg">' + tmpIconProvider.getIcon('document-large', 64) + '</span>' : '&#128196;';
780
1078
  return '<div style="text-align: center; padding: 40px;">'
781
1079
  + '<div style="margin-bottom: 24px;">' + tmpDocIconHTML + '</div>'
782
- + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this._escapeHTML(pFileName) + '</div>'
1080
+ + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName) + '</div>'
783
1081
  + '<a href="' + pURL + '" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Open in new tab</a>'
784
1082
  + '</div>';
785
1083
  }
@@ -998,7 +1296,7 @@ class RetoldRemoteMediaViewerView extends libPictView
998
1296
  if (tmpEl)
999
1297
  {
1000
1298
  tmpEl.innerHTML = '<div class="retold-remote-ebook-loading">Failed to convert: '
1001
- + tmpSelf._escapeHTML(pError.message)
1299
+ + tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pError.message)
1002
1300
  + '<br><a href="' + pContentURL + '" target="_blank" style="color: var(--retold-accent); margin-top: 12px; display: inline-block;">Download file</a>'
1003
1301
  + '</div>';
1004
1302
  }
@@ -1102,7 +1400,7 @@ class RetoldRemoteMediaViewerView extends libPictView
1102
1400
  if (tmpContentEl)
1103
1401
  {
1104
1402
  tmpContentEl.innerHTML = '<div class="retold-remote-ebook-loading">Failed to load ebook: '
1105
- + tmpSelf._escapeHTML(pError.message) + '</div>';
1403
+ + tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pError.message) + '</div>';
1106
1404
  }
1107
1405
  });
1108
1406
  }
@@ -1130,9 +1428,9 @@ class RetoldRemoteMediaViewerView extends libPictView
1130
1428
  let tmpItem = pItems[i];
1131
1429
  let tmpIndentClass = pDepth > 0 ? ' indent-' + Math.min(pDepth, 2) : '';
1132
1430
  tmpHTML += '<button class="retold-remote-ebook-toc-item' + tmpIndentClass + '" '
1133
- + 'data-href="' + tmpSelf._escapeHTML(tmpItem.href) + '" '
1431
+ + 'data-href="' + tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpItem.href) + '" '
1134
1432
  + 'onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookGoToChapter(this.getAttribute(\'data-href\'))">'
1135
- + tmpSelf._escapeHTML(tmpItem.label.trim())
1433
+ + tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpItem.label.trim())
1136
1434
  + '</button>';
1137
1435
 
1138
1436
  if (tmpItem.subitems && tmpItem.subitems.length > 0)
@@ -1199,7 +1497,7 @@ class RetoldRemoteMediaViewerView extends libPictView
1199
1497
  let tmpFallbackIconHTML = tmpIconProvider ? '<span class="retold-remote-icon retold-remote-icon-lg">' + tmpIconProvider.getIcon('document-large', 64) + '</span>' : '&#128196;';
1200
1498
  return '<div style="text-align: center; padding: 40px;">'
1201
1499
  + '<div style="margin-bottom: 24px;">' + tmpFallbackIconHTML + '</div>'
1202
- + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this._escapeHTML(pFileName) + '</div>'
1500
+ + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName) + '</div>'
1203
1501
  + '<a href="' + pURL + '" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Download / Open in new tab</a>'
1204
1502
  + '</div>';
1205
1503
  }
@@ -1232,7 +1530,7 @@ class RetoldRemoteMediaViewerView extends libPictView
1232
1530
 
1233
1531
  if (pData.Size !== undefined)
1234
1532
  {
1235
- tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Size</span><span class="retold-remote-fileinfo-value">' + tmpSelf._formatFileSize(pData.Size) + '</span></div>';
1533
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Size</span><span class="retold-remote-fileinfo-value">' + tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].formatFileSize(pData.Size) + '</span></div>';
1236
1534
  }
1237
1535
  if (pData.Width && pData.Height)
1238
1536
  {
@@ -1304,7 +1602,7 @@ class RetoldRemoteMediaViewerView extends libPictView
1304
1602
  }
1305
1603
  if (pData.Size !== undefined)
1306
1604
  {
1307
- tmpStatsHTML += '<span><span class="retold-remote-video-stat-label">Size</span> <span class="retold-remote-video-stat-value">' + tmpSelf._formatFileSize(pData.Size) + '</span></span>';
1605
+ tmpStatsHTML += '<span><span class="retold-remote-video-stat-label">Size</span> <span class="retold-remote-video-stat-value">' + tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].formatFileSize(pData.Size) + '</span></span>';
1308
1606
  }
1309
1607
 
1310
1608
  // Preserve the Explore and VLC buttons if they exist
@@ -1318,21 +1616,6 @@ class RetoldRemoteMediaViewerView extends libPictView
1318
1616
  });
1319
1617
  }
1320
1618
 
1321
- _formatFileSize(pBytes)
1322
- {
1323
- if (!pBytes || pBytes === 0) return '0 B';
1324
- let tmpUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
1325
- let tmpIndex = Math.floor(Math.log(pBytes) / Math.log(1024));
1326
- if (tmpIndex >= tmpUnits.length) tmpIndex = tmpUnits.length - 1;
1327
- let tmpSize = pBytes / Math.pow(1024, tmpIndex);
1328
- return tmpSize.toFixed(tmpIndex === 0 ? 0 : 1) + ' ' + tmpUnits[tmpIndex];
1329
- }
1330
-
1331
- _escapeHTML(pText)
1332
- {
1333
- if (!pText) return '';
1334
- return pText.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
1335
- }
1336
1619
  }
1337
1620
 
1338
1621
  RetoldRemoteMediaViewerView.default_configuration = _ViewConfiguration;