UNPKG

3.31 kBJavaScriptView Raw
1const defaultOpts = {name: "files[]"};
2
3export default function Uppie() { // eslint-disable-line import/no-unused-modules
4 return (node, opts, cb) => {
5 if (typeof opts === "function") {
6 cb = opts;
7 opts = defaultOpts;
8 } else {
9 if (!opts) opts = {};
10 if (!opts.name) opts.name = defaultOpts.name;
11 }
12
13 if (node instanceof NodeList) {
14 for (const n of [].slice.call(node)) {
15 watch(n, opts, cb);
16 }
17 } else {
18 watch(node, opts, cb);
19 }
20 };
21}
22
23function watch(node, opts, cb) {
24 if (node.tagName?.toLowerCase() === "input" && node.type === "file") {
25 node.addEventListener("change", e => {
26 const t = e.target;
27 if (t?.files?.length) {
28 arrayApi(t, opts, cb.bind(null, e));
29 } else {
30 cb(e);
31 }
32 });
33 } else {
34 const stop = e => e.preventDefault();
35 node.addEventListener("dragover", stop);
36 node.addEventListener("dragenter", stop);
37 node.addEventListener("drop", (e) => {
38 e.preventDefault();
39 const dt = e.dataTransfer;
40 if (dt.items?.[0]?.webkitGetAsEntry()) {
41 entriesApi(dt.items, opts, cb.bind(null, e));
42 } else if (dt.files) {
43 arrayApi(dt, opts, cb.bind(null, e));
44 } else cb(e);
45 });
46 }
47}
48
49// old prefixed API implemented in Chrome 11+ as well as array fallback
50function arrayApi(input, opts, cb) {
51 const fd = new FormData();
52 const files = [];
53
54 for (const file of [].slice.call(input.files)) {
55 fd.append(opts.name, file, file.webkitRelativePath || file.name);
56 files.push(file.webkitRelativePath || file.name);
57 }
58 cb(fd, files);
59}
60
61function readEntries(entry, reader, oldEntries, cb) {
62 const dirReader = reader || entry.createReader();
63
64 dirReader.readEntries((entries) => {
65 const newEntries = oldEntries ? oldEntries.concat(entries) : entries;
66 if (entries.length) {
67 setTimeout(readEntries.bind(null, entry, dirReader, newEntries, cb), 0);
68 } else {
69 cb(newEntries);
70 }
71 });
72}
73
74// old drag and drop API implemented in Chrome 11+
75function entriesApi(items, opts, cb) {
76 const fd = new FormData(), files = [], rootPromises = [];
77
78 function readDirectory(entry, path, resolve) {
79 if (!path) path = entry.name;
80 readEntries(entry, 0, 0, (entries) => {
81 const promises = [];
82 for (const entry of entries) {
83 promises.push(new Promise((resolve) => {
84 if (entry.isFile) {
85 entry.file((file) => {
86 const p = `${path}/${file.name}`;
87 fd.append(opts.name, file, p);
88 files.push(p);
89 resolve();
90 }, resolve.bind());
91 } else readDirectory(entry, `${path}/${entry.name}`, resolve);
92 }));
93 }
94 Promise.all(promises).then(resolve.bind());
95 });
96 }
97
98 for (let entry of [].slice.call(items)) {
99 entry = entry.webkitGetAsEntry();
100 if (entry) {
101 rootPromises.push(new Promise((resolve) => {
102 if (entry.isFile) {
103 entry.file((file) => {
104 fd.append(opts.name, file, file.name);
105 files.push(file.name);
106 resolve();
107 }, resolve.bind());
108 } else if (entry.isDirectory) {
109 readDirectory(entry, null, resolve);
110 }
111 }));
112 }
113 }
114 Promise.all(rootPromises).then(cb.bind(null, fd, files));
115}