@cocreate/file 1.19.0 → 1.19.2
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 +16 -0
- package/package.json +1 -1
- package/src/client.js +933 -902
- package/src/server.js +526 -495
package/src/client.js
CHANGED
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
* For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import Observer from
|
|
26
|
-
import Crud from
|
|
27
|
-
import Elements from
|
|
28
|
-
import Actions from
|
|
29
|
-
import { render } from
|
|
30
|
-
import { queryElements } from
|
|
31
|
-
import
|
|
25
|
+
import Observer from "@cocreate/observer";
|
|
26
|
+
import Crud from "@cocreate/crud-client";
|
|
27
|
+
import Elements from "@cocreate/elements";
|
|
28
|
+
import Actions from "@cocreate/actions";
|
|
29
|
+
import { render } from "@cocreate/render";
|
|
30
|
+
import { queryElements } from "@cocreate/utils";
|
|
31
|
+
import "@cocreate/element-prototype";
|
|
32
32
|
|
|
33
33
|
const inputs = new Map();
|
|
34
34
|
const Files = new Map();
|
|
@@ -36,968 +36,999 @@ const Files = new Map();
|
|
|
36
36
|
/**
|
|
37
37
|
* Initializes file elements. If no parameter is provided, it queries and initializes all elements with type="file".
|
|
38
38
|
* It can also initialize a single element or an array of elements. Specifically focuses on elements of type 'file'.
|
|
39
|
-
*
|
|
40
|
-
* @param {(Element|Element[]|null)} [elements] - Optional. An element, an array of elements, or null.
|
|
39
|
+
*
|
|
40
|
+
* @param {(Element|Element[]|null)} [elements] - Optional. An element, an array of elements, or null.
|
|
41
41
|
* - If null or omitted, the function queries and initializes all elements in the document with type="file".
|
|
42
42
|
* - If a single element is provided, it initializes that element (assuming it is of type "file").
|
|
43
43
|
* - If an array of elements is provided, each element in the array is initialized.
|
|
44
44
|
*/
|
|
45
45
|
async function init(elements) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
46
|
+
if (!elements) elements = document.querySelectorAll('[type="file"]');
|
|
47
|
+
else if (!Array.isArray(elements)) elements = [elements];
|
|
48
|
+
for (let i = 0; i < elements.length; i++) {
|
|
49
|
+
let nestedInput,
|
|
50
|
+
isInput = elements[i].tagName === "INPUT";
|
|
51
|
+
if (!isInput) {
|
|
52
|
+
nestedInput = elements[i].querySelector('input[type="file"]');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
elements[i].getValue = async () => await getFiles([elements[i]]);
|
|
56
|
+
elements[i].getFiles = async () => await getFiles([elements[i]]);
|
|
57
|
+
elements[i].setValue = (files) => setFiles(elements[i], files);
|
|
58
|
+
elements[i].renderValue = (files) => setFiles(elements[i], files);
|
|
59
|
+
|
|
60
|
+
// if (elements[i].renderValue) {
|
|
61
|
+
// let data = await elements[i].getValue()
|
|
62
|
+
// if (data)
|
|
63
|
+
// elements[i].setValue(data)
|
|
64
|
+
// }
|
|
65
|
+
|
|
66
|
+
if (elements[i].hasAttribute("directory")) {
|
|
67
|
+
if (!isInput && window.showDirectoryPicker)
|
|
68
|
+
elements[i].addEventListener("click", fileEvent);
|
|
69
|
+
else if ("webkitdirectory" in elements[i]) {
|
|
70
|
+
elements[i].webkitdirectory = true;
|
|
71
|
+
if (!isInput && !nestedInput) {
|
|
72
|
+
nestedInput = document.createElement("input");
|
|
73
|
+
nestedInput.type = "file";
|
|
74
|
+
nestedInput.setAttribute("hidden", "");
|
|
75
|
+
elements[i].appendChild(nestedInput);
|
|
76
|
+
nestedInput.fileElement = elements[i];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (nestedInput) {
|
|
80
|
+
elements[i].addEventListener("click", function () {
|
|
81
|
+
nestedInput.click();
|
|
82
|
+
});
|
|
83
|
+
nestedInput.addEventListener("change", fileEvent);
|
|
84
|
+
} else elements[i].addEventListener("change", fileEvent);
|
|
85
|
+
} else
|
|
86
|
+
console.error(
|
|
87
|
+
"Directory selection not supported in this browser."
|
|
88
|
+
);
|
|
89
|
+
} else if (!isInput && window.showOpenFilePicker)
|
|
90
|
+
elements[i].addEventListener("click", fileEvent);
|
|
91
|
+
else {
|
|
92
|
+
if (!isInput && !nestedInput) {
|
|
93
|
+
nestedInput = document.createElement("input");
|
|
94
|
+
nestedInput.type = "file";
|
|
95
|
+
nestedInput.setAttribute("hidden", "");
|
|
96
|
+
elements[i].appendChild(nestedInput);
|
|
97
|
+
nestedInput.fileElement = elements[i];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (nestedInput) {
|
|
101
|
+
elements[i].addEventListener("click", function () {
|
|
102
|
+
nestedInput.click();
|
|
103
|
+
});
|
|
104
|
+
nestedInput.addEventListener("change", fileEvent);
|
|
105
|
+
} else elements[i].addEventListener("change", fileEvent);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
async function fileEvent(event) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
111
|
+
try {
|
|
112
|
+
let input = event.currentTarget;
|
|
113
|
+
let multiple = input.multiple;
|
|
114
|
+
|
|
115
|
+
// If 'multiple' is not explicitly set, check the attribute.
|
|
116
|
+
if (multiple !== true && multiple !== false) {
|
|
117
|
+
multiple = input.getAttribute("multiple");
|
|
118
|
+
multiple = multiple !== null && multiple !== "false";
|
|
119
|
+
input.multiple = multiple;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let selected = inputs.get(input) || new Map();
|
|
123
|
+
let files = input.files;
|
|
124
|
+
input = input.fileElement || input;
|
|
125
|
+
if (!files || !files.length) {
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
if (input.hasAttribute("directory")) {
|
|
128
|
+
let handle = await window.showDirectoryPicker();
|
|
129
|
+
let file = {
|
|
130
|
+
name: handle.name,
|
|
131
|
+
directory: "/",
|
|
132
|
+
path: "/" + handle.name,
|
|
133
|
+
type: "text/directory",
|
|
134
|
+
"content-type": "text/directory"
|
|
135
|
+
};
|
|
136
|
+
file.input = input;
|
|
137
|
+
file.id = await getFileId(file);
|
|
138
|
+
if (selected.has(file.id)) {
|
|
139
|
+
console.log(
|
|
140
|
+
"Duplicate file has been selected. This could be in error as the browser does not provide a clear way of checking duplictaes"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
file.handle = handle;
|
|
145
|
+
|
|
146
|
+
if (!multiple) {
|
|
147
|
+
for (let [id] of selected) {
|
|
148
|
+
Files.delete(id);
|
|
149
|
+
}
|
|
150
|
+
selected.clear();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
selected.set(file.id, file);
|
|
154
|
+
Files.set(file.id, file);
|
|
155
|
+
|
|
156
|
+
files = await getDirectoryHandles(handle, handle.name);
|
|
157
|
+
} else {
|
|
158
|
+
files = await window.showOpenFilePicker({ multiple });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (let i = 0; i < files.length; i++) {
|
|
163
|
+
const handle = files[i];
|
|
164
|
+
if (files[i].kind === "file") {
|
|
165
|
+
files[i] = await files[i].getFile();
|
|
166
|
+
files[i].handle = handle;
|
|
167
|
+
} else if (files[i].kind === "directory") {
|
|
168
|
+
files[i].handle = handle;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!files[i].src) await readFile(files[i]);
|
|
172
|
+
// if (!files[i].src)
|
|
173
|
+
// files[i].src = files[i]
|
|
174
|
+
|
|
175
|
+
// if (!files[i].src.name)
|
|
176
|
+
// files[i].src = files[i]
|
|
177
|
+
|
|
178
|
+
if (!files[i].size) files[i].size = handle.size;
|
|
179
|
+
|
|
180
|
+
files[i].directory = handle.directory || "/";
|
|
181
|
+
files[i].path = handle.path || "/";
|
|
182
|
+
files[i].pathname = handle.pathname || "/" + handle.name;
|
|
183
|
+
files[i]["content-type"] = files[i].type;
|
|
184
|
+
files[i].input = input;
|
|
185
|
+
files[i].id = await getFileId(files[i]);
|
|
186
|
+
if (selected.has(files[i].id)) {
|
|
187
|
+
console.log(
|
|
188
|
+
"Duplicate file has been selected. This could be in error as the browser does not provide a clear way of checking duplictaes"
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!multiple) {
|
|
193
|
+
for (let [id] of selected) {
|
|
194
|
+
Files.delete(id);
|
|
195
|
+
}
|
|
196
|
+
selected.clear();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
selected.set(files[i].id, files[i]);
|
|
200
|
+
Files.set(files[i].id, files[i]);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (selected.size) {
|
|
204
|
+
inputs.set(input, selected);
|
|
205
|
+
// console.log("Files selected:", selected);
|
|
206
|
+
|
|
207
|
+
if (input.renderValue)
|
|
208
|
+
input.renderValue(Array.from(selected.values()));
|
|
209
|
+
|
|
210
|
+
const isImport = input.getAttribute("import");
|
|
211
|
+
const isRealtime = input.getAttribute("realtime");
|
|
212
|
+
if (isRealtime && isRealtime !== "false") {
|
|
213
|
+
if (isImport || isImport == "") {
|
|
214
|
+
Import(input);
|
|
215
|
+
} else if (input.save) input.save();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (error.name !== "AbortError") {
|
|
220
|
+
console.error("Error selecting directory:", error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
async function getDirectoryHandles(handle, name) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
226
|
+
let handles = [];
|
|
227
|
+
for await (const entry of handle.values()) {
|
|
228
|
+
entry.directory = name;
|
|
229
|
+
entry.path = "/" + name + "/";
|
|
230
|
+
entry.pathname = "/" + name + "/" + entry.name;
|
|
231
|
+
if (!entry.webkitRelativePath) entry.webkitRelativePath = name;
|
|
232
|
+
|
|
233
|
+
if (entry.kind === "file") {
|
|
234
|
+
handles.push(entry);
|
|
235
|
+
} else if (entry.kind === "directory") {
|
|
236
|
+
entry.type = "text/directory";
|
|
237
|
+
handles.push(entry);
|
|
238
|
+
const entries = await getDirectoryHandles(
|
|
239
|
+
entry,
|
|
240
|
+
name + "/" + entry.name
|
|
241
|
+
);
|
|
242
|
+
handles = handles.concat(entries);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return handles;
|
|
243
246
|
}
|
|
244
247
|
|
|
245
248
|
async function getFileId(file) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
249
|
+
if ((file.id = file.pathname)) {
|
|
250
|
+
return file.id;
|
|
251
|
+
} else {
|
|
252
|
+
file.id = `${file.name}${file.size}${file.type}${file.lastModified}`;
|
|
253
|
+
return file.id;
|
|
254
|
+
}
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
async function getFiles(fileInputs, readAs) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return files
|
|
258
|
+
const files = [];
|
|
259
|
+
|
|
260
|
+
if (!Array.isArray(fileInputs)) fileInputs = [fileInputs];
|
|
261
|
+
|
|
262
|
+
for (let input of fileInputs) {
|
|
263
|
+
const selected = inputs.get(input);
|
|
264
|
+
if (selected) {
|
|
265
|
+
for (let file of Array.from(selected.values())) {
|
|
266
|
+
if (!file.src) {
|
|
267
|
+
// if (readAs === 'blob')
|
|
268
|
+
file.src = file;
|
|
269
|
+
// else
|
|
270
|
+
// await readFile(file, readAs)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
let fileObject = { ...file };
|
|
274
|
+
fileObject.size = file.size;
|
|
275
|
+
await getCustomData(fileObject);
|
|
276
|
+
|
|
277
|
+
files.push(fileObject);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return files;
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
async function getCustomData(file) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
286
|
+
if (!file.id) file.id = file.pathname;
|
|
287
|
+
// TODO: Consider potential replacment of file_id, perhaps supporting selector
|
|
288
|
+
let form = document.querySelector(`[file_id="${file.id}"]`);
|
|
289
|
+
if (form) {
|
|
290
|
+
let elements = form.querySelectorAll("[file]");
|
|
291
|
+
for (let i = 0; i < elements.length; i++) {
|
|
292
|
+
let name = elements[i].getAttribute("file");
|
|
293
|
+
if (name) {
|
|
294
|
+
file[name] = await elements[i].getValue();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
delete file.input;
|
|
300
|
+
|
|
301
|
+
return file;
|
|
300
302
|
}
|
|
301
303
|
|
|
302
304
|
// This function reads the file and returns its src
|
|
303
305
|
function readFile(file, readAs) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
306
|
+
return new Promise((resolve) => {
|
|
307
|
+
const fileType = file.type.split("/");
|
|
308
|
+
|
|
309
|
+
if (fileType[1] === "directory") {
|
|
310
|
+
return resolve(file);
|
|
311
|
+
} else if (readAs) {
|
|
312
|
+
if (readAs === "blob") return resolve(file);
|
|
313
|
+
} else if (fileType[0] === "image") {
|
|
314
|
+
readAs = "readAsDataURL";
|
|
315
|
+
} else if (fileType[0] === "video") {
|
|
316
|
+
readAs = "readAsDataURL";
|
|
317
|
+
} else if (fileType[0] === "audio") {
|
|
318
|
+
readAs = "readAsDataURL";
|
|
319
|
+
} else if (fileType[1] === "pdf") {
|
|
320
|
+
readAs = "readAsDataURL";
|
|
321
|
+
} else if (
|
|
322
|
+
["doc", "msword", "docx", "xlsx", "pptx"].includes(fileType[1])
|
|
323
|
+
) {
|
|
324
|
+
readAs = "readAsBinaryString";
|
|
325
|
+
} else {
|
|
326
|
+
readAs = "readAsText";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const reader = new FileReader();
|
|
330
|
+
reader[readAs](file);
|
|
331
|
+
|
|
332
|
+
reader.onload = () => {
|
|
333
|
+
file.src = reader.result;
|
|
334
|
+
if (["doc", "msword", "docx", "xlsx", "pptx"].includes(fileType)) {
|
|
335
|
+
file.src = btoa(file.src);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
resolve(file);
|
|
339
|
+
};
|
|
340
|
+
});
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
function setFiles(element, files) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
344
|
+
if (!files || typeof files !== "object") return;
|
|
345
|
+
if (!Array.isArray(files)) files = [files];
|
|
346
|
+
else if (!files.length) return;
|
|
347
|
+
|
|
348
|
+
let selected = inputs.get(element) || new Map();
|
|
349
|
+
|
|
350
|
+
if (!element.multiple) {
|
|
351
|
+
for (let key of selected.keys()) {
|
|
352
|
+
selected.delete(key); // Remove the entry from the selected map
|
|
353
|
+
Files.delete(key); // Remove the corresponding entry from the Files map
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
for (let i = 0; i < files.length; i++) {
|
|
357
|
+
if (!files[i].id) files[i].id = files[i].pathname;
|
|
358
|
+
files[i].input = element;
|
|
359
|
+
selected.set(files[i].id, files[i]);
|
|
360
|
+
Files.set(files[i].id, files[i]);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
inputs.set(element, selected);
|
|
364
|
+
if (element.renderValue)
|
|
365
|
+
render({
|
|
366
|
+
source: element,
|
|
367
|
+
data: Array.from(selected.values())
|
|
368
|
+
});
|
|
359
369
|
}
|
|
360
370
|
|
|
361
371
|
// TODO: Could this benifit from media processing to save results locally
|
|
362
372
|
async function save(element, action, data) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
373
|
+
try {
|
|
374
|
+
if (!data) data = [];
|
|
375
|
+
|
|
376
|
+
if (!Array.isArray(element)) element = [element];
|
|
377
|
+
|
|
378
|
+
for (let i = 0; i < element.length; i++) {
|
|
379
|
+
const inputs = [];
|
|
380
|
+
if (element[i].type === "file") inputs.push(element[i]);
|
|
381
|
+
else if (element[i].tagName === "form") {
|
|
382
|
+
let fileInputs =
|
|
383
|
+
element[i].querySelectorAll('input[type="file"]');
|
|
384
|
+
inputs.push(...fileInputs);
|
|
385
|
+
} else {
|
|
386
|
+
const form = element[i].closest("form");
|
|
387
|
+
if (form)
|
|
388
|
+
inputs.push(...form.querySelectorAll('input[type="file"]'));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
for (let input of inputs) {
|
|
392
|
+
let files = await getFiles(input);
|
|
393
|
+
|
|
394
|
+
for (let i = 0; i < files.length; i++) {
|
|
395
|
+
if (!files[i].src) continue;
|
|
396
|
+
|
|
397
|
+
if (files[i].handle && action !== "download") {
|
|
398
|
+
if (action === "saveAs") {
|
|
399
|
+
if (files[i].kind === "file") {
|
|
400
|
+
const options = {
|
|
401
|
+
suggestedName: files[i].name,
|
|
402
|
+
types: [
|
|
403
|
+
{
|
|
404
|
+
description: "Text Files"
|
|
405
|
+
}
|
|
406
|
+
]
|
|
407
|
+
};
|
|
408
|
+
files[i].handle =
|
|
409
|
+
await window.showSaveFilePicker(options);
|
|
410
|
+
} else if (files[i].kind === "directory") {
|
|
411
|
+
// Create a new subdirectory
|
|
412
|
+
files[i].handle = await files[
|
|
413
|
+
i
|
|
414
|
+
].handle.getDirectoryHandle("new_directory", {
|
|
415
|
+
create: true
|
|
416
|
+
});
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (files[i].handle.kind === "directory") continue;
|
|
422
|
+
|
|
423
|
+
const writable = await files[i].handle.createWritable();
|
|
424
|
+
await writable.write(files[i].src);
|
|
425
|
+
await writable.close();
|
|
426
|
+
} else {
|
|
427
|
+
const blob = new Blob([files[i].src], {
|
|
428
|
+
type: files[i].type
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Create a temporary <a> element to trigger the file download
|
|
432
|
+
const downloadLink = document.createElement("a");
|
|
433
|
+
downloadLink.href = URL.createObjectURL(blob);
|
|
434
|
+
downloadLink.download = files[i].name;
|
|
435
|
+
|
|
436
|
+
// Trigger the download
|
|
437
|
+
downloadLink.click();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let queryElements = queryElements({
|
|
443
|
+
element: element[i],
|
|
444
|
+
prefix: action
|
|
445
|
+
});
|
|
446
|
+
if (queryElements) {
|
|
447
|
+
save(queryElements, action, data);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return data;
|
|
451
|
+
} catch (error) {
|
|
452
|
+
if (error.name !== "AbortError") {
|
|
453
|
+
console.error("Error selecting files:", error);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
441
456
|
}
|
|
442
457
|
|
|
443
458
|
async function upload(element, data) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
459
|
+
if (!data) data = [];
|
|
460
|
+
|
|
461
|
+
if (!Array.isArray(element)) element = [element];
|
|
462
|
+
|
|
463
|
+
for (let i = 0; i < element.length; i++) {
|
|
464
|
+
const fileInputs = [];
|
|
465
|
+
if (element[i].type === "file") fileInputs.push(element[i]);
|
|
466
|
+
else if (element[i].tagName === "form") {
|
|
467
|
+
fileInputs.push(
|
|
468
|
+
...element[i].querySelectorAll('input[type="file"]')
|
|
469
|
+
);
|
|
470
|
+
} else {
|
|
471
|
+
const form = element[i].closest("form");
|
|
472
|
+
if (form)
|
|
473
|
+
fileInputs.push(...form.querySelectorAll('input[type="file"]'));
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
for (let input of fileInputs) {
|
|
477
|
+
let Data = Elements.getObject(input);
|
|
478
|
+
let object = input.getAttribute("object") || "";
|
|
479
|
+
let key = input.getAttribute("key");
|
|
480
|
+
|
|
481
|
+
Data.broadcastBrowser = false;
|
|
482
|
+
Data.method = "object.update";
|
|
483
|
+
if (!Data.array) Data.array = "files";
|
|
484
|
+
|
|
485
|
+
let path = input.getAttribute("path");
|
|
486
|
+
let directory = "/";
|
|
487
|
+
|
|
488
|
+
if (path) {
|
|
489
|
+
directory = path.split("/");
|
|
490
|
+
directory = directory[directory.length - 1];
|
|
491
|
+
if (!path.endsWith("/")) path += "/";
|
|
492
|
+
} else path = directory = "/";
|
|
493
|
+
|
|
494
|
+
if (!Data.host) Data.host = ["*"];
|
|
495
|
+
|
|
496
|
+
if (!Data.public) Data.public = true;
|
|
497
|
+
|
|
498
|
+
if (input.getFilter) {
|
|
499
|
+
Data.$filter = await input.getFilter();
|
|
500
|
+
if (!Data.$filter.query) Data.$filter.query = {};
|
|
501
|
+
} else
|
|
502
|
+
Data.$filter = {
|
|
503
|
+
query: {}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// let files = await getFiles(input, 'blob')
|
|
507
|
+
let files;
|
|
508
|
+
const selected = inputs.get(input);
|
|
509
|
+
if (selected) {
|
|
510
|
+
files = Array.from(selected.values());
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
let segmentSize = 10 * 1024 * 1024;
|
|
514
|
+
for (let i = 0; i < files.length; i++) {
|
|
515
|
+
files[i].path = path;
|
|
516
|
+
files[i].pathname = path + files[i].name;
|
|
517
|
+
files[i].directory = directory;
|
|
518
|
+
|
|
519
|
+
// let fileObject = { ...file }
|
|
520
|
+
// fileObject.size = file.size
|
|
521
|
+
// await getCustomData(fileObject)
|
|
522
|
+
|
|
523
|
+
if (input.processFile && files[i].size > segmentSize) {
|
|
524
|
+
// let test = await input.processFile(files[i], null, segmentSize, null, null, null, input);
|
|
525
|
+
let { playlist, segments } = await input.processFile(
|
|
526
|
+
files[i],
|
|
527
|
+
null,
|
|
528
|
+
segmentSize
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// Create a video element
|
|
532
|
+
const videoElement = document.createElement("video");
|
|
533
|
+
videoElement.setAttribute("controls", ""); // Add controls so you can play/pause
|
|
534
|
+
videoElement.style.width = "100%";
|
|
535
|
+
document.body.appendChild(videoElement);
|
|
536
|
+
|
|
537
|
+
const mediaSource = new MediaSource();
|
|
538
|
+
videoElement.src = URL.createObjectURL(mediaSource);
|
|
539
|
+
|
|
540
|
+
mediaSource.addEventListener("sourceopen", () => {
|
|
541
|
+
const sourceBuffer = mediaSource.addSourceBuffer(
|
|
542
|
+
'video/mp4; codecs="avc1.42E01E"'
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
sourceBuffer.addEventListener("updateend", () => {
|
|
546
|
+
console.log("Append operation completed.");
|
|
547
|
+
try {
|
|
548
|
+
console.log(
|
|
549
|
+
"Buffered ranges:",
|
|
550
|
+
sourceBuffer.buffered
|
|
551
|
+
);
|
|
552
|
+
// Append next segment here if applicable
|
|
553
|
+
} catch (e) {
|
|
554
|
+
console.error(
|
|
555
|
+
"Error accessing buffered property:",
|
|
556
|
+
e
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
function appendSegment(index) {
|
|
562
|
+
if (index >= segments.length) {
|
|
563
|
+
console.log("All segments have been appended.");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (!sourceBuffer.updating) {
|
|
568
|
+
segments[index].src
|
|
569
|
+
.arrayBuffer()
|
|
570
|
+
.then((arrayBuffer) => {
|
|
571
|
+
console.log(
|
|
572
|
+
`Appending segment ${index}`
|
|
573
|
+
);
|
|
574
|
+
sourceBuffer.appendBuffer(arrayBuffer);
|
|
575
|
+
// Next segment will be appended on 'updateend' event
|
|
576
|
+
})
|
|
577
|
+
.catch((error) => {
|
|
578
|
+
console.error(
|
|
579
|
+
`Error reading segment[${index}] as ArrayBuffer:`,
|
|
580
|
+
error
|
|
581
|
+
);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Append the first segment to start
|
|
587
|
+
appendSegment(0);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// mediaSource.addEventListener('sourceopen', () => {
|
|
591
|
+
// const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E"'); // avc1.4D401E, avc1.4D401F, avc1.4D4028, avc1.4D4020, avc1.4D4029, avc1.4D402A
|
|
592
|
+
|
|
593
|
+
// // Append the first segment to start
|
|
594
|
+
// if (!sourceBuffer.updating) {
|
|
595
|
+
|
|
596
|
+
// segments[0].src.arrayBuffer().then(arrayBuffer => {
|
|
597
|
+
// sourceBuffer.appendBuffer(arrayBuffer);
|
|
598
|
+
|
|
599
|
+
// // Wait for 3 seconds before logging the sourceBuffer state
|
|
600
|
+
// setTimeout(() => {
|
|
601
|
+
// console.log(sourceBuffer);
|
|
602
|
+
// }, 3000); // 3000 milliseconds = 3 seconds
|
|
603
|
+
|
|
604
|
+
// sourceBuffer.addEventListener('updateend', () => {
|
|
605
|
+
// console.log('Append operation completed.');
|
|
606
|
+
// try {
|
|
607
|
+
// console.log('Buffered ranges:', sourceBuffer.buffered);
|
|
608
|
+
// } catch (e) {
|
|
609
|
+
// console.error('Error accessing buffered property:', e);
|
|
610
|
+
// }
|
|
611
|
+
// // Proceed with additional operations here
|
|
612
|
+
// });
|
|
613
|
+
|
|
614
|
+
// }).catch(error => {
|
|
615
|
+
// console.error('Error reading segment[0] as ArrayBuffer:', error);
|
|
616
|
+
// });
|
|
617
|
+
|
|
618
|
+
// // segments[0].src.arrayBuffer().then(arrayBuffer => {
|
|
619
|
+
// // sourceBuffer.appendBuffer(arrayBuffer);
|
|
620
|
+
// // })
|
|
621
|
+
|
|
622
|
+
// // let segmentLength = 0
|
|
623
|
+
// // sourceBuffer.addEventListener('updateend', () => {
|
|
624
|
+
// // segmentLength += 1
|
|
625
|
+
// // if (segments[segmentLength])
|
|
626
|
+
// // segments[segmentLength].src.arrayBuffer().then(arrayBuffer => {
|
|
627
|
+
// // console.log(sourceBuffer)
|
|
628
|
+
// // // sourceBuffer.appendBuffer(arrayBuffer);
|
|
629
|
+
// // })
|
|
630
|
+
// // });
|
|
631
|
+
// }
|
|
632
|
+
// });
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// files[i].src = playlist
|
|
636
|
+
// for (let j = 0; j < segments.length; j++) {
|
|
637
|
+
// segments[j].path = path
|
|
638
|
+
// segments[j].pathname = path + segments[j].name
|
|
639
|
+
// segments[j].directory = directory
|
|
640
|
+
// segments[j] = { ...segments[j], ...await readFile(segments[j].src) }
|
|
641
|
+
// segments[j].public = true
|
|
642
|
+
// segments[j].host = ['*']
|
|
643
|
+
|
|
644
|
+
// playlist.segments[j].src = segments[j].pathname
|
|
645
|
+
// Data.$filter.query.pathname = segments[j].pathname
|
|
646
|
+
// Crud.send({
|
|
647
|
+
// ...Data,
|
|
648
|
+
// object: segments[j],
|
|
649
|
+
// upsert: true
|
|
650
|
+
// });
|
|
651
|
+
// }
|
|
652
|
+
|
|
653
|
+
// } else {
|
|
654
|
+
// files[i] = { ...files[i], ...await readFile(files[i].src) }
|
|
655
|
+
// }
|
|
656
|
+
|
|
657
|
+
// if (!key) {
|
|
658
|
+
// Data.object = { ...files[i] }
|
|
659
|
+
// } else {
|
|
660
|
+
// Data.object = { [key]: { ...files[i] } }
|
|
661
|
+
// }
|
|
662
|
+
|
|
663
|
+
// if (object) {
|
|
664
|
+
// Data.object._id = object // test
|
|
665
|
+
// }
|
|
666
|
+
|
|
667
|
+
// delete Data.object.input
|
|
668
|
+
// Data.$filter.query.pathname = files[i].pathname
|
|
669
|
+
// let response = await Crud.send({
|
|
670
|
+
// ...Data,
|
|
671
|
+
// upsert: true
|
|
672
|
+
// });
|
|
673
|
+
|
|
674
|
+
// console.log(response, 'tes')
|
|
675
|
+
// if (response && (!object || object !== response.object)) {
|
|
676
|
+
// Elements.setTypeValue(element, response);
|
|
677
|
+
// }
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
let queriedElements = queryElements({
|
|
682
|
+
element: element[i],
|
|
683
|
+
prefix: "upload"
|
|
684
|
+
});
|
|
685
|
+
if (queriedElements) {
|
|
686
|
+
upload(queriedElements, data);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return data;
|
|
665
690
|
}
|
|
666
691
|
|
|
667
692
|
async function Import(element, data) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
Import(queriedElements, data)
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
return data
|
|
693
|
+
if (!data) data = [];
|
|
694
|
+
|
|
695
|
+
if (!Array.isArray(element)) element = [element];
|
|
696
|
+
|
|
697
|
+
for (let i = 0; i < element.length; i++) {
|
|
698
|
+
const inputs = [];
|
|
699
|
+
if (element[i].type === "file") inputs.push(element[i]);
|
|
700
|
+
else if (element[i].tagName === "form") {
|
|
701
|
+
let fileInputs = element[i].querySelectorAll('input[type="file"]');
|
|
702
|
+
inputs.push(...fileInputs);
|
|
703
|
+
} else {
|
|
704
|
+
const form = element[i].closest("form");
|
|
705
|
+
if (form)
|
|
706
|
+
inputs.push(...form.querySelectorAll('input[type="file"]'));
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (inputs.length) {
|
|
710
|
+
let Data = await getFiles(inputs);
|
|
711
|
+
Data.reduce((result, { src }) => {
|
|
712
|
+
try {
|
|
713
|
+
const parsedSrc = JSON.parse(src);
|
|
714
|
+
if (Array.isArray(parsedSrc)) data.push(...parsedSrc);
|
|
715
|
+
else data.push(parsedSrc);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
console.error(`Error parsing JSON: ${error}`);
|
|
718
|
+
}
|
|
719
|
+
return result;
|
|
720
|
+
}, []);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (element[i].type !== "file") {
|
|
724
|
+
let Data = Elements.getObject(element[i]);
|
|
725
|
+
if (Data.type) {
|
|
726
|
+
if (element[i].getFilter)
|
|
727
|
+
Data.$filter = await element[i].getFilter();
|
|
728
|
+
|
|
729
|
+
if (Data.type === "key") Data.type = "object";
|
|
730
|
+
|
|
731
|
+
data.push(Data);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (data.length) {
|
|
736
|
+
for (let i = 0; i < data.length; i++) {
|
|
737
|
+
// TODO: if _id exist use update method
|
|
738
|
+
data[i].method = data[i].type + ".create";
|
|
739
|
+
data[i] = await Crud.send(data[i]);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
let queriedElements = queryElements({
|
|
744
|
+
element: element[i],
|
|
745
|
+
prefix: "import"
|
|
746
|
+
});
|
|
747
|
+
if (queriedElements) {
|
|
748
|
+
Import(queriedElements, data);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return data;
|
|
731
752
|
}
|
|
732
753
|
|
|
754
|
+
// TODO: Export selected rows or entire table or entire array
|
|
733
755
|
async function Export(element, data) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
if (data.length)
|
|
777
|
-
exportFile(data);
|
|
778
|
-
|
|
779
|
-
return data
|
|
756
|
+
if (!data) data = [];
|
|
757
|
+
|
|
758
|
+
if (!Array.isArray(element)) element = [element];
|
|
759
|
+
|
|
760
|
+
for (let i = 0; i < element.length; i++) {
|
|
761
|
+
const inputs = [];
|
|
762
|
+
if (element[i].type === "file") inputs.push(element[i]);
|
|
763
|
+
else if (element[i].tagName === "form") {
|
|
764
|
+
let fileInputs = element[i].querySelectorAll('input[type="file"]');
|
|
765
|
+
inputs.push(...fileInputs);
|
|
766
|
+
} else {
|
|
767
|
+
const form = element[i].closest("form");
|
|
768
|
+
if (form)
|
|
769
|
+
inputs.push(...form.querySelectorAll('input[type="file"]'));
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (inputs.length) data.push(...getFiles(inputs));
|
|
773
|
+
|
|
774
|
+
let Data = Elements.getObject(element[i]);
|
|
775
|
+
if (Data.type) {
|
|
776
|
+
if (element[i].getFilter)
|
|
777
|
+
Data.$filter = await element[i].getFilter();
|
|
778
|
+
|
|
779
|
+
if (Data.type === "key") Data.type = "object";
|
|
780
|
+
Data.method = Data.type + ".read";
|
|
781
|
+
Data = await Crud.send(Data);
|
|
782
|
+
data.push(...Data[Data.type]);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
let queriedElements = queryElements({
|
|
786
|
+
element: element[i],
|
|
787
|
+
prefix: "export"
|
|
788
|
+
});
|
|
789
|
+
if (queriedElements) {
|
|
790
|
+
Export(queriedElements, data);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (data.length) exportFile(data);
|
|
795
|
+
|
|
796
|
+
return data;
|
|
780
797
|
}
|
|
781
798
|
|
|
782
799
|
async function exportFile(data) {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
800
|
+
let name = data.type || "download";
|
|
801
|
+
let exportData = JSON.stringify(data, null, 2);
|
|
802
|
+
let blob = new Blob([exportData], { type: "application/json" });
|
|
803
|
+
let url = URL.createObjectURL(blob);
|
|
787
804
|
|
|
788
|
-
|
|
805
|
+
let link = document.createElement("a");
|
|
789
806
|
|
|
790
|
-
|
|
791
|
-
|
|
807
|
+
link.href = url;
|
|
808
|
+
link.download = name;
|
|
792
809
|
|
|
793
|
-
|
|
810
|
+
document.body.appendChild(link);
|
|
794
811
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
812
|
+
link.dispatchEvent(
|
|
813
|
+
new MouseEvent("click", {
|
|
814
|
+
bubbles: true,
|
|
815
|
+
cancelable: true,
|
|
816
|
+
view: window
|
|
817
|
+
})
|
|
818
|
+
);
|
|
802
819
|
|
|
803
|
-
|
|
804
|
-
|
|
820
|
+
URL.revokeObjectURL(url);
|
|
821
|
+
link.remove();
|
|
805
822
|
}
|
|
806
823
|
|
|
807
824
|
// TODO: handled by import? if value is a valid url get file by url?
|
|
808
825
|
async function importURL(action) {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
console.error('Error importing file from URL:', error);
|
|
855
|
-
throw error;
|
|
856
|
-
}
|
|
826
|
+
try {
|
|
827
|
+
let element = action.element;
|
|
828
|
+
let url = element.getAttribute("url");
|
|
829
|
+
if (!url) {
|
|
830
|
+
element = action.form.querySelector("[import-url]");
|
|
831
|
+
if (!element) return;
|
|
832
|
+
url = element.getValue();
|
|
833
|
+
if (!url) return;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const urlObject = new URL(url);
|
|
837
|
+
const filename = urlObject.pathname.split("/").pop();
|
|
838
|
+
|
|
839
|
+
const file = {
|
|
840
|
+
src: url,
|
|
841
|
+
name: filename,
|
|
842
|
+
directory: "/",
|
|
843
|
+
path: "/",
|
|
844
|
+
pathname: "/" + filename
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
await getCustomData(file);
|
|
848
|
+
|
|
849
|
+
let data = await Crud.socket.send({
|
|
850
|
+
method: "importUrl",
|
|
851
|
+
file,
|
|
852
|
+
broadcast: false,
|
|
853
|
+
broadcastClient: false
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
let queriedElements = queryElements({ element, prefix: "import-url" });
|
|
857
|
+
if (queriedElements) {
|
|
858
|
+
for (let queriedElement of queriedElements)
|
|
859
|
+
queriedElement.setValue(data.file);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
document.dispatchEvent(
|
|
863
|
+
new CustomEvent(action.name, {
|
|
864
|
+
detail: {}
|
|
865
|
+
})
|
|
866
|
+
);
|
|
867
|
+
} catch (error) {
|
|
868
|
+
console.error("Error importing file from URL:", error);
|
|
869
|
+
throw error;
|
|
870
|
+
}
|
|
857
871
|
}
|
|
858
872
|
|
|
859
873
|
async function fileRenderAction(action) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
}));
|
|
890
|
-
|
|
874
|
+
const element = action.element;
|
|
875
|
+
|
|
876
|
+
let file_id = element.getAttribute("file_id");
|
|
877
|
+
if (!file_id) {
|
|
878
|
+
const closestElement = element.closest("[file_id]");
|
|
879
|
+
if (closestElement) file_id = closestElement.getAttribute("file_id");
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
let input = Files.get(file_id).input;
|
|
883
|
+
|
|
884
|
+
if (!file_id || !input) return;
|
|
885
|
+
|
|
886
|
+
let file = inputs.get(input).get(file_id);
|
|
887
|
+
if (!file) return;
|
|
888
|
+
|
|
889
|
+
if (action.name === "createFile") {
|
|
890
|
+
let name = element.getAttribute("value");
|
|
891
|
+
create(file, "file", name);
|
|
892
|
+
} else if (action.name === "deleteFile") Delete(file);
|
|
893
|
+
else if (action.name === "createDirectory") {
|
|
894
|
+
let name = element.getAttribute("value");
|
|
895
|
+
create(file, "directory", name);
|
|
896
|
+
} else if (action.name === "deleteDirectory") Delete(file);
|
|
897
|
+
|
|
898
|
+
document.dispatchEvent(
|
|
899
|
+
new CustomEvent(action.name, {
|
|
900
|
+
detail: {}
|
|
901
|
+
})
|
|
902
|
+
);
|
|
891
903
|
}
|
|
892
904
|
|
|
893
905
|
async function create(directory, type, name, src = "") {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
906
|
+
try {
|
|
907
|
+
if (directory.handle && directory.input) {
|
|
908
|
+
if (!name) {
|
|
909
|
+
const name = prompt("Enter the file name:");
|
|
910
|
+
if (!name) {
|
|
911
|
+
console.log("Invalid file name.");
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
let handle, file;
|
|
917
|
+
if (type === "directory") {
|
|
918
|
+
handle = await directory.handle.getDirectoryHandle(name, {
|
|
919
|
+
create: true
|
|
920
|
+
});
|
|
921
|
+
file = { name: handle.name, type: "text/directory" };
|
|
922
|
+
} else if (type === "file") {
|
|
923
|
+
handle = await directory.handle.getFileHandle(name, {
|
|
924
|
+
create: true
|
|
925
|
+
});
|
|
926
|
+
const writable = await handle.createWritable();
|
|
927
|
+
|
|
928
|
+
// Write data to the new file...
|
|
929
|
+
await writable.write(src);
|
|
930
|
+
await writable.close();
|
|
931
|
+
|
|
932
|
+
file = handle.getFile();
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (directory.input) {
|
|
936
|
+
file.directory = directory.name;
|
|
937
|
+
file.pathname = directory.path + "/" + file.name;
|
|
938
|
+
file.path = directory.path + "/" + file.name;
|
|
939
|
+
file.input = directory.input;
|
|
940
|
+
file.handle = handle;
|
|
941
|
+
file["content-type"] = file.type;
|
|
942
|
+
|
|
943
|
+
file.id = await getFileId(file);
|
|
944
|
+
if (inputs.get(directory.input).has(file.id)) {
|
|
945
|
+
console.log(
|
|
946
|
+
"Duplicate file has been selected. This could be in error as the browser does not provide a clear way of checking duplictaes"
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
inputs.get(directory.input).set(file.id, file);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
} catch (error) {
|
|
954
|
+
console.log("Error adding file:", error);
|
|
955
|
+
}
|
|
939
956
|
}
|
|
940
957
|
|
|
941
958
|
async function Delete(file) {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
}
|
|
959
|
+
try {
|
|
960
|
+
if (file.handle) {
|
|
961
|
+
await file.handle.remove();
|
|
962
|
+
if (file.input && file.id) inputs.get(file.input).delete(file.id);
|
|
963
|
+
}
|
|
964
|
+
} catch (error) {
|
|
965
|
+
console.log("Error deleting file:", error);
|
|
966
|
+
}
|
|
951
967
|
}
|
|
952
968
|
|
|
953
969
|
Observer.init({
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
970
|
+
name: "CoCreateFileAddedNodes",
|
|
971
|
+
observe: ["addedNodes"],
|
|
972
|
+
selector: '[type="file"]',
|
|
973
|
+
callback: (mutation) => init(mutation.target)
|
|
959
974
|
});
|
|
960
975
|
|
|
961
976
|
Observer.init({
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
977
|
+
name: "CoCreateFileAttributes",
|
|
978
|
+
observe: ["attributes"],
|
|
979
|
+
attributeName: ["type"],
|
|
980
|
+
selector: '[type="file"]',
|
|
981
|
+
callback: (mutation) => init(mutation.target)
|
|
967
982
|
});
|
|
968
983
|
|
|
969
984
|
Actions.init([
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
985
|
+
{
|
|
986
|
+
name: [
|
|
987
|
+
"upload",
|
|
988
|
+
"download",
|
|
989
|
+
"saveLocally",
|
|
990
|
+
"asveAs",
|
|
991
|
+
"import",
|
|
992
|
+
"export",
|
|
993
|
+
"importUrl"
|
|
994
|
+
],
|
|
995
|
+
callback: (action) => {
|
|
996
|
+
if (action.name === "upload") upload(action.element);
|
|
997
|
+
else if (
|
|
998
|
+
action.name === "saveLocally" ||
|
|
999
|
+
action.name === "saveAs"
|
|
1000
|
+
) {
|
|
1001
|
+
save(action.element);
|
|
1002
|
+
} else if (action.name === "export") {
|
|
1003
|
+
Export(action.element);
|
|
1004
|
+
} else if (action.name === "import") {
|
|
1005
|
+
Import(action.element);
|
|
1006
|
+
} else if (action.name === "importUrl") {
|
|
1007
|
+
importURL(action);
|
|
1008
|
+
} else {
|
|
1009
|
+
// Something...
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
document.dispatchEvent(
|
|
1013
|
+
new CustomEvent(action.name, {
|
|
1014
|
+
detail: {}
|
|
1015
|
+
})
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
name: [
|
|
1021
|
+
"createFile",
|
|
1022
|
+
"deleteFile",
|
|
1023
|
+
"createDirectory",
|
|
1024
|
+
"deleteDirectory"
|
|
1025
|
+
],
|
|
1026
|
+
callback: (action) => {
|
|
1027
|
+
fileRenderAction(action);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
]);
|
|
1031
|
+
|
|
1032
|
+
init();
|
|
1033
|
+
|
|
1034
|
+
export default { inputs, getFiles, create, Delete };
|