vgapp 1.1.6 → 1.1.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 (54) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +48 -48
  3. package/app/langs/en/buttons.json +17 -17
  4. package/app/langs/en/messages.json +36 -36
  5. package/app/langs/ru/buttons.json +17 -17
  6. package/app/langs/ru/messages.json +36 -36
  7. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  8. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  9. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  10. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  11. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  12. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  13. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  14. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  15. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  16. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  17. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  18. package/app/modules/vgfilepreview/js/renderers/video-modal.js +272 -272
  19. package/app/modules/vgfilepreview/js/renderers/video.js +80 -80
  20. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  21. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  22. package/app/modules/vgfilepreview/js/vgfilepreview.js +7 -7
  23. package/app/modules/vgfilepreview/readme.md +68 -68
  24. package/app/modules/vgfilepreview/scss/_variables.scss +113 -113
  25. package/app/modules/vgfilepreview/scss/vgfilepreview.scss +464 -464
  26. package/app/modules/vgfiles/js/base.js +26 -26
  27. package/app/modules/vgfiles/js/droppable.js +260 -260
  28. package/app/modules/vgfiles/js/render.js +153 -153
  29. package/app/modules/vgfiles/js/vgfiles.js +41 -41
  30. package/app/modules/vgfiles/readme.md +123 -123
  31. package/app/modules/vgfiles/scss/_variables.scss +18 -18
  32. package/app/modules/vgfiles/scss/vgfiles.scss +148 -148
  33. package/app/modules/vgformsender/js/vgformsender.js +1 -1
  34. package/app/modules/vgmodal/js/vgmodal.drag.js +332 -332
  35. package/app/modules/vgmodal/js/vgmodal.js +33 -33
  36. package/app/modules/vgmodal/js/vgmodal.resize.js +435 -435
  37. package/app/modules/vgnav/js/vgnav.js +135 -135
  38. package/app/modules/vgnav/readme.md +67 -67
  39. package/app/modules/vgnestable/README.md +307 -307
  40. package/app/modules/vgnestable/scss/_variables.scss +60 -60
  41. package/app/modules/vgnestable/scss/vgnestable.scss +163 -163
  42. package/app/modules/vgselect/js/vgselect.js +39 -39
  43. package/app/modules/vgselect/scss/vgselect.scss +22 -22
  44. package/app/modules/vgspy/readme.md +28 -28
  45. package/app/utils/js/components/audio-metadata.js +240 -240
  46. package/app/utils/js/components/file-icon.js +109 -109
  47. package/app/utils/js/components/file-preview.js +304 -304
  48. package/app/utils/js/components/sanitize.js +150 -150
  49. package/app/utils/js/components/video-metadata.js +140 -140
  50. package/build/vgapp.css +1 -1
  51. package/build/vgapp.css.map +1 -1
  52. package/build/vgapp.js.map +1 -1
  53. package/index.scss +9 -9
  54. package/package.json +1 -1
@@ -1,343 +1,343 @@
1
- import VGModal from "../../../vgmodal";
2
-
3
- class TextModal {
4
- constructor() {
5
- this._modalId = 'vg-filepreview-text-modal';
6
- this._abortController = null;
7
- this._labels = {};
8
- }
9
-
10
- static getInstance() {
11
- if (!TextModal._instance) {
12
- TextModal._instance = new TextModal();
13
- }
14
-
15
- return TextModal._instance;
16
- }
17
-
18
- open(payload = {}) {
19
- const src = String(payload.src || '').trim();
20
- if (!src) {
21
- return;
22
- }
23
-
24
- this._ensureModal();
25
- if (!this._modal || !this._content) {
26
- return;
27
- }
28
-
29
- this._labels = payload?.labels && typeof payload.labels === 'object' ? payload.labels : {};
30
- const defaultTitle = String(payload.defaultTitle || '').trim();
31
- const title = String(payload.title || '').trim();
32
- const ext = String(payload.ext || '').toLowerCase();
33
- this._title.textContent = title || defaultTitle;
34
- this._content.textContent = this._label('loading');
35
- this._content.classList.remove('is-markdown');
36
- this._modal.show();
37
-
38
- this._loadText(src, {ext});
39
- }
40
-
41
- close() {
42
- if (!this._modal) {
43
- return;
44
- }
45
-
46
- this._modal.hide();
47
- }
48
-
49
- _ensureModal() {
50
- if (this._modal && this._root) {
51
- return;
52
- }
53
-
54
- this._initModal();
55
- }
56
-
57
- _initModal() {
58
- const params = {
59
- centered: true,
60
- dismiss: true,
61
- backdrop: true,
62
- keyboard: true,
63
- sizes: {
64
- width: '600px',
65
- height: '',
66
- },
67
- animation: {
68
- enable: false
69
- }
70
- };
71
-
72
- const existed = document.getElementById(this._modalId);
73
- if (existed) {
74
- this._root = existed;
75
- this._modal = VGModal.getOrCreateInstance(existed, params);
76
- this._bindElements(existed);
77
- this._bindLifecycle(existed);
78
- return;
79
- }
80
-
81
- this._modal = VGModal.build(this._modalId, params, (modalInstance) => {
82
- const element = modalInstance._element;
83
- this._root = element;
84
- element.classList.add('vg-filepreview-text-modal');
85
-
86
- const body = element.querySelector('.vg-modal-body');
87
- const content = element.querySelector('.vg-modal-content');
88
- if (!body || !content) {
89
- return;
90
- }
91
-
92
- body.classList.add('vg-filepreview-text-modal__body');
93
- body.innerHTML = '';
94
-
95
- let header = element.querySelector('.vg-modal-header');
96
- if (!header) {
97
- header = document.createElement('div');
98
- header.className = 'vg-modal-header';
99
- content.prepend(header);
100
- }
101
-
102
- this._title = document.createElement('div');
103
- this._title.className = 'vg-modal-title';
104
- this._title.textContent = '';
105
-
106
- this._content = document.createElement('pre');
107
- this._content.className = 'vg-filepreview-text-modal__content';
108
- this._content.textContent = '';
109
-
110
- header.appendChild(this._title);
111
- body.appendChild(this._content);
112
-
113
- this._bindLifecycle(element);
114
- });
115
- }
116
-
117
- _bindElements(root) {
118
- this._title = root.querySelector('.vg-filepreview-text-modal__title');
119
- this._content = root.querySelector('.vg-filepreview-text-modal__content');
120
- }
121
-
122
- _bindLifecycle(root) {
123
- if (!root || root.hasAttribute('data-vg-filepreview-text-lifecycle-bind')) {
124
- return;
125
- }
126
-
127
- root.setAttribute('data-vg-filepreview-text-lifecycle-bind', 'true');
128
- root.addEventListener('vg.modal.hidden', () => {
129
- this._destroyModal();
130
- });
131
- }
132
-
133
- _loadText(src, options = {}) {
134
- const ext = String(options.ext || '').toLowerCase();
135
- const cacheKey = `${src}|${ext}`;
136
-
137
- if (TextModal._cache.has(cacheKey)) {
138
- const cached = TextModal._cache.get(cacheKey);
139
- this._renderLoadedText(cached, ext);
140
- return;
141
- }
142
-
143
- if (this._abortController) {
144
- this._abortController.abort();
145
- }
146
-
147
- this._abortController = new AbortController();
148
-
149
- fetch(src, {
150
- method: 'GET',
151
- signal: this._abortController.signal
152
- })
153
- .then((response) => {
154
- if (!response.ok) {
155
- throw new Error(`HTTP ${response.status}`);
156
- }
157
-
158
- return response.text();
159
- })
160
- .then((text) => {
161
- const normalized = text || '';
162
- TextModal._cache.set(cacheKey, normalized);
163
- this._renderLoadedText(normalized, ext);
164
- })
165
- .catch((error) => {
166
- if (!this._content) {
167
- return;
168
- }
169
-
170
- if (error?.name === 'AbortError') {
171
- return;
172
- }
173
-
174
- this._content.classList.remove('is-markdown');
175
- this._content.textContent = `${this._label('cannotOpen')}: ${error?.message || this._label('unknownError')}`;
176
- });
177
- }
178
-
179
- _renderLoadedText(text, ext) {
180
- if (!this._content) {
181
- return;
182
- }
183
-
184
- const normalized = String(text || '');
185
- if (ext === '.md') {
186
- this._content.classList.add('is-markdown');
187
- this._content.innerHTML = this._renderMarkdown(normalized);
188
- } else {
189
- this._content.classList.remove('is-markdown');
190
- this._content.textContent = normalized || this._label('emptyFile');
191
- }
192
- }
193
-
194
- _destroyModal() {
195
- if (this._abortController) {
196
- this._abortController.abort();
197
- this._abortController = null;
198
- }
199
-
200
- if (this._modal && typeof this._modal.dispose === 'function') {
201
- this._modal.dispose();
202
- }
203
-
204
- if (this._root && this._root.parentNode) {
205
- this._root.parentNode.removeChild(this._root);
206
- }
207
-
208
- this._root = null;
209
- this._modal = null;
210
- this._title = null;
211
- this._content = null;
212
- this._labels = null;
213
- }
214
-
215
- _label(key, fallback = '') {
216
- const value = this._labels?.[key];
217
- return typeof value === 'string' && value.trim() ? value : fallback;
218
- }
219
-
220
- _renderMarkdown(markdownText) {
221
- const escapeHtml = (value) => String(value)
222
- .replace(/&/g, '&')
223
- .replace(/</g, '&lt;')
224
- .replace(/>/g, '&gt;')
225
- .replace(/"/g, '&quot;')
226
- .replace(/'/g, '&#39;');
227
-
228
- const inline = (line) => {
229
- let result = escapeHtml(line);
230
- result = result.replace(/`([^`]+)`/g, '<code>$1</code>');
231
- result = result.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
232
- result = result.replace(/\*([^*]+)\*/g, '<em>$1</em>');
233
- result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, href) => {
234
- const safeHref = this._sanitizeUrl(href);
235
- return `<a href="${safeHref}" target="_blank" rel="noopener noreferrer">${text}</a>`;
236
- });
237
- return result;
238
- };
239
-
240
- const lines = String(markdownText || '').split(/\r?\n/);
241
- const html = [];
242
- let inList = false;
243
- let inCode = false;
244
-
245
- lines.forEach((rawLine) => {
246
- const line = rawLine || '';
247
-
248
- if (line.trim().startsWith('```')) {
249
- if (!inCode) {
250
- inCode = true;
251
- html.push('<pre><code>');
252
- } else {
253
- inCode = false;
254
- html.push('</code></pre>');
255
- }
256
- return;
257
- }
258
-
259
- if (inCode) {
260
- html.push(`${escapeHtml(line)}\n`);
261
- return;
262
- }
263
-
264
- if (!line.trim()) {
265
- if (inList) {
266
- inList = false;
267
- html.push('</ul>');
268
- }
269
- return;
270
- }
271
-
272
- const heading = line.match(/^(#{1,6})\s+(.+)$/);
273
- if (heading) {
274
- if (inList) {
275
- inList = false;
276
- html.push('</ul>');
277
- }
278
-
279
- const level = heading[1].length;
280
- html.push(`<h${level}>${inline(heading[2])}</h${level}>`);
281
- return;
282
- }
283
-
284
- const listItem = line.match(/^[-*]\s+(.+)$/);
285
- if (listItem) {
286
- if (!inList) {
287
- inList = true;
288
- html.push('<ul>');
289
- }
290
- html.push(`<li>${inline(listItem[1])}</li>`);
291
- return;
292
- }
293
-
294
- if (inList) {
295
- inList = false;
296
- html.push('</ul>');
297
- }
298
-
299
- html.push(`<p>${inline(line)}</p>`);
300
- });
301
-
302
- if (inList) {
303
- html.push('</ul>');
304
- }
305
- if (inCode) {
306
- html.push('</code></pre>');
307
- }
308
-
309
- return html.join('') || `<p>${this._escapeHtml(this._label('emptyFile'))}</p>`;
310
- }
311
-
312
- _escapeHtml(value) {
313
- return String(value)
314
- .replace(/&/g, '&amp;')
315
- .replace(/</g, '&lt;')
316
- .replace(/>/g, '&gt;')
317
- .replace(/"/g, '&quot;')
318
- .replace(/'/g, '&#39;');
319
- }
320
-
321
- _sanitizeUrl(url) {
322
- const value = String(url || '').trim();
323
- if (!value) {
324
- return '#';
325
- }
326
-
327
- try {
328
- const parsed = new URL(value, window.location.origin);
329
- if (['http:', 'https:', 'mailto:'].includes(parsed.protocol)) {
330
- return parsed.href;
331
- }
332
- } catch {
333
- return '#';
334
- }
335
-
336
- return '#';
337
- }
338
-
339
- }
340
-
341
- TextModal._cache = new Map();
342
-
343
- export default TextModal;
1
+ import VGModal from "../../../vgmodal";
2
+
3
+ class TextModal {
4
+ constructor() {
5
+ this._modalId = 'vg-filepreview-text-modal';
6
+ this._abortController = null;
7
+ this._labels = {};
8
+ }
9
+
10
+ static getInstance() {
11
+ if (!TextModal._instance) {
12
+ TextModal._instance = new TextModal();
13
+ }
14
+
15
+ return TextModal._instance;
16
+ }
17
+
18
+ open(payload = {}) {
19
+ const src = String(payload.src || '').trim();
20
+ if (!src) {
21
+ return;
22
+ }
23
+
24
+ this._ensureModal();
25
+ if (!this._modal || !this._content) {
26
+ return;
27
+ }
28
+
29
+ this._labels = payload?.labels && typeof payload.labels === 'object' ? payload.labels : {};
30
+ const defaultTitle = String(payload.defaultTitle || '').trim();
31
+ const title = String(payload.title || '').trim();
32
+ const ext = String(payload.ext || '').toLowerCase();
33
+ this._title.textContent = title || defaultTitle;
34
+ this._content.textContent = this._label('loading');
35
+ this._content.classList.remove('is-markdown');
36
+ this._modal.show();
37
+
38
+ this._loadText(src, {ext});
39
+ }
40
+
41
+ close() {
42
+ if (!this._modal) {
43
+ return;
44
+ }
45
+
46
+ this._modal.hide();
47
+ }
48
+
49
+ _ensureModal() {
50
+ if (this._modal && this._root) {
51
+ return;
52
+ }
53
+
54
+ this._initModal();
55
+ }
56
+
57
+ _initModal() {
58
+ const params = {
59
+ centered: true,
60
+ dismiss: true,
61
+ backdrop: true,
62
+ keyboard: true,
63
+ sizes: {
64
+ width: '600px',
65
+ height: '',
66
+ },
67
+ animation: {
68
+ enable: false
69
+ }
70
+ };
71
+
72
+ const existed = document.getElementById(this._modalId);
73
+ if (existed) {
74
+ this._root = existed;
75
+ this._modal = VGModal.getOrCreateInstance(existed, params);
76
+ this._bindElements(existed);
77
+ this._bindLifecycle(existed);
78
+ return;
79
+ }
80
+
81
+ this._modal = VGModal.build(this._modalId, params, (modalInstance) => {
82
+ const element = modalInstance._element;
83
+ this._root = element;
84
+ element.classList.add('vg-filepreview-text-modal');
85
+
86
+ const body = element.querySelector('.vg-modal-body');
87
+ const content = element.querySelector('.vg-modal-content');
88
+ if (!body || !content) {
89
+ return;
90
+ }
91
+
92
+ body.classList.add('vg-filepreview-text-modal__body');
93
+ body.innerHTML = '';
94
+
95
+ let header = element.querySelector('.vg-modal-header');
96
+ if (!header) {
97
+ header = document.createElement('div');
98
+ header.className = 'vg-modal-header';
99
+ content.prepend(header);
100
+ }
101
+
102
+ this._title = document.createElement('div');
103
+ this._title.className = 'vg-modal-title';
104
+ this._title.textContent = '';
105
+
106
+ this._content = document.createElement('pre');
107
+ this._content.className = 'vg-filepreview-text-modal__content';
108
+ this._content.textContent = '';
109
+
110
+ header.appendChild(this._title);
111
+ body.appendChild(this._content);
112
+
113
+ this._bindLifecycle(element);
114
+ });
115
+ }
116
+
117
+ _bindElements(root) {
118
+ this._title = root.querySelector('.vg-filepreview-text-modal__title');
119
+ this._content = root.querySelector('.vg-filepreview-text-modal__content');
120
+ }
121
+
122
+ _bindLifecycle(root) {
123
+ if (!root || root.hasAttribute('data-vg-filepreview-text-lifecycle-bind')) {
124
+ return;
125
+ }
126
+
127
+ root.setAttribute('data-vg-filepreview-text-lifecycle-bind', 'true');
128
+ root.addEventListener('vg.modal.hidden', () => {
129
+ this._destroyModal();
130
+ });
131
+ }
132
+
133
+ _loadText(src, options = {}) {
134
+ const ext = String(options.ext || '').toLowerCase();
135
+ const cacheKey = `${src}|${ext}`;
136
+
137
+ if (TextModal._cache.has(cacheKey)) {
138
+ const cached = TextModal._cache.get(cacheKey);
139
+ this._renderLoadedText(cached, ext);
140
+ return;
141
+ }
142
+
143
+ if (this._abortController) {
144
+ this._abortController.abort();
145
+ }
146
+
147
+ this._abortController = new AbortController();
148
+
149
+ fetch(src, {
150
+ method: 'GET',
151
+ signal: this._abortController.signal
152
+ })
153
+ .then((response) => {
154
+ if (!response.ok) {
155
+ throw new Error(`HTTP ${response.status}`);
156
+ }
157
+
158
+ return response.text();
159
+ })
160
+ .then((text) => {
161
+ const normalized = text || '';
162
+ TextModal._cache.set(cacheKey, normalized);
163
+ this._renderLoadedText(normalized, ext);
164
+ })
165
+ .catch((error) => {
166
+ if (!this._content) {
167
+ return;
168
+ }
169
+
170
+ if (error?.name === 'AbortError') {
171
+ return;
172
+ }
173
+
174
+ this._content.classList.remove('is-markdown');
175
+ this._content.textContent = `${this._label('cannotOpen')}: ${error?.message || this._label('unknownError')}`;
176
+ });
177
+ }
178
+
179
+ _renderLoadedText(text, ext) {
180
+ if (!this._content) {
181
+ return;
182
+ }
183
+
184
+ const normalized = String(text || '');
185
+ if (ext === '.md') {
186
+ this._content.classList.add('is-markdown');
187
+ this._content.innerHTML = this._renderMarkdown(normalized);
188
+ } else {
189
+ this._content.classList.remove('is-markdown');
190
+ this._content.textContent = normalized || this._label('emptyFile');
191
+ }
192
+ }
193
+
194
+ _destroyModal() {
195
+ if (this._abortController) {
196
+ this._abortController.abort();
197
+ this._abortController = null;
198
+ }
199
+
200
+ if (this._modal && typeof this._modal.dispose === 'function') {
201
+ this._modal.dispose();
202
+ }
203
+
204
+ if (this._root && this._root.parentNode) {
205
+ this._root.parentNode.removeChild(this._root);
206
+ }
207
+
208
+ this._root = null;
209
+ this._modal = null;
210
+ this._title = null;
211
+ this._content = null;
212
+ this._labels = null;
213
+ }
214
+
215
+ _label(key, fallback = '') {
216
+ const value = this._labels?.[key];
217
+ return typeof value === 'string' && value.trim() ? value : fallback;
218
+ }
219
+
220
+ _renderMarkdown(markdownText) {
221
+ const escapeHtml = (value) => String(value)
222
+ .replace(/&/g, '&amp;')
223
+ .replace(/</g, '&lt;')
224
+ .replace(/>/g, '&gt;')
225
+ .replace(/"/g, '&quot;')
226
+ .replace(/'/g, '&#39;');
227
+
228
+ const inline = (line) => {
229
+ let result = escapeHtml(line);
230
+ result = result.replace(/`([^`]+)`/g, '<code>$1</code>');
231
+ result = result.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
232
+ result = result.replace(/\*([^*]+)\*/g, '<em>$1</em>');
233
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, href) => {
234
+ const safeHref = this._sanitizeUrl(href);
235
+ return `<a href="${safeHref}" target="_blank" rel="noopener noreferrer">${text}</a>`;
236
+ });
237
+ return result;
238
+ };
239
+
240
+ const lines = String(markdownText || '').split(/\r?\n/);
241
+ const html = [];
242
+ let inList = false;
243
+ let inCode = false;
244
+
245
+ lines.forEach((rawLine) => {
246
+ const line = rawLine || '';
247
+
248
+ if (line.trim().startsWith('```')) {
249
+ if (!inCode) {
250
+ inCode = true;
251
+ html.push('<pre><code>');
252
+ } else {
253
+ inCode = false;
254
+ html.push('</code></pre>');
255
+ }
256
+ return;
257
+ }
258
+
259
+ if (inCode) {
260
+ html.push(`${escapeHtml(line)}\n`);
261
+ return;
262
+ }
263
+
264
+ if (!line.trim()) {
265
+ if (inList) {
266
+ inList = false;
267
+ html.push('</ul>');
268
+ }
269
+ return;
270
+ }
271
+
272
+ const heading = line.match(/^(#{1,6})\s+(.+)$/);
273
+ if (heading) {
274
+ if (inList) {
275
+ inList = false;
276
+ html.push('</ul>');
277
+ }
278
+
279
+ const level = heading[1].length;
280
+ html.push(`<h${level}>${inline(heading[2])}</h${level}>`);
281
+ return;
282
+ }
283
+
284
+ const listItem = line.match(/^[-*]\s+(.+)$/);
285
+ if (listItem) {
286
+ if (!inList) {
287
+ inList = true;
288
+ html.push('<ul>');
289
+ }
290
+ html.push(`<li>${inline(listItem[1])}</li>`);
291
+ return;
292
+ }
293
+
294
+ if (inList) {
295
+ inList = false;
296
+ html.push('</ul>');
297
+ }
298
+
299
+ html.push(`<p>${inline(line)}</p>`);
300
+ });
301
+
302
+ if (inList) {
303
+ html.push('</ul>');
304
+ }
305
+ if (inCode) {
306
+ html.push('</code></pre>');
307
+ }
308
+
309
+ return html.join('') || `<p>${this._escapeHtml(this._label('emptyFile'))}</p>`;
310
+ }
311
+
312
+ _escapeHtml(value) {
313
+ return String(value)
314
+ .replace(/&/g, '&amp;')
315
+ .replace(/</g, '&lt;')
316
+ .replace(/>/g, '&gt;')
317
+ .replace(/"/g, '&quot;')
318
+ .replace(/'/g, '&#39;');
319
+ }
320
+
321
+ _sanitizeUrl(url) {
322
+ const value = String(url || '').trim();
323
+ if (!value) {
324
+ return '#';
325
+ }
326
+
327
+ try {
328
+ const parsed = new URL(value, window.location.origin);
329
+ if (['http:', 'https:', 'mailto:'].includes(parsed.protocol)) {
330
+ return parsed.href;
331
+ }
332
+ } catch {
333
+ return '#';
334
+ }
335
+
336
+ return '#';
337
+ }
338
+
339
+ }
340
+
341
+ TextModal._cache = new Map();
342
+
343
+ export default TextModal;