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.
- package/CHANGELOG.md +10 -1
- package/README.md +48 -48
- package/app/langs/en/buttons.json +17 -17
- package/app/langs/en/messages.json +36 -36
- package/app/langs/ru/buttons.json +17 -17
- package/app/langs/ru/messages.json +36 -36
- package/app/modules/vgfilepreview/js/i18n.js +56 -56
- package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
- package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
- package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
- package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
- package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
- package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
- package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
- package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
- package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
- package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
- package/app/modules/vgfilepreview/js/renderers/video-modal.js +272 -272
- package/app/modules/vgfilepreview/js/renderers/video.js +80 -80
- package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
- package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
- package/app/modules/vgfilepreview/js/vgfilepreview.js +7 -7
- package/app/modules/vgfilepreview/readme.md +68 -68
- package/app/modules/vgfilepreview/scss/_variables.scss +113 -113
- package/app/modules/vgfilepreview/scss/vgfilepreview.scss +464 -464
- package/app/modules/vgfiles/js/base.js +26 -26
- package/app/modules/vgfiles/js/droppable.js +260 -260
- package/app/modules/vgfiles/js/render.js +153 -153
- package/app/modules/vgfiles/js/vgfiles.js +41 -41
- package/app/modules/vgfiles/readme.md +123 -123
- package/app/modules/vgfiles/scss/_variables.scss +18 -18
- package/app/modules/vgfiles/scss/vgfiles.scss +148 -148
- package/app/modules/vgformsender/js/vgformsender.js +1 -1
- package/app/modules/vgmodal/js/vgmodal.drag.js +332 -332
- package/app/modules/vgmodal/js/vgmodal.js +33 -33
- package/app/modules/vgmodal/js/vgmodal.resize.js +435 -435
- package/app/modules/vgnav/js/vgnav.js +135 -135
- package/app/modules/vgnav/readme.md +67 -67
- package/app/modules/vgnestable/README.md +307 -307
- package/app/modules/vgnestable/scss/_variables.scss +60 -60
- package/app/modules/vgnestable/scss/vgnestable.scss +163 -163
- package/app/modules/vgselect/js/vgselect.js +39 -39
- package/app/modules/vgselect/scss/vgselect.scss +22 -22
- package/app/modules/vgspy/readme.md +28 -28
- package/app/utils/js/components/audio-metadata.js +240 -240
- package/app/utils/js/components/file-icon.js +109 -109
- package/app/utils/js/components/file-preview.js +304 -304
- package/app/utils/js/components/sanitize.js +150 -150
- package/app/utils/js/components/video-metadata.js +140 -140
- package/build/vgapp.css +1 -1
- package/build/vgapp.css.map +1 -1
- package/build/vgapp.js.map +1 -1
- package/index.scss +9 -9
- package/package.json +1 -1
|
@@ -1,154 +1,154 @@
|
|
|
1
|
-
import { isElement, normalizeData } from "../../../utils/js/functions";
|
|
2
|
-
import Params from "../../../utils/js/components/params";
|
|
3
|
-
import Selectors from "../../../utils/js/dom/selectors";
|
|
4
|
-
import { Manipulator } from "../../../utils/js/dom/manipulator";
|
|
5
|
-
|
|
6
|
-
class VGFilesTemplateRender {
|
|
7
|
-
constructor(vgFilesInstance, element, params = {}) {
|
|
8
|
-
this.module = vgFilesInstance;
|
|
9
|
-
this.element = isElement(element) ? element : null;
|
|
10
|
-
|
|
11
|
-
if (!this.element) {
|
|
12
|
-
console.error('Invalid element provided:', element);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
this._params = new Params(params, element).get();
|
|
17
|
-
this._nodes = {
|
|
18
|
-
info: this.module._nodes.info,
|
|
19
|
-
drop: this.module._nodes.drop
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
this.bufferTemplate = '';
|
|
23
|
-
this.parsedFiles = [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
init() {
|
|
27
|
-
const $targetNode = this._nodes.info || this._nodes.drop;
|
|
28
|
-
if (!$targetNode) return false;
|
|
29
|
-
|
|
30
|
-
const area = this._nodes.info ? 'info' : 'drop';
|
|
31
|
-
return this._nativeRenderFiles(area, $targetNode);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
_nativeRenderFiles(area, $node) {
|
|
35
|
-
const $list = Selectors.find(`.vg-files-${area}--list`, $node);
|
|
36
|
-
if (!$list) return false;
|
|
37
|
-
|
|
38
|
-
const $items = Array.from($list.children).filter(li => li.tagName === 'LI');
|
|
39
|
-
if ($items.length === 0) return false;
|
|
40
|
-
|
|
41
|
-
this._setTemplateInBuffer($items);
|
|
42
|
-
if (!this.bufferTemplate) return false;
|
|
43
|
-
|
|
44
|
-
this.parsedFiles = $items
|
|
45
|
-
.map(li => {
|
|
46
|
-
const rawData = Manipulator.get(li, 'data-file');
|
|
47
|
-
if (!rawData) return null;
|
|
48
|
-
|
|
49
|
-
return this._parseDataFile(rawData);
|
|
50
|
-
})
|
|
51
|
-
.filter(Boolean);
|
|
52
|
-
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
_parseDataFile(rawData) {
|
|
57
|
-
const dataFile = normalizeData(rawData);
|
|
58
|
-
if (!dataFile || typeof dataFile !== 'object' || Array.isArray(dataFile)) return null;
|
|
59
|
-
|
|
60
|
-
const binaryMeta = this._extractBinaryMeta(dataFile);
|
|
61
|
-
const name = this._toStringOrNull(dataFile.name);
|
|
62
|
-
const id = this._toStringOrNull(dataFile.id);
|
|
63
|
-
const src = this._toStringOrNull(dataFile.src);
|
|
64
|
-
const type = this._toStringOrNull(dataFile.type) || binaryMeta.type || 'application/octet-stream';
|
|
65
|
-
const size = this._toNumberOrNull(dataFile.size) ?? binaryMeta.size ?? 0;
|
|
66
|
-
const lastModified = this._toNumberOrNull(dataFile.lastModified ?? dataFile['last-modified']) ?? binaryMeta.lastModified ?? Date.now();
|
|
67
|
-
|
|
68
|
-
const result = {
|
|
69
|
-
id,
|
|
70
|
-
name,
|
|
71
|
-
size,
|
|
72
|
-
type,
|
|
73
|
-
src,
|
|
74
|
-
lastModified
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
if (dataFile.image !== undefined && dataFile.image !== null && dataFile.image !== '') {
|
|
78
|
-
result.image = dataFile.image;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const requiredKeys = ['id', 'name', 'size', 'type', 'src'];
|
|
82
|
-
const isValid = requiredKeys.every((key) => {
|
|
83
|
-
const value = result[key];
|
|
84
|
-
if (key === 'size') return Number.isFinite(value);
|
|
85
|
-
return value !== undefined && value !== null && value !== '';
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (!isValid) return null;
|
|
89
|
-
|
|
90
|
-
const customData = {};
|
|
91
|
-
const reserved = new Set(['id', 'name', 'size', 'type', 'src', 'image', 'lastModified', 'last-modified']);
|
|
92
|
-
|
|
93
|
-
Object.entries(dataFile).forEach(([key, value]) => {
|
|
94
|
-
if (reserved.has(key)) return;
|
|
95
|
-
if (value === undefined || value === null || value === '') return;
|
|
96
|
-
customData[key] = value;
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (Object.keys(customData).length) {
|
|
100
|
-
result.customData = customData;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
_extractBinaryMeta(dataFile) {
|
|
107
|
-
const binary = dataFile.file || dataFile.blob || dataFile.originFile || null;
|
|
108
|
-
if (!(binary instanceof Blob)) return {};
|
|
109
|
-
|
|
110
|
-
const size = this._toNumberOrNull(binary.size);
|
|
111
|
-
const type = this._toStringOrNull(binary.type);
|
|
112
|
-
const lastModified = (binary instanceof File)
|
|
113
|
-
? this._toNumberOrNull(binary.lastModified)
|
|
114
|
-
: null;
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
size: size ?? null,
|
|
118
|
-
type: type || null,
|
|
119
|
-
lastModified: lastModified ?? null
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
_toStringOrNull(value) {
|
|
124
|
-
if (value === undefined || value === null) return null;
|
|
125
|
-
const normalized = String(value).trim();
|
|
126
|
-
return normalized ? normalized : null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
_toNumberOrNull(value) {
|
|
130
|
-
if (value === undefined || value === null || value === '') return null;
|
|
131
|
-
const normalized = Number(value);
|
|
132
|
-
return Number.isFinite(normalized) ? normalized : null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
_setTemplateInBuffer($items) {
|
|
136
|
-
if (this.bufferTemplate || $items.length === 0) return;
|
|
137
|
-
|
|
138
|
-
const firstItem = $items[0];
|
|
139
|
-
|
|
140
|
-
if (!Manipulator.has(firstItem, 'data-file')) {
|
|
141
|
-
this.bufferTemplate = firstItem.outerHTML;
|
|
142
|
-
firstItem.remove();
|
|
143
|
-
} else {
|
|
144
|
-
this.bufferTemplate = firstItem.outerHTML;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
dispose() {
|
|
149
|
-
this.bufferTemplate = '';
|
|
150
|
-
this.parsedFiles = [];
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
1
|
+
import { isElement, normalizeData } from "../../../utils/js/functions";
|
|
2
|
+
import Params from "../../../utils/js/components/params";
|
|
3
|
+
import Selectors from "../../../utils/js/dom/selectors";
|
|
4
|
+
import { Manipulator } from "../../../utils/js/dom/manipulator";
|
|
5
|
+
|
|
6
|
+
class VGFilesTemplateRender {
|
|
7
|
+
constructor(vgFilesInstance, element, params = {}) {
|
|
8
|
+
this.module = vgFilesInstance;
|
|
9
|
+
this.element = isElement(element) ? element : null;
|
|
10
|
+
|
|
11
|
+
if (!this.element) {
|
|
12
|
+
console.error('Invalid element provided:', element);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this._params = new Params(params, element).get();
|
|
17
|
+
this._nodes = {
|
|
18
|
+
info: this.module._nodes.info,
|
|
19
|
+
drop: this.module._nodes.drop
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
this.bufferTemplate = '';
|
|
23
|
+
this.parsedFiles = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
init() {
|
|
27
|
+
const $targetNode = this._nodes.info || this._nodes.drop;
|
|
28
|
+
if (!$targetNode) return false;
|
|
29
|
+
|
|
30
|
+
const area = this._nodes.info ? 'info' : 'drop';
|
|
31
|
+
return this._nativeRenderFiles(area, $targetNode);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_nativeRenderFiles(area, $node) {
|
|
35
|
+
const $list = Selectors.find(`.vg-files-${area}--list`, $node);
|
|
36
|
+
if (!$list) return false;
|
|
37
|
+
|
|
38
|
+
const $items = Array.from($list.children).filter(li => li.tagName === 'LI');
|
|
39
|
+
if ($items.length === 0) return false;
|
|
40
|
+
|
|
41
|
+
this._setTemplateInBuffer($items);
|
|
42
|
+
if (!this.bufferTemplate) return false;
|
|
43
|
+
|
|
44
|
+
this.parsedFiles = $items
|
|
45
|
+
.map(li => {
|
|
46
|
+
const rawData = Manipulator.get(li, 'data-file');
|
|
47
|
+
if (!rawData) return null;
|
|
48
|
+
|
|
49
|
+
return this._parseDataFile(rawData);
|
|
50
|
+
})
|
|
51
|
+
.filter(Boolean);
|
|
52
|
+
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_parseDataFile(rawData) {
|
|
57
|
+
const dataFile = normalizeData(rawData);
|
|
58
|
+
if (!dataFile || typeof dataFile !== 'object' || Array.isArray(dataFile)) return null;
|
|
59
|
+
|
|
60
|
+
const binaryMeta = this._extractBinaryMeta(dataFile);
|
|
61
|
+
const name = this._toStringOrNull(dataFile.name);
|
|
62
|
+
const id = this._toStringOrNull(dataFile.id);
|
|
63
|
+
const src = this._toStringOrNull(dataFile.src);
|
|
64
|
+
const type = this._toStringOrNull(dataFile.type) || binaryMeta.type || 'application/octet-stream';
|
|
65
|
+
const size = this._toNumberOrNull(dataFile.size) ?? binaryMeta.size ?? 0;
|
|
66
|
+
const lastModified = this._toNumberOrNull(dataFile.lastModified ?? dataFile['last-modified']) ?? binaryMeta.lastModified ?? Date.now();
|
|
67
|
+
|
|
68
|
+
const result = {
|
|
69
|
+
id,
|
|
70
|
+
name,
|
|
71
|
+
size,
|
|
72
|
+
type,
|
|
73
|
+
src,
|
|
74
|
+
lastModified
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (dataFile.image !== undefined && dataFile.image !== null && dataFile.image !== '') {
|
|
78
|
+
result.image = dataFile.image;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const requiredKeys = ['id', 'name', 'size', 'type', 'src'];
|
|
82
|
+
const isValid = requiredKeys.every((key) => {
|
|
83
|
+
const value = result[key];
|
|
84
|
+
if (key === 'size') return Number.isFinite(value);
|
|
85
|
+
return value !== undefined && value !== null && value !== '';
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!isValid) return null;
|
|
89
|
+
|
|
90
|
+
const customData = {};
|
|
91
|
+
const reserved = new Set(['id', 'name', 'size', 'type', 'src', 'image', 'lastModified', 'last-modified']);
|
|
92
|
+
|
|
93
|
+
Object.entries(dataFile).forEach(([key, value]) => {
|
|
94
|
+
if (reserved.has(key)) return;
|
|
95
|
+
if (value === undefined || value === null || value === '') return;
|
|
96
|
+
customData[key] = value;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (Object.keys(customData).length) {
|
|
100
|
+
result.customData = customData;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_extractBinaryMeta(dataFile) {
|
|
107
|
+
const binary = dataFile.file || dataFile.blob || dataFile.originFile || null;
|
|
108
|
+
if (!(binary instanceof Blob)) return {};
|
|
109
|
+
|
|
110
|
+
const size = this._toNumberOrNull(binary.size);
|
|
111
|
+
const type = this._toStringOrNull(binary.type);
|
|
112
|
+
const lastModified = (binary instanceof File)
|
|
113
|
+
? this._toNumberOrNull(binary.lastModified)
|
|
114
|
+
: null;
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
size: size ?? null,
|
|
118
|
+
type: type || null,
|
|
119
|
+
lastModified: lastModified ?? null
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_toStringOrNull(value) {
|
|
124
|
+
if (value === undefined || value === null) return null;
|
|
125
|
+
const normalized = String(value).trim();
|
|
126
|
+
return normalized ? normalized : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
_toNumberOrNull(value) {
|
|
130
|
+
if (value === undefined || value === null || value === '') return null;
|
|
131
|
+
const normalized = Number(value);
|
|
132
|
+
return Number.isFinite(normalized) ? normalized : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_setTemplateInBuffer($items) {
|
|
136
|
+
if (this.bufferTemplate || $items.length === 0) return;
|
|
137
|
+
|
|
138
|
+
const firstItem = $items[0];
|
|
139
|
+
|
|
140
|
+
if (!Manipulator.has(firstItem, 'data-file')) {
|
|
141
|
+
this.bufferTemplate = firstItem.outerHTML;
|
|
142
|
+
firstItem.remove();
|
|
143
|
+
} else {
|
|
144
|
+
this.bufferTemplate = firstItem.outerHTML;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
dispose() {
|
|
149
|
+
this.bufferTemplate = '';
|
|
150
|
+
this.parsedFiles = [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
154
|
export default VGFilesTemplateRender;
|
|
@@ -482,14 +482,14 @@ class VGFiles extends VGFilesBase {
|
|
|
482
482
|
if (li) {
|
|
483
483
|
this._setButtonElement(file);
|
|
484
484
|
|
|
485
|
-
const fileRemove = Selectors.find('.file-remove', li);
|
|
486
|
-
if (fileRemove) {
|
|
487
|
-
fileRemove.innerHTML = '';
|
|
488
|
-
fileRemove.appendChild(this._setFailingButtons(file));
|
|
489
|
-
Classes.add(li, CLASS_NAME_FAILING);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
485
|
+
const fileRemove = Selectors.find('.file-remove', li);
|
|
486
|
+
if (fileRemove) {
|
|
487
|
+
fileRemove.innerHTML = '';
|
|
488
|
+
fileRemove.appendChild(this._setFailingButtons(file));
|
|
489
|
+
Classes.add(li, CLASS_NAME_FAILING);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
493
|
|
|
494
494
|
const payload = { file: uploadData.file };
|
|
495
495
|
|
|
@@ -677,15 +677,15 @@ class VGFiles extends VGFilesBase {
|
|
|
677
677
|
}
|
|
678
678
|
});
|
|
679
679
|
|
|
680
|
-
if (this._params.ajax && this._params.removes.single.route) {
|
|
681
|
-
if (!id) {
|
|
682
|
-
this._files = this._files.filter(f => !(f.name === name && f.size === size));
|
|
683
|
-
this._updateStatsAfterRemove();
|
|
684
|
-
this._files.length ? this.build() : this.clear(true);
|
|
685
|
-
emitRemove();
|
|
686
|
-
this._resetFileInput();
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
680
|
+
if (this._params.ajax && this._params.removes.single.route) {
|
|
681
|
+
if (!id) {
|
|
682
|
+
this._files = this._files.filter(f => !(f.name === name && f.size === size));
|
|
683
|
+
this._updateStatsAfterRemove();
|
|
684
|
+
this._files.length ? this.build() : this.clear(true);
|
|
685
|
+
emitRemove();
|
|
686
|
+
this._resetFileInput();
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
689
|
|
|
690
690
|
const routeBase = this._params.removes.single.route;
|
|
691
691
|
const routeSeparator = routeBase.includes('?') ? '&' : '?';
|
|
@@ -775,36 +775,36 @@ class VGFiles extends VGFilesBase {
|
|
|
775
775
|
return Selectors.find(`button[data-name="${file.name}"][data-size="${file.size}"]`, this._element);
|
|
776
776
|
}
|
|
777
777
|
|
|
778
|
-
_setButtonElement(file, isAjax = false, status = '') {
|
|
779
|
-
let icon = getSVG('trash'), action = 'data-vg-dismiss';
|
|
780
|
-
if (!this._params.info) icon = getSVG('cross');
|
|
781
|
-
if (isAjax) {
|
|
782
|
-
icon = getSVG('spinner');
|
|
783
|
-
if (status === 'completed') icon = getSVG('check');
|
|
784
|
-
else action = 'data-vg-reload';
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
return this._tpl.button([
|
|
788
|
-
this._tpl.i({}, icon, { isHTML: true })
|
|
789
|
-
], 'button', this._buildFileDataAttributes(file, {
|
|
778
|
+
_setButtonElement(file, isAjax = false, status = '') {
|
|
779
|
+
let icon = getSVG('trash'), action = 'data-vg-dismiss';
|
|
780
|
+
if (!this._params.info) icon = getSVG('cross');
|
|
781
|
+
if (isAjax) {
|
|
782
|
+
icon = getSVG('spinner');
|
|
783
|
+
if (status === 'completed') icon = getSVG('check');
|
|
784
|
+
else action = 'data-vg-reload';
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return this._tpl.button([
|
|
788
|
+
this._tpl.i({}, icon, { isHTML: true })
|
|
789
|
+
], 'button', this._buildFileDataAttributes(file, {
|
|
790
790
|
type: 'button',
|
|
791
791
|
[action]: 'file',
|
|
792
792
|
'data-name': file.name,
|
|
793
793
|
'data-size': file.size ?? 0,
|
|
794
794
|
'data-type': file.type || '',
|
|
795
795
|
'data-last-modified': file.lastModified || '',
|
|
796
|
-
'data-id': file.id || ''
|
|
797
|
-
}));
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
_setFailingButtons(file) {
|
|
801
|
-
const wrapper = this._tpl.div({ class: 'file-failing-actions' }, [
|
|
802
|
-
this._setButtonElement(file, true, 'failing'),
|
|
803
|
-
this._setButtonElement(file)
|
|
804
|
-
]);
|
|
805
|
-
|
|
806
|
-
return wrapper;
|
|
807
|
-
}
|
|
796
|
+
'data-id': file.id || ''
|
|
797
|
+
}));
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
_setFailingButtons(file) {
|
|
801
|
+
const wrapper = this._tpl.div({ class: 'file-failing-actions' }, [
|
|
802
|
+
this._setButtonElement(file, true, 'failing'),
|
|
803
|
+
this._setButtonElement(file)
|
|
804
|
+
]);
|
|
805
|
+
|
|
806
|
+
return wrapper;
|
|
807
|
+
}
|
|
808
808
|
|
|
809
809
|
_renderStat() {
|
|
810
810
|
if (!this._nodes.stat) return;
|
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
# VGFiles
|
|
2
|
-
|
|
3
|
-
`VGFiles` — модуль загрузки и управления файлами с поддержкой локального режима и AJAX.
|
|
4
|
-
|
|
5
|
-
## Новые фичи
|
|
6
|
-
|
|
7
|
-
- Поддержка `replace` для single-mode (`limits.count = 1`): новый файл заменяет предыдущий без накопления.
|
|
8
|
-
- Переименование входящих файлов через `rename: true | (file, index) => string`.
|
|
9
|
-
- `smartdrop`: глобальная подсветка dropzone, если на экране только одна видимая зона дропа.
|
|
10
|
-
- Расширенный lifecycle для внешних файлов из `data-file`: парсинг `customData`, `src/image`, бинарных метаданных.
|
|
11
|
-
- Интеграция с `VGFilePreview` в info-списке (`ui.nameOnly: true`) для inline-аудио, скачивания и предпросмотра по клику.
|
|
12
|
-
- Авто-обогащение аудиофайлов метаданными (title/cover) через `extractAudioMetadata`.
|
|
13
|
-
- Гибкие confirm-хуки для удаления (`removes.single.confirm`, `removes.all.confirm`) с контрактом `accepted/data`.
|
|
14
|
-
- Повторная загрузка упавших файлов через `data-vg-reload="file"` и кнопки retry.
|
|
15
|
-
- Проброс `customData` файла в `data-*` атрибуты элементов списка.
|
|
16
|
-
|
|
17
|
-
## Базовые возможности
|
|
18
|
-
|
|
19
|
-
- Выбор файлов через `<input type="file">` и drag&drop.
|
|
20
|
-
- Лимиты: количество, размер файла, общий размер, MIME-типы.
|
|
21
|
-
- Режимы загрузки: `sequential` и `parallel`.
|
|
22
|
-
- Автоматическая/ручная инициализация.
|
|
23
|
-
- Сортировка загруженных файлов с сохранением порядка на сервере.
|
|
24
|
-
- Удаление одного файла и очистка всего списка.
|
|
25
|
-
|
|
26
|
-
## Инициализация
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
const files = new VGFiles(document.querySelector('.vg-files'), {
|
|
30
|
-
ajax: true,
|
|
31
|
-
smartdrop: true,
|
|
32
|
-
rename: true,
|
|
33
|
-
replace: true,
|
|
34
|
-
uploads: {
|
|
35
|
-
route: '/api/upload',
|
|
36
|
-
mode: 'sequential'
|
|
37
|
-
},
|
|
38
|
-
limits: {
|
|
39
|
-
count: 10,
|
|
40
|
-
sizes: 5,
|
|
41
|
-
total: 50
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Ключевые параметры
|
|
47
|
-
|
|
48
|
-
- `allowed` — разрешить drag-drop режим и особенности drop-зоны.
|
|
49
|
-
- `ajax` — включить серверную загрузку.
|
|
50
|
-
- `replace` (`default: true`) — замена файла в single-mode.
|
|
51
|
-
- `rename` (`default: false`) — переименование файлов до добавления в список.
|
|
52
|
-
- `smartdrop` (`default: false`) — глобальная логика подсказки drop-зоны.
|
|
53
|
-
- `prepend` (`default: true`) — добавление новых файлов в начало.
|
|
54
|
-
- `uploads.mode` — `'sequential' | 'parallel'`.
|
|
55
|
-
- `uploads.maxParallel`, `uploads.maxConcurrent`, `uploads.retryAttempts`, `uploads.retryDelay`.
|
|
56
|
-
- `removes.single.confirm`, `removes.all.confirm` — кастомные confirm-обработчики.
|
|
57
|
-
- `sortable.enabled`, `sortable.route`, `sortable.handle`, `sortable.lists`.
|
|
58
|
-
|
|
59
|
-
## Внешние (уже загруженные) файлы
|
|
60
|
-
|
|
61
|
-
Модуль может стартовать с предзаполненным списком через `data-file` в `<li>`.
|
|
62
|
-
|
|
63
|
-
Поддерживаемые поля объекта:
|
|
64
|
-
|
|
65
|
-
- `id`, `name`, `size`, `type`, `src` (обязательный набор для валидного внешнего файла)
|
|
66
|
-
- `image` (опционально)
|
|
67
|
-
- `lastModified` / `last-modified` (опционально)
|
|
68
|
-
- любые дополнительные поля -> попадают в `customData`
|
|
69
|
-
|
|
70
|
-
`customData` дальше пробрасывается в `data-*` атрибуты элементов файла и может использоваться в шаблонах/интеграциях.
|
|
71
|
-
|
|
72
|
-
## AJAX-события и callbacks
|
|
73
|
-
|
|
74
|
-
DOM-события:
|
|
75
|
-
|
|
76
|
-
- `vg.files.change`
|
|
77
|
-
- `vg.files.upload.start`
|
|
78
|
-
- `vg.files.upload.progress`
|
|
79
|
-
- `vg.files.upload.complete`
|
|
80
|
-
- `vg.files.upload.error`
|
|
81
|
-
- `vg.files.upload.allComplete`
|
|
82
|
-
- `vg.files.remove`
|
|
83
|
-
- `vg.files.reload`
|
|
84
|
-
|
|
85
|
-
Callbacks (`params.callbacks`):
|
|
86
|
-
|
|
87
|
-
- `onInit`
|
|
88
|
-
- `onChange` (`{ files, input, inputFiles }`)
|
|
89
|
-
- `onUploadStart`
|
|
90
|
-
- `onUploadProgress`
|
|
91
|
-
- `onUploadComplete`
|
|
92
|
-
- `onUploadError`
|
|
93
|
-
- `onUploadAllComplete`
|
|
94
|
-
- `onRemoveFile`
|
|
95
|
-
- `onClear`
|
|
96
|
-
- `onReload`
|
|
97
|
-
|
|
98
|
-
## Удаление с кастомным confirm
|
|
99
|
-
|
|
100
|
-
Если передан `removes.single.confirm` или `removes.all.confirm`, модуль вызывает вашу функцию вместо стандартного `VGAlert`.
|
|
101
|
-
|
|
102
|
-
Контракт результата:
|
|
103
|
-
|
|
104
|
-
- `true` или `{ accepted: true }` — подтвердить.
|
|
105
|
-
- `false` или `{ accepted: false }` — отменить.
|
|
106
|
-
- `{ accepted: true, data }` — подтвердить и использовать готовый ответ удаления (без дополнительного AJAX-запроса).
|
|
107
|
-
|
|
108
|
-
## Статусы файлов
|
|
109
|
-
|
|
110
|
-
- `pending` — ожидает загрузки.
|
|
111
|
-
- `loading` — идет загрузка.
|
|
112
|
-
- `completed` — успешно загружен.
|
|
113
|
-
- `failing` — ошибка загрузки (доступен reload).
|
|
114
|
-
|
|
115
|
-
## Методы API
|
|
116
|
-
|
|
117
|
-
- `upload(file)`
|
|
118
|
-
- `uploadAll(files)`
|
|
119
|
-
- `reload(button)`
|
|
120
|
-
- `removeFile(button)`
|
|
121
|
-
- `clear(resetInput, uiOnly)`
|
|
122
|
-
- `dispose()`
|
|
123
|
-
|
|
1
|
+
# VGFiles
|
|
2
|
+
|
|
3
|
+
`VGFiles` — модуль загрузки и управления файлами с поддержкой локального режима и AJAX.
|
|
4
|
+
|
|
5
|
+
## Новые фичи
|
|
6
|
+
|
|
7
|
+
- Поддержка `replace` для single-mode (`limits.count = 1`): новый файл заменяет предыдущий без накопления.
|
|
8
|
+
- Переименование входящих файлов через `rename: true | (file, index) => string`.
|
|
9
|
+
- `smartdrop`: глобальная подсветка dropzone, если на экране только одна видимая зона дропа.
|
|
10
|
+
- Расширенный lifecycle для внешних файлов из `data-file`: парсинг `customData`, `src/image`, бинарных метаданных.
|
|
11
|
+
- Интеграция с `VGFilePreview` в info-списке (`ui.nameOnly: true`) для inline-аудио, скачивания и предпросмотра по клику.
|
|
12
|
+
- Авто-обогащение аудиофайлов метаданными (title/cover) через `extractAudioMetadata`.
|
|
13
|
+
- Гибкие confirm-хуки для удаления (`removes.single.confirm`, `removes.all.confirm`) с контрактом `accepted/data`.
|
|
14
|
+
- Повторная загрузка упавших файлов через `data-vg-reload="file"` и кнопки retry.
|
|
15
|
+
- Проброс `customData` файла в `data-*` атрибуты элементов списка.
|
|
16
|
+
|
|
17
|
+
## Базовые возможности
|
|
18
|
+
|
|
19
|
+
- Выбор файлов через `<input type="file">` и drag&drop.
|
|
20
|
+
- Лимиты: количество, размер файла, общий размер, MIME-типы.
|
|
21
|
+
- Режимы загрузки: `sequential` и `parallel`.
|
|
22
|
+
- Автоматическая/ручная инициализация.
|
|
23
|
+
- Сортировка загруженных файлов с сохранением порядка на сервере.
|
|
24
|
+
- Удаление одного файла и очистка всего списка.
|
|
25
|
+
|
|
26
|
+
## Инициализация
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const files = new VGFiles(document.querySelector('.vg-files'), {
|
|
30
|
+
ajax: true,
|
|
31
|
+
smartdrop: true,
|
|
32
|
+
rename: true,
|
|
33
|
+
replace: true,
|
|
34
|
+
uploads: {
|
|
35
|
+
route: '/api/upload',
|
|
36
|
+
mode: 'sequential'
|
|
37
|
+
},
|
|
38
|
+
limits: {
|
|
39
|
+
count: 10,
|
|
40
|
+
sizes: 5,
|
|
41
|
+
total: 50
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Ключевые параметры
|
|
47
|
+
|
|
48
|
+
- `allowed` — разрешить drag-drop режим и особенности drop-зоны.
|
|
49
|
+
- `ajax` — включить серверную загрузку.
|
|
50
|
+
- `replace` (`default: true`) — замена файла в single-mode.
|
|
51
|
+
- `rename` (`default: false`) — переименование файлов до добавления в список.
|
|
52
|
+
- `smartdrop` (`default: false`) — глобальная логика подсказки drop-зоны.
|
|
53
|
+
- `prepend` (`default: true`) — добавление новых файлов в начало.
|
|
54
|
+
- `uploads.mode` — `'sequential' | 'parallel'`.
|
|
55
|
+
- `uploads.maxParallel`, `uploads.maxConcurrent`, `uploads.retryAttempts`, `uploads.retryDelay`.
|
|
56
|
+
- `removes.single.confirm`, `removes.all.confirm` — кастомные confirm-обработчики.
|
|
57
|
+
- `sortable.enabled`, `sortable.route`, `sortable.handle`, `sortable.lists`.
|
|
58
|
+
|
|
59
|
+
## Внешние (уже загруженные) файлы
|
|
60
|
+
|
|
61
|
+
Модуль может стартовать с предзаполненным списком через `data-file` в `<li>`.
|
|
62
|
+
|
|
63
|
+
Поддерживаемые поля объекта:
|
|
64
|
+
|
|
65
|
+
- `id`, `name`, `size`, `type`, `src` (обязательный набор для валидного внешнего файла)
|
|
66
|
+
- `image` (опционально)
|
|
67
|
+
- `lastModified` / `last-modified` (опционально)
|
|
68
|
+
- любые дополнительные поля -> попадают в `customData`
|
|
69
|
+
|
|
70
|
+
`customData` дальше пробрасывается в `data-*` атрибуты элементов файла и может использоваться в шаблонах/интеграциях.
|
|
71
|
+
|
|
72
|
+
## AJAX-события и callbacks
|
|
73
|
+
|
|
74
|
+
DOM-события:
|
|
75
|
+
|
|
76
|
+
- `vg.files.change`
|
|
77
|
+
- `vg.files.upload.start`
|
|
78
|
+
- `vg.files.upload.progress`
|
|
79
|
+
- `vg.files.upload.complete`
|
|
80
|
+
- `vg.files.upload.error`
|
|
81
|
+
- `vg.files.upload.allComplete`
|
|
82
|
+
- `vg.files.remove`
|
|
83
|
+
- `vg.files.reload`
|
|
84
|
+
|
|
85
|
+
Callbacks (`params.callbacks`):
|
|
86
|
+
|
|
87
|
+
- `onInit`
|
|
88
|
+
- `onChange` (`{ files, input, inputFiles }`)
|
|
89
|
+
- `onUploadStart`
|
|
90
|
+
- `onUploadProgress`
|
|
91
|
+
- `onUploadComplete`
|
|
92
|
+
- `onUploadError`
|
|
93
|
+
- `onUploadAllComplete`
|
|
94
|
+
- `onRemoveFile`
|
|
95
|
+
- `onClear`
|
|
96
|
+
- `onReload`
|
|
97
|
+
|
|
98
|
+
## Удаление с кастомным confirm
|
|
99
|
+
|
|
100
|
+
Если передан `removes.single.confirm` или `removes.all.confirm`, модуль вызывает вашу функцию вместо стандартного `VGAlert`.
|
|
101
|
+
|
|
102
|
+
Контракт результата:
|
|
103
|
+
|
|
104
|
+
- `true` или `{ accepted: true }` — подтвердить.
|
|
105
|
+
- `false` или `{ accepted: false }` — отменить.
|
|
106
|
+
- `{ accepted: true, data }` — подтвердить и использовать готовый ответ удаления (без дополнительного AJAX-запроса).
|
|
107
|
+
|
|
108
|
+
## Статусы файлов
|
|
109
|
+
|
|
110
|
+
- `pending` — ожидает загрузки.
|
|
111
|
+
- `loading` — идет загрузка.
|
|
112
|
+
- `completed` — успешно загружен.
|
|
113
|
+
- `failing` — ошибка загрузки (доступен reload).
|
|
114
|
+
|
|
115
|
+
## Методы API
|
|
116
|
+
|
|
117
|
+
- `upload(file)`
|
|
118
|
+
- `uploadAll(files)`
|
|
119
|
+
- `reload(button)`
|
|
120
|
+
- `removeFile(button)`
|
|
121
|
+
- `clear(resetInput, uiOnly)`
|
|
122
|
+
- `dispose()`
|
|
123
|
+
|