retold-remote 0.0.1

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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/css/retold-remote.css +83 -0
  3. package/html/codejar.js +511 -0
  4. package/html/index.html +23 -0
  5. package/package.json +68 -0
  6. package/server.js +43 -0
  7. package/source/Pict-Application-RetoldRemote-Configuration.json +7 -0
  8. package/source/Pict-Application-RetoldRemote.js +622 -0
  9. package/source/Pict-RetoldRemote-Bundle.js +14 -0
  10. package/source/cli/RetoldRemote-CLI-Program.js +15 -0
  11. package/source/cli/RetoldRemote-CLI-Run.js +3 -0
  12. package/source/cli/RetoldRemote-Server-Setup.js +257 -0
  13. package/source/cli/commands/RetoldRemote-Command-Serve.js +87 -0
  14. package/source/providers/Pict-Provider-GalleryFilterSort.js +597 -0
  15. package/source/providers/Pict-Provider-GalleryNavigation.js +819 -0
  16. package/source/providers/Pict-Provider-RetoldRemote.js +273 -0
  17. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +640 -0
  18. package/source/providers/Pict-Provider-RetoldRemoteTheme.js +879 -0
  19. package/source/server/RetoldRemote-MediaService.js +536 -0
  20. package/source/server/RetoldRemote-PathRegistry.js +121 -0
  21. package/source/server/RetoldRemote-ThumbnailCache.js +89 -0
  22. package/source/server/RetoldRemote-ToolDetector.js +78 -0
  23. package/source/views/PictView-Remote-Gallery.js +1437 -0
  24. package/source/views/PictView-Remote-ImageViewer.js +363 -0
  25. package/source/views/PictView-Remote-Layout.js +420 -0
  26. package/source/views/PictView-Remote-MediaViewer.js +530 -0
  27. package/source/views/PictView-Remote-SettingsPanel.js +318 -0
  28. package/source/views/PictView-Remote-TopBar.js +206 -0
  29. package/web-application/codejar.js +511 -0
  30. package/web-application/css/retold-remote.css +83 -0
  31. package/web-application/index.html +23 -0
  32. package/web-application/js/pict.min.js +12 -0
  33. package/web-application/js/pict.min.js.map +1 -0
  34. package/web-application/retold-remote.compatible.js +5764 -0
  35. package/web-application/retold-remote.compatible.js.map +1 -0
  36. package/web-application/retold-remote.compatible.min.js +120 -0
  37. package/web-application/retold-remote.compatible.min.js.map +1 -0
  38. package/web-application/retold-remote.js +5763 -0
  39. package/web-application/retold-remote.js.map +1 -0
  40. package/web-application/retold-remote.min.js +120 -0
  41. package/web-application/retold-remote.min.js.map +1 -0
@@ -0,0 +1,530 @@
1
+ const libPictView = require('pict-view');
2
+ const libPictSectionCode = require('pict-section-code');
3
+
4
+ const _ViewConfiguration =
5
+ {
6
+ ViewIdentifier: "RetoldRemote-MediaViewer",
7
+ DefaultRenderable: "RetoldRemote-MediaViewer",
8
+ DefaultDestinationAddress: "#RetoldRemote-Viewer-Container",
9
+ AutoRender: false,
10
+
11
+ CSS: /*css*/`
12
+ .retold-remote-viewer
13
+ {
14
+ display: flex;
15
+ flex-direction: column;
16
+ height: 100%;
17
+ }
18
+ .retold-remote-viewer-header
19
+ {
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 12px;
23
+ padding: 8px 16px;
24
+ background: var(--retold-bg-secondary);
25
+ border-bottom: 1px solid var(--retold-border);
26
+ flex-shrink: 0;
27
+ z-index: 5;
28
+ }
29
+ .retold-remote-viewer-nav-btn
30
+ {
31
+ padding: 4px 10px;
32
+ border: 1px solid var(--retold-border);
33
+ border-radius: 3px;
34
+ background: transparent;
35
+ color: var(--retold-text-muted);
36
+ font-size: 0.8rem;
37
+ cursor: pointer;
38
+ transition: color 0.15s, border-color 0.15s;
39
+ font-family: inherit;
40
+ }
41
+ .retold-remote-viewer-nav-btn:hover
42
+ {
43
+ color: var(--retold-text-primary);
44
+ border-color: var(--retold-accent);
45
+ }
46
+ .retold-remote-viewer-title
47
+ {
48
+ flex: 1;
49
+ font-size: 0.82rem;
50
+ color: var(--retold-text-secondary);
51
+ overflow: hidden;
52
+ text-overflow: ellipsis;
53
+ white-space: nowrap;
54
+ text-align: center;
55
+ }
56
+ .retold-remote-viewer-body
57
+ {
58
+ flex: 1;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ overflow: auto;
63
+ position: relative;
64
+ }
65
+ /* File info overlay */
66
+ .retold-remote-fileinfo-overlay
67
+ {
68
+ position: absolute;
69
+ top: 48px;
70
+ right: 16px;
71
+ background: var(--retold-bg-secondary);
72
+ border: 1px solid var(--retold-border);
73
+ border-radius: 6px;
74
+ padding: 16px;
75
+ color: var(--retold-text-secondary);
76
+ font-size: 0.78rem;
77
+ z-index: 10;
78
+ min-width: 200px;
79
+ display: none;
80
+ }
81
+ .retold-remote-fileinfo-row
82
+ {
83
+ display: flex;
84
+ justify-content: space-between;
85
+ padding: 3px 0;
86
+ }
87
+ .retold-remote-fileinfo-label
88
+ {
89
+ color: var(--retold-text-dim);
90
+ }
91
+ .retold-remote-fileinfo-value
92
+ {
93
+ color: var(--retold-text-primary);
94
+ }
95
+ /* Code viewer container */
96
+ .retold-remote-code-viewer-container
97
+ {
98
+ width: 100%;
99
+ height: 100%;
100
+ overflow: hidden;
101
+ }
102
+ .retold-remote-code-viewer-loading
103
+ {
104
+ padding: 16px 20px;
105
+ color: var(--retold-text-dim);
106
+ font-style: italic;
107
+ font-size: 0.82rem;
108
+ }
109
+ /* pict-section-code dark theme overrides */
110
+ .retold-remote-code-viewer-container .pict-code-editor-wrap
111
+ {
112
+ border: none;
113
+ border-radius: 0;
114
+ height: 100%;
115
+ font-family: var(--retold-font-mono, 'SFMono-Regular', 'SF Mono', 'Menlo', 'Consolas', monospace);
116
+ font-size: 0.82rem;
117
+ line-height: 1.6;
118
+ }
119
+ .retold-remote-code-viewer-container .pict-code-line-numbers
120
+ {
121
+ background: var(--retold-bg-secondary);
122
+ border-right: 1px solid var(--retold-border);
123
+ color: var(--retold-text-dim);
124
+ font-size: 0.78rem;
125
+ line-height: 1.6;
126
+ padding: 10px 0;
127
+ }
128
+ .retold-remote-code-viewer-container .pict-code-editor
129
+ {
130
+ background: var(--retold-bg-tertiary);
131
+ color: var(--retold-text-primary);
132
+ padding: 10px 10px 10px 12px;
133
+ tab-size: 4;
134
+ -moz-tab-size: 4;
135
+ caret-color: var(--retold-accent);
136
+ border-radius: 0;
137
+ }
138
+ /* Syntax highlighting colors for dark themes */
139
+ .retold-remote-code-viewer-container .pict-code-editor .keyword { color: #C678DD; }
140
+ .retold-remote-code-viewer-container .pict-code-editor .string { color: #98C379; }
141
+ .retold-remote-code-viewer-container .pict-code-editor .number { color: #D19A66; }
142
+ .retold-remote-code-viewer-container .pict-code-editor .comment { color: #5C6370; font-style: italic; }
143
+ .retold-remote-code-viewer-container .pict-code-editor .operator { color: #56B6C2; }
144
+ .retold-remote-code-viewer-container .pict-code-editor .punctuation { color: #ABB2BF; }
145
+ .retold-remote-code-viewer-container .pict-code-editor .function-name { color: #61AFEF; }
146
+ .retold-remote-code-viewer-container .pict-code-editor .property { color: #E06C75; }
147
+ .retold-remote-code-viewer-container .pict-code-editor .tag { color: #E06C75; }
148
+ .retold-remote-code-viewer-container .pict-code-editor .attr-name { color: #D19A66; }
149
+ .retold-remote-code-viewer-container .pict-code-editor .attr-value { color: #98C379; }
150
+ `
151
+ };
152
+
153
+ class RetoldRemoteMediaViewerView extends libPictView
154
+ {
155
+ constructor(pFable, pOptions, pServiceHash)
156
+ {
157
+ super(pFable, pOptions, pServiceHash);
158
+ }
159
+
160
+ /**
161
+ * Show the media viewer for a given file.
162
+ *
163
+ * @param {string} pFilePath - Relative file path
164
+ * @param {string} pMediaType - 'image', 'video', 'audio', 'document', or 'other'
165
+ */
166
+ showMedia(pFilePath, pMediaType)
167
+ {
168
+ let tmpRemote = this.pict.AppData.RetoldRemote;
169
+ tmpRemote.ActiveMode = 'viewer';
170
+ tmpRemote.CurrentViewerFile = pFilePath;
171
+ tmpRemote.CurrentViewerMediaType = pMediaType;
172
+
173
+ // Show viewer, hide gallery
174
+ let tmpGalleryContainer = document.getElementById('RetoldRemote-Gallery-Container');
175
+ let tmpViewerContainer = document.getElementById('RetoldRemote-Viewer-Container');
176
+
177
+ if (tmpGalleryContainer) tmpGalleryContainer.style.display = 'none';
178
+ if (tmpViewerContainer) tmpViewerContainer.style.display = 'block';
179
+
180
+ let tmpFileName = pFilePath.replace(/^.*\//, '');
181
+ let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
182
+ let tmpContentURL = tmpProvider ? tmpProvider.getContentURL(pFilePath) : ('/content/' + encodeURIComponent(pFilePath));
183
+
184
+ // Build the viewer HTML
185
+ let tmpHTML = '<div class="retold-remote-viewer">';
186
+
187
+ // Header with nav
188
+ tmpHTML += '<div class="retold-remote-viewer-header">';
189
+ tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].closeViewer()" title="Back (Esc)">&larr; Back</button>';
190
+ tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].prevFile()" title="Previous (k)">&lsaquo; Prev</button>';
191
+ tmpHTML += '<div class="retold-remote-viewer-title">' + this._escapeHTML(tmpFileName) + '</div>';
192
+ tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].nextFile()" title="Next (j)">Next &rsaquo;</button>';
193
+ tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFileInfo()" title="Info (i)">&#9432;</button>';
194
+ tmpHTML += '<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFullscreen()" title="Fullscreen (f)">&#9634;</button>';
195
+ tmpHTML += '</div>';
196
+
197
+ // Body with media content
198
+ tmpHTML += '<div class="retold-remote-viewer-body">';
199
+
200
+ switch (pMediaType)
201
+ {
202
+ case 'image':
203
+ tmpHTML += this._buildImageHTML(tmpContentURL, tmpFileName);
204
+ break;
205
+ case 'video':
206
+ tmpHTML += this._buildVideoHTML(tmpContentURL, tmpFileName);
207
+ break;
208
+ case 'audio':
209
+ tmpHTML += this._buildAudioHTML(tmpContentURL, tmpFileName);
210
+ break;
211
+ case 'text':
212
+ tmpHTML += this._buildTextHTML(tmpContentURL, tmpFileName, pFilePath);
213
+ break;
214
+ case 'document':
215
+ tmpHTML += this._buildDocumentHTML(tmpContentURL, tmpFileName, pFilePath);
216
+ break;
217
+ default:
218
+ tmpHTML += this._buildFallbackHTML(tmpContentURL, tmpFileName);
219
+ break;
220
+ }
221
+
222
+ // File info overlay (hidden by default)
223
+ tmpHTML += '<div class="retold-remote-fileinfo-overlay" id="RetoldRemote-FileInfo-Overlay">';
224
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Loading...</span></div>';
225
+ tmpHTML += '</div>';
226
+
227
+ tmpHTML += '</div>'; // end body
228
+ tmpHTML += '</div>'; // end viewer
229
+
230
+ if (tmpViewerContainer)
231
+ {
232
+ tmpViewerContainer.innerHTML = tmpHTML;
233
+ }
234
+
235
+ // Fetch and populate file info
236
+ this._loadFileInfo(pFilePath);
237
+
238
+ // Fetch text content and initialize code viewer
239
+ if (pMediaType === 'text')
240
+ {
241
+ this._loadCodeViewer(tmpContentURL, pFilePath);
242
+ }
243
+
244
+ // Update topbar
245
+ let tmpTopBar = this.pict.views['ContentEditor-TopBar'];
246
+ if (tmpTopBar)
247
+ {
248
+ tmpTopBar.updateInfo();
249
+ }
250
+ }
251
+
252
+ _buildImageHTML(pURL, pFileName)
253
+ {
254
+ return '<img src="' + pURL + '" alt="' + this._escapeHTML(pFileName) + '" '
255
+ + 'style="max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;" '
256
+ + 'id="RetoldRemote-ImageViewer-Img" '
257
+ + 'onload="pict.views[\'RetoldRemote-ImageViewer\'].initImage()" '
258
+ + 'onclick="pict.views[\'RetoldRemote-ImageViewer\'].toggleZoom()">';
259
+ }
260
+
261
+ _buildVideoHTML(pURL, pFileName)
262
+ {
263
+ return '<video controls autoplay preload="metadata" '
264
+ + 'style="max-width: 100%; max-height: 100%;" '
265
+ + 'id="RetoldRemote-VideoPlayer">'
266
+ + '<source src="' + pURL + '">'
267
+ + 'Your browser does not support the video tag.'
268
+ + '</video>';
269
+ }
270
+
271
+ _buildAudioHTML(pURL, pFileName)
272
+ {
273
+ let tmpIconProvider = this.pict.providers['RetoldRemote-Icons'];
274
+ let tmpIconHTML = tmpIconProvider ? '<span class="retold-remote-icon retold-remote-icon-lg">' + tmpIconProvider.getIcon('music-note', 64) + '</span>' : '&#127925;';
275
+ return '<div style="text-align: center; padding: 40px;">'
276
+ + '<div style="margin-bottom: 24px;">' + tmpIconHTML + '</div>'
277
+ + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this._escapeHTML(pFileName) + '</div>'
278
+ + '<audio controls autoplay preload="metadata" id="RetoldRemote-AudioPlayer" style="width: 100%; max-width: 500px;">'
279
+ + '<source src="' + pURL + '">'
280
+ + 'Your browser does not support the audio tag.'
281
+ + '</audio>'
282
+ + '</div>';
283
+ }
284
+
285
+ _buildDocumentHTML(pURL, pFileName, pFilePath)
286
+ {
287
+ let tmpExtension = pFilePath.replace(/^.*\./, '').toLowerCase();
288
+
289
+ if (tmpExtension === 'pdf')
290
+ {
291
+ return '<iframe src="' + pURL + '" '
292
+ + 'style="width: 100%; height: 100%; border: none;">'
293
+ + '</iframe>';
294
+ }
295
+
296
+ // For other document types, show a download link
297
+ let tmpIconProvider = this.pict.providers['RetoldRemote-Icons'];
298
+ let tmpDocIconHTML = tmpIconProvider ? '<span class="retold-remote-icon retold-remote-icon-lg">' + tmpIconProvider.getIcon('document-large', 64) + '</span>' : '&#128196;';
299
+ return '<div style="text-align: center; padding: 40px;">'
300
+ + '<div style="margin-bottom: 24px;">' + tmpDocIconHTML + '</div>'
301
+ + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this._escapeHTML(pFileName) + '</div>'
302
+ + '<a href="' + pURL + '" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Open in new tab</a>'
303
+ + '</div>';
304
+ }
305
+
306
+ _buildTextHTML(pURL, pFileName, pFilePath)
307
+ {
308
+ return '<div class="retold-remote-code-viewer-container" id="RetoldRemote-CodeViewer-Container">'
309
+ + '<div class="retold-remote-code-viewer-loading">Loading...</div>'
310
+ + '</div>';
311
+ }
312
+
313
+ /**
314
+ * Map a file extension to a pict-section-code highlight language.
315
+ *
316
+ * @param {string} pExtension - File extension (no dot)
317
+ * @returns {string} One of: javascript, json, html, css, sql
318
+ */
319
+ _getHighlightLanguage(pExtension)
320
+ {
321
+ let tmpJSExtensions = { 'js': true, 'mjs': true, 'cjs': true, 'ts': true, 'tsx': true, 'jsx': true };
322
+ if (tmpJSExtensions[pExtension]) return 'javascript';
323
+
324
+ if (pExtension === 'json') return 'json';
325
+
326
+ let tmpHTMLExtensions = { 'html': true, 'htm': true, 'xml': true, 'svg': true };
327
+ if (tmpHTMLExtensions[pExtension]) return 'html';
328
+
329
+ let tmpCSSExtensions = { 'css': true, 'scss': true, 'sass': true, 'less': true };
330
+ if (tmpCSSExtensions[pExtension]) return 'css';
331
+
332
+ if (pExtension === 'sql') return 'sql';
333
+
334
+ // Default to javascript highlighting for other text files
335
+ return 'javascript';
336
+ }
337
+
338
+ /**
339
+ * Load text content and display it using pict-section-code for
340
+ * syntax highlighting and line numbers.
341
+ *
342
+ * @param {string} pURL - Content URL to fetch
343
+ * @param {string} pFilePath - Full file path (used to derive extension)
344
+ */
345
+ _loadCodeViewer(pURL, pFilePath)
346
+ {
347
+ let tmpSelf = this;
348
+ let tmpExtension = pFilePath.replace(/^.*\./, '').toLowerCase();
349
+
350
+ fetch(pURL)
351
+ .then((pResponse) =>
352
+ {
353
+ if (!pResponse.ok)
354
+ {
355
+ throw new Error('HTTP ' + pResponse.status);
356
+ }
357
+ return pResponse.text();
358
+ })
359
+ .then((pText) =>
360
+ {
361
+ let tmpContainer = document.getElementById('RetoldRemote-CodeViewer-Container');
362
+ if (!tmpContainer)
363
+ {
364
+ return;
365
+ }
366
+
367
+ let tmpLanguage = tmpSelf._getHighlightLanguage(tmpExtension);
368
+
369
+ // Destroy any previous CodeJar instance
370
+ if (tmpSelf._activeCodeJar)
371
+ {
372
+ tmpSelf._activeCodeJar.destroy();
373
+ tmpSelf._activeCodeJar = null;
374
+ }
375
+
376
+ // Check that CodeJar is available
377
+ if (typeof (window) === 'undefined' || typeof (window.CodeJar) !== 'function')
378
+ {
379
+ tmpContainer.innerHTML = '<div class="retold-remote-code-viewer-loading">CodeJar not loaded; showing plain text.</div>';
380
+ let tmpPre = document.createElement('pre');
381
+ tmpPre.style.cssText = 'padding:16px 20px; margin:0; color:var(--retold-text-primary); font-family:monospace; white-space:pre; tab-size:4; overflow:auto; height:100%;';
382
+ tmpPre.textContent = pText;
383
+ tmpContainer.appendChild(tmpPre);
384
+ return;
385
+ }
386
+
387
+ // Build the editor DOM (mirrors pict-section-code _buildEditorDOM)
388
+ tmpContainer.innerHTML = '';
389
+
390
+ let tmpWrap = document.createElement('div');
391
+ tmpWrap.className = 'pict-code-editor-wrap';
392
+
393
+ let tmpLineNumbers = document.createElement('div');
394
+ tmpLineNumbers.className = 'pict-code-line-numbers';
395
+ tmpWrap.appendChild(tmpLineNumbers);
396
+
397
+ let tmpEditor = document.createElement('div');
398
+ tmpEditor.className = 'pict-code-editor language-' + tmpLanguage;
399
+ tmpWrap.appendChild(tmpEditor);
400
+
401
+ tmpContainer.appendChild(tmpWrap);
402
+
403
+ // Create the highlight function from pict-section-code
404
+ let tmpHighlight = libPictSectionCode.createHighlighter(tmpLanguage);
405
+
406
+ // Instantiate CodeJar
407
+ tmpSelf._activeCodeJar = window.CodeJar(tmpEditor, tmpHighlight,
408
+ {
409
+ tab: '\t',
410
+ catchTab: false,
411
+ addClosing: false
412
+ });
413
+
414
+ // Reset inline styles that CodeJar forces
415
+ tmpEditor.style.whiteSpace = 'pre';
416
+ tmpEditor.style.overflowWrap = 'normal';
417
+
418
+ // Set the content
419
+ tmpSelf._activeCodeJar.updateCode(pText);
420
+
421
+ // Make it read-only
422
+ tmpEditor.setAttribute('contenteditable', 'false');
423
+
424
+ // Render line numbers
425
+ let tmpLineCount = pText.split('\n').length;
426
+ let tmpLineHTML = '';
427
+ for (let i = 1; i <= tmpLineCount; i++)
428
+ {
429
+ tmpLineHTML += '<span>' + i + '</span>';
430
+ }
431
+ tmpLineNumbers.innerHTML = tmpLineHTML;
432
+ })
433
+ .catch((pError) =>
434
+ {
435
+ let tmpContainer = document.getElementById('RetoldRemote-CodeViewer-Container');
436
+ if (tmpContainer)
437
+ {
438
+ tmpContainer.innerHTML = '<div class="retold-remote-code-viewer-loading">Failed to load file: ' + pError.message + '</div>';
439
+ }
440
+ });
441
+ }
442
+
443
+ _buildFallbackHTML(pURL, pFileName)
444
+ {
445
+ let tmpIconProvider = this.pict.providers['RetoldRemote-Icons'];
446
+ let tmpFallbackIconHTML = tmpIconProvider ? '<span class="retold-remote-icon retold-remote-icon-lg">' + tmpIconProvider.getIcon('document-large', 64) + '</span>' : '&#128196;';
447
+ return '<div style="text-align: center; padding: 40px;">'
448
+ + '<div style="margin-bottom: 24px;">' + tmpFallbackIconHTML + '</div>'
449
+ + '<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">' + this._escapeHTML(pFileName) + '</div>'
450
+ + '<a href="' + pURL + '" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Download / Open in new tab</a>'
451
+ + '</div>';
452
+ }
453
+
454
+ /**
455
+ * Fetch file info and populate the overlay.
456
+ */
457
+ _loadFileInfo(pFilePath)
458
+ {
459
+ let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
460
+ if (!tmpProvider)
461
+ {
462
+ return;
463
+ }
464
+
465
+ tmpProvider.fetchMediaProbe(pFilePath,
466
+ (pError, pData) =>
467
+ {
468
+ let tmpOverlay = document.getElementById('RetoldRemote-FileInfo-Overlay');
469
+ if (!tmpOverlay || !pData)
470
+ {
471
+ return;
472
+ }
473
+
474
+ let tmpHTML = '';
475
+
476
+ if (pData.Size !== undefined)
477
+ {
478
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Size</span><span class="retold-remote-fileinfo-value">' + this._formatFileSize(pData.Size) + '</span></div>';
479
+ }
480
+ if (pData.Width && pData.Height)
481
+ {
482
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Dimensions</span><span class="retold-remote-fileinfo-value">' + pData.Width + ' x ' + pData.Height + '</span></div>';
483
+ }
484
+ if (pData.Duration)
485
+ {
486
+ let tmpMin = Math.floor(pData.Duration / 60);
487
+ let tmpSec = Math.floor(pData.Duration % 60);
488
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Duration</span><span class="retold-remote-fileinfo-value">' + tmpMin + ':' + (tmpSec < 10 ? '0' : '') + tmpSec + '</span></div>';
489
+ }
490
+ if (pData.Codec)
491
+ {
492
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Codec</span><span class="retold-remote-fileinfo-value">' + pData.Codec + '</span></div>';
493
+ }
494
+ if (pData.Format)
495
+ {
496
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Format</span><span class="retold-remote-fileinfo-value">' + pData.Format + '</span></div>';
497
+ }
498
+ if (pData.Modified)
499
+ {
500
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Modified</span><span class="retold-remote-fileinfo-value">' + new Date(pData.Modified).toLocaleString() + '</span></div>';
501
+ }
502
+ if (pData.Path)
503
+ {
504
+ tmpHTML += '<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Path</span><span class="retold-remote-fileinfo-value">' + pData.Path + '</span></div>';
505
+ }
506
+
507
+ tmpOverlay.innerHTML = tmpHTML;
508
+ });
509
+ }
510
+
511
+ _formatFileSize(pBytes)
512
+ {
513
+ if (!pBytes || pBytes === 0) return '0 B';
514
+ let tmpUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
515
+ let tmpIndex = Math.floor(Math.log(pBytes) / Math.log(1024));
516
+ if (tmpIndex >= tmpUnits.length) tmpIndex = tmpUnits.length - 1;
517
+ let tmpSize = pBytes / Math.pow(1024, tmpIndex);
518
+ return tmpSize.toFixed(tmpIndex === 0 ? 0 : 1) + ' ' + tmpUnits[tmpIndex];
519
+ }
520
+
521
+ _escapeHTML(pText)
522
+ {
523
+ if (!pText) return '';
524
+ return pText.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
525
+ }
526
+ }
527
+
528
+ RetoldRemoteMediaViewerView.default_configuration = _ViewConfiguration;
529
+
530
+ module.exports = RetoldRemoteMediaViewerView;