@environment-safe/file 0.0.1 → 0.1.0
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/.husky/pre-commit +5 -3
- package/README.md +25 -7
- package/package.json +19 -5
- package/src/buffer.d.mts +5 -0
- package/src/buffer.mjs +9 -3
- package/src/filesystem.d.mts +47 -0
- package/src/filesystem.mjs +449 -0
- package/src/index.d.mts +27 -0
- package/src/index.mjs +146 -519
- package/src/path.d.mts +35 -0
- package/src/path.mjs +835 -0
- package/test/isolated/crud.mjs +0 -0
- package/test/isolated/path.mjs +48 -0
- package/test/isolated-path.html +448 -0
- package/test/test.html +466 -0
- package/test/test.mjs +77 -22
- /package/test/{index.html → local-file-test.html} +0 -0
package/src/index.mjs
CHANGED
|
@@ -1,378 +1,138 @@
|
|
|
1
|
-
/*
|
|
2
|
-
import { isBrowser, isJsDom } from 'browser-or-node';
|
|
3
|
-
import * as mod from 'module';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
let internalRequire = null;
|
|
6
|
-
if(typeof require !== 'undefined') internalRequire = require;
|
|
7
|
-
const ensureRequire = ()=> (!internalRequire) && (internalRequire = mod.createRequire(import.meta.url));
|
|
8
|
-
//*/
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
2
|
* A JSON object
|
|
12
3
|
* @typedef { object } JSON
|
|
13
4
|
*/
|
|
14
5
|
|
|
15
|
-
|
|
6
|
+
// Isn't the fact that we had 2 path scenarios driven by OS, fixed it, (win, unix)
|
|
7
|
+
// and we now have an explosion an obvious indicator that we went wrong?
|
|
8
|
+
// win, posix, file:win, file:posix, known dirs, web urls, plus file: behavior is different on local vs server
|
|
9
|
+
|
|
16
10
|
import { FileBuffer } from './buffer.mjs';
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}catch(ex){
|
|
28
|
-
inputQueue.unshift(input);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
window.addEventListener('load', (event) => {
|
|
33
|
-
document.body.addEventListener(eventType, handler, false);
|
|
34
|
-
});
|
|
35
|
-
//document.body.addEventListener(eventType, handler, false);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
if(isBrowser || isJsDom){
|
|
39
|
-
attachInputGenerator('mousedown');
|
|
40
|
-
// mousemove is cleanest, but seems unreliable
|
|
41
|
-
// attachInputGenerator('mousemove');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const wantInput = async (id, handler, cache)=>{
|
|
45
|
-
const promise = new Promise((resolve, reject)=>{
|
|
46
|
-
inputQueue.push({ resolve, reject, handler });
|
|
47
|
-
});
|
|
48
|
-
const input = await promise;
|
|
49
|
-
return await input;
|
|
11
|
+
import {
|
|
12
|
+
isServer, // is running on a server runtime
|
|
13
|
+
isLocalFileRoot, // run within a page using a file: url
|
|
14
|
+
variables
|
|
15
|
+
} from '@environment-safe/runtime-context';
|
|
16
|
+
import { localFile as lf, serverFile as sf, file as f, remote as r, setInputHandler, bindInput} from './filesystem.mjs';
|
|
17
|
+
import { Path } from './path.mjs';
|
|
18
|
+
export { Path, setInputHandler, bindInput };
|
|
19
|
+
const handleCanonicalPath = (dir, os, user)=>{
|
|
20
|
+
|
|
50
21
|
};
|
|
51
22
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
suggestedName: name
|
|
57
|
-
};
|
|
58
|
-
if(path) options.startIn = path;
|
|
59
|
-
if(suffix){
|
|
60
|
-
const accept = {};
|
|
61
|
-
accept[mimesBySuffix[suffix]] = '.'+suffix;
|
|
62
|
-
options.types = [{
|
|
63
|
-
description: suffix,
|
|
64
|
-
accept,
|
|
65
|
-
}];
|
|
66
|
-
options.excludeAcceptAllOption = true;
|
|
67
|
-
}
|
|
68
|
-
return options;
|
|
69
|
-
};
|
|
23
|
+
let localFile=null;
|
|
24
|
+
let serverFile=null;
|
|
25
|
+
let file=null;
|
|
26
|
+
let remote=null;
|
|
70
27
|
|
|
71
|
-
const
|
|
72
|
-
if(
|
|
73
|
-
if(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}else{
|
|
80
|
-
return pathJoin(File.directory.current, '..', path);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
28
|
+
export const initialized = async (path, options={})=>{
|
|
29
|
+
if(isServer){
|
|
30
|
+
if(serverFile) return;
|
|
31
|
+
serverFile = await sf.initialize();
|
|
32
|
+
}else{
|
|
33
|
+
if(isLocalFileRoot){
|
|
34
|
+
if(file) return;
|
|
35
|
+
file = await f.initialize();
|
|
83
36
|
}else{
|
|
84
|
-
if(
|
|
85
|
-
|
|
37
|
+
if(path.indexOf('://') !== -1){
|
|
38
|
+
if(path.indexOf('file://') === 0){
|
|
39
|
+
try{
|
|
40
|
+
Path.relative(path);
|
|
41
|
+
}catch(ex){
|
|
42
|
+
//if it's not
|
|
43
|
+
// 1) A file url root
|
|
44
|
+
// 2) within the web root
|
|
45
|
+
// we have no idea what to do
|
|
46
|
+
console.log(ex);
|
|
47
|
+
throw new Error(`Could not resolve path:${path}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if(remote) return;
|
|
51
|
+
//remote url
|
|
52
|
+
remote = await r.initialize();
|
|
86
53
|
}else{
|
|
87
|
-
if(
|
|
88
|
-
|
|
54
|
+
if(options.filesystemAPI){
|
|
55
|
+
if(localFile) return;
|
|
56
|
+
//an absolute or relative file path
|
|
57
|
+
localFile = await lf.initialize();
|
|
89
58
|
}else{
|
|
90
|
-
|
|
59
|
+
if(remote) return;
|
|
60
|
+
//remote url
|
|
61
|
+
remote = await r.initialize();
|
|
91
62
|
}
|
|
92
63
|
}
|
|
93
64
|
}
|
|
94
65
|
}
|
|
95
|
-
return dir?handleCanonicalPath(dir, File.os, File.user)+ '/' + path:path;
|
|
96
66
|
};
|
|
97
67
|
|
|
98
|
-
export const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
window.showSaveFilePicker(options).then((thisHandle)=>{
|
|
105
|
-
resolve(thisHandle);
|
|
106
|
-
}).catch((ex)=>{
|
|
107
|
-
reject(ex);
|
|
108
|
-
});
|
|
109
|
-
}catch(ex){
|
|
110
|
-
reject(ex);
|
|
111
|
-
}
|
|
112
|
-
}, meta.cache);
|
|
113
|
-
const writableStream = await newHandle.createWritable();
|
|
114
|
-
// write our file
|
|
115
|
-
await writableStream.write(buffer);
|
|
116
|
-
// close the file and write the contents to disk.
|
|
117
|
-
await writableStream.close();
|
|
68
|
+
export const act = async (action, ...args)=>{
|
|
69
|
+
const path = args[0];
|
|
70
|
+
const options = args[1] || {};
|
|
71
|
+
await initialized(path, options);
|
|
72
|
+
if(isServer){
|
|
73
|
+
return serverFile[action].apply(serverFile, args);
|
|
118
74
|
}else{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
ssv : 'text/ssv',
|
|
139
|
-
js : 'text/javascript',
|
|
140
|
-
mjs : 'text/javascript',
|
|
141
|
-
cjs : 'text/javascript',
|
|
142
|
-
css : 'text/css',
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export const pathJoin = (...parts)=>{ //returns buffer, eventually stream
|
|
146
|
-
if(isBrowser || isJsDom){
|
|
147
|
-
return parts.join('/');
|
|
148
|
-
}else{
|
|
149
|
-
return path.join.apply(path, parts);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
//todo: should I remove export (no one should use this)?
|
|
155
|
-
export const fileBody = async (path, dir, baseDir, allowRedirect, forceReturn)=>{
|
|
156
|
-
try{
|
|
157
|
-
//let location = dir?dir+ '/' + path:path; //todo: looser handling
|
|
158
|
-
let location = makeLocation(path, dir);
|
|
159
|
-
if(canonicalLocationToPath['darwin'][dir]){
|
|
160
|
-
if(baseDir){
|
|
161
|
-
throw new Error('custom directories unsupported');
|
|
75
|
+
if(isLocalFileRoot){
|
|
76
|
+
return file[action].apply(file, args);
|
|
77
|
+
}else{
|
|
78
|
+
if(path.indexOf('://') !== -1){
|
|
79
|
+
if(path.indexOf('file://') === 0){
|
|
80
|
+
try{
|
|
81
|
+
const currentProtocol = variables.location?variables.location.protocol:'http:';
|
|
82
|
+
const relativePath = Path.from(path).toUrl(currentProtocol, true);
|
|
83
|
+
args[0] = '../'+relativePath;
|
|
84
|
+
}catch(ex){
|
|
85
|
+
//if it's not
|
|
86
|
+
// 1) A file url root
|
|
87
|
+
// 2) within the web root
|
|
88
|
+
// we have no idea what to do
|
|
89
|
+
throw new Error(`Could not resolve path:${path}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return remote[action].apply(remote, args);
|
|
93
|
+
|
|
162
94
|
}else{
|
|
163
|
-
|
|
95
|
+
if(options.filesystemAPI){
|
|
96
|
+
return localFile[action].apply(localFile, args);
|
|
97
|
+
}else{
|
|
98
|
+
return remote[action].apply(remote, args);
|
|
99
|
+
}
|
|
164
100
|
}
|
|
165
101
|
}
|
|
166
|
-
const response = await fetch(location);
|
|
167
|
-
const text = await response.text();
|
|
168
|
-
if(!(response.ok || (allowRedirect && response.redirected) || forceReturn)){
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
return text;
|
|
172
|
-
}catch(ex){
|
|
173
|
-
//console.log(location, ex);
|
|
174
|
-
return null;
|
|
175
102
|
}
|
|
176
103
|
};
|
|
177
104
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const location = makeLocation(path, dir);
|
|
182
|
-
if(isBrowser || isJsDom){
|
|
183
|
-
if(cache && cache[path]) return cache[path];
|
|
184
|
-
const options = getFilePickerOptions(path);
|
|
185
|
-
try{
|
|
186
|
-
const response = await fileBody(path, dir);
|
|
187
|
-
if(response === null) throw new Error('File not found');
|
|
188
|
-
}catch(ex){
|
|
189
|
-
const newHandle = await wantInput(location, (event, resolve, reject)=>{
|
|
190
|
-
try{
|
|
191
|
-
window.showSaveFilePicker(options).then((thisHandle)=>{
|
|
192
|
-
resolve(thisHandle);
|
|
193
|
-
}).catch((ex)=>{
|
|
194
|
-
reject(ex);
|
|
195
|
-
});
|
|
196
|
-
}catch(ex){
|
|
197
|
-
reject(ex);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
return newHandle;
|
|
201
|
-
}
|
|
202
|
-
const fileHandle = await wantInput(location, (event, resolve, reject)=>{
|
|
203
|
-
try{
|
|
204
|
-
window.showOpenFilePicker(options).then(([ handle ])=>{
|
|
205
|
-
resolve(handle);
|
|
206
|
-
}).catch((ex)=>{
|
|
207
|
-
reject(ex);
|
|
208
|
-
});
|
|
209
|
-
}catch(ex){
|
|
210
|
-
reject(ex);
|
|
211
|
-
}
|
|
212
|
-
}, cache);
|
|
213
|
-
// eslint-disable-next-line no-undef
|
|
214
|
-
if(cache) cache[location] = fileHandle;
|
|
215
|
-
// eslint-disable-next-line no-undef
|
|
216
|
-
return fileHandle;
|
|
217
|
-
}else{
|
|
218
|
-
// todo: impl
|
|
219
|
-
}
|
|
105
|
+
//*
|
|
106
|
+
export const read = async (path, options)=>{
|
|
107
|
+
return await act('read', path, options);
|
|
220
108
|
};
|
|
221
109
|
|
|
222
|
-
export const
|
|
223
|
-
|
|
224
|
-
if(isBrowser || isJsDom){
|
|
225
|
-
try{
|
|
226
|
-
const response = await fetch(location);
|
|
227
|
-
if(!response){
|
|
228
|
-
return new ArrayBuffer();
|
|
229
|
-
}
|
|
230
|
-
const buffer = await response.arrayBuffer();
|
|
231
|
-
buffer;
|
|
232
|
-
return buffer;
|
|
233
|
-
}catch(ex){
|
|
234
|
-
return new ArrayBuffer();
|
|
235
|
-
}
|
|
236
|
-
}else{
|
|
237
|
-
return await new Promise((resolve, reject)=>{
|
|
238
|
-
fs.readFile(location, (err, body)=>{
|
|
239
|
-
if(err) return reject(err);
|
|
240
|
-
resolve(body);
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
}
|
|
110
|
+
export const list = async (path, options)=>{
|
|
111
|
+
return await act('list', path, options);
|
|
244
112
|
};
|
|
245
113
|
|
|
246
|
-
export const
|
|
247
|
-
|
|
248
|
-
if(incomingHandle){
|
|
249
|
-
const fileHandle = incomingHandle;
|
|
250
|
-
const file = await fileHandle.getFile();
|
|
251
|
-
const buffer = await file.arrayBuffer();
|
|
252
|
-
return !!buffer;
|
|
253
|
-
}else{
|
|
254
|
-
const body = await fileBody(path, dir);
|
|
255
|
-
return body !== null;
|
|
256
|
-
}
|
|
257
|
-
}else{
|
|
258
|
-
return await new Promise((resolve, reject)=>{
|
|
259
|
-
const location = makeLocation(path, dir);
|
|
260
|
-
fs.stat(location, (err, res)=>{
|
|
261
|
-
if(err) resolve(false);
|
|
262
|
-
resolve(true);
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
}
|
|
114
|
+
export const write = async (path, buffer, options)=>{
|
|
115
|
+
return await act('write', path, buffer, options);
|
|
266
116
|
};
|
|
267
117
|
|
|
268
|
-
export const
|
|
269
|
-
|
|
270
|
-
const fileHandle = await handle(path, dir, true, cache);
|
|
271
|
-
if(fileHandle.remove) fileHandle.remove(); //non-standard, but supported
|
|
272
|
-
}else{
|
|
273
|
-
// todo: impl
|
|
274
|
-
}
|
|
118
|
+
export const create = async (path)=>{
|
|
119
|
+
return await act('create', path);
|
|
275
120
|
};
|
|
276
121
|
|
|
277
|
-
export const
|
|
278
|
-
|
|
279
|
-
// todo: impl
|
|
280
|
-
}else{
|
|
281
|
-
// todo: impl
|
|
282
|
-
}
|
|
122
|
+
export const exists = async (path)=>{
|
|
123
|
+
return await act('exists', path);
|
|
283
124
|
};
|
|
284
125
|
|
|
285
|
-
export const
|
|
286
|
-
|
|
287
|
-
// todo: impl
|
|
288
|
-
switch(File.agent.name){
|
|
289
|
-
case 'chrome': {
|
|
290
|
-
const page = await fileBody('', path, null, null, true);
|
|
291
|
-
let rows = (page && page.match( /<script>addRow\((.*)\);<\/script>/g ) ) || [];
|
|
292
|
-
rows = rows.map((row)=>{
|
|
293
|
-
return row.match( /<script>addRow\((.*)\);<\/script>/ )[1];
|
|
294
|
-
});
|
|
295
|
-
const jsonData = `[[${rows.join('], [')}]]`;
|
|
296
|
-
const data = JSON.parse(jsonData);
|
|
297
|
-
let results = data.map((meta)=>{
|
|
298
|
-
return {
|
|
299
|
-
name: meta[0],
|
|
300
|
-
isFile: ()=>{
|
|
301
|
-
return !!meta[2];
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
});
|
|
305
|
-
if(Object.keys(options).length){
|
|
306
|
-
if(options.files === false){
|
|
307
|
-
results = results.filter((file)=>{
|
|
308
|
-
return !file.isFile();
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
if(options.directories === false){
|
|
312
|
-
results = results.filter((file)=>{
|
|
313
|
-
return file.isFile();
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
if(!options.hidden){
|
|
317
|
-
results = results.filter((file)=>{
|
|
318
|
-
return file !== '.' && file !== '..';
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return results.map((file)=>{
|
|
323
|
-
return file.name;
|
|
324
|
-
});
|
|
325
|
-
//TODO: apache fallback
|
|
326
|
-
//break;
|
|
327
|
-
}
|
|
328
|
-
default: throw new Error(`Usupported Browser: ${File.os}`);
|
|
329
|
-
}
|
|
330
|
-
}else{
|
|
331
|
-
//todo: platform safe separator
|
|
332
|
-
const target = path.indexOf('/') === -1?makeLocation('', path):path;
|
|
333
|
-
return await new Promise((resolve, reject)=>{
|
|
334
|
-
fs.readdir(target, { withFileTypes: true }, (err, files)=>{
|
|
335
|
-
if(err) return reject(err);
|
|
336
|
-
let results = files;
|
|
337
|
-
if(Object.keys(options).length){
|
|
338
|
-
if(options.files === false){
|
|
339
|
-
results = results.filter((file)=>{
|
|
340
|
-
return !file.isFile();
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
if(options.directories === false){
|
|
344
|
-
results = results.filter((file)=>{
|
|
345
|
-
return file.isFile();
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
if(!options.hidden){
|
|
349
|
-
results = results.filter((file)=>{
|
|
350
|
-
return file !== '.' && file !== '..';
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
resolve(results.map((file)=>{
|
|
355
|
-
return file.name;
|
|
356
|
-
}));
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
}
|
|
126
|
+
export const remove = async (path)=>{
|
|
127
|
+
return await act('delete', path);
|
|
360
128
|
};
|
|
361
129
|
|
|
362
130
|
const internalCache = {};
|
|
363
131
|
|
|
364
|
-
export const listFiles = (path)=>{
|
|
365
|
-
return list(path).map((src)=>{
|
|
366
|
-
const url = src.indexOf('://') !== -1?src:`file://${src}`;
|
|
367
|
-
return new File(url);
|
|
368
|
-
});
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
|
|
372
132
|
export class File{
|
|
373
133
|
constructor(path, options={}){
|
|
374
134
|
//todo: clean this rats nest up
|
|
375
|
-
const location = (
|
|
135
|
+
const location = ( path ) ||
|
|
376
136
|
( (!path) && options.directory && handleCanonicalPath(options.directory, File.os, File.user) ) ||
|
|
377
137
|
('/tmp/' + Math.floor( Math.random() * 10000 ));
|
|
378
138
|
if(options.cache === true) options.cache = internalCache;
|
|
@@ -380,220 +140,87 @@ export class File{
|
|
|
380
140
|
//one of: desktop, documents, downloads, music, pictures, videos
|
|
381
141
|
this.directory = options.directory || '.';
|
|
382
142
|
this.path = location;
|
|
383
|
-
this.
|
|
143
|
+
this.setBuffer(FileBuffer.from(''));
|
|
384
144
|
}
|
|
385
145
|
|
|
386
146
|
async save(){
|
|
387
|
-
await
|
|
147
|
+
await write(this.path, this.buffer, this.options);
|
|
388
148
|
return this;
|
|
389
149
|
}
|
|
390
150
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
this.buffer
|
|
151
|
+
setBuffer(buffer){
|
|
152
|
+
this.buffer = buffer;
|
|
153
|
+
if(!this.buffer) throw new Error(`Error: ${this.path} ${this.options}`);
|
|
394
154
|
this.buffer.cast = (type)=>{
|
|
395
155
|
return FileBuffer.to(type, this.buffer);
|
|
396
156
|
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async load(){
|
|
160
|
+
this.setBuffer(await read(this.path, this.options));
|
|
397
161
|
return this;
|
|
398
162
|
}
|
|
399
163
|
|
|
400
164
|
body(value){
|
|
401
165
|
if(value === null || value === undefined) return this.buffer;
|
|
402
|
-
this.
|
|
403
|
-
this
|
|
404
|
-
return FileBuffer.to(type, this.buffer);
|
|
405
|
-
};
|
|
406
|
-
if(value) return this;
|
|
407
|
-
return this.buffer;
|
|
166
|
+
this.setBuffer(FileBuffer.from(value));
|
|
167
|
+
return this;
|
|
408
168
|
}
|
|
409
169
|
|
|
410
170
|
async info(){
|
|
411
|
-
return await info(this.path
|
|
171
|
+
//return await info(this.path);
|
|
412
172
|
}
|
|
413
173
|
|
|
414
174
|
async 'delete'(){
|
|
415
|
-
await remove(this.path, this.
|
|
175
|
+
await remove(this.path, this.options);
|
|
416
176
|
return this;
|
|
417
177
|
}
|
|
418
178
|
|
|
419
|
-
static exists(path, directory){
|
|
420
|
-
return exists(path, directory);
|
|
179
|
+
static async exists(path, directory){
|
|
180
|
+
return await exists(path, directory);
|
|
421
181
|
}
|
|
422
182
|
|
|
423
|
-
static list(path, options){
|
|
424
|
-
return list(path, options);
|
|
183
|
+
static async list(path, options){
|
|
184
|
+
return await list(path, options);
|
|
425
185
|
}
|
|
426
186
|
}
|
|
427
|
-
let user = '';
|
|
428
|
-
Object.defineProperty(File, 'user', {
|
|
429
|
-
get() {
|
|
430
|
-
if(isBrowser || isJsDom){
|
|
431
|
-
return user || 'khrome'; //todo: something real;
|
|
432
|
-
}else{
|
|
433
|
-
return user || 'khrome'; //todo: something real;
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
set(newValue) {
|
|
437
|
-
user = newValue;
|
|
438
|
-
},
|
|
439
|
-
enumerable: true,
|
|
440
|
-
configurable: true,
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
Object.defineProperty(File, 'os', {
|
|
444
|
-
get() {
|
|
445
|
-
if(isBrowser || isJsDom){
|
|
446
|
-
return 'darwin'; //todo: something real;
|
|
447
|
-
}else{
|
|
448
|
-
return 'darwin';
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
set(newValue) {
|
|
452
|
-
//do nothing
|
|
453
|
-
},
|
|
454
|
-
enumerable: true,
|
|
455
|
-
configurable: true,
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
const canonicalLocationToPath = {
|
|
459
|
-
darwin : {
|
|
460
|
-
'desktop': '~/Desktop',
|
|
461
|
-
'documents': '~/Documents',
|
|
462
|
-
'downloads': '~/Downloads',
|
|
463
|
-
'music': '~/Music',
|
|
464
|
-
'pictures': '~/Pictures',
|
|
465
|
-
'home': '~/Pictures',
|
|
466
|
-
'videos': '~/Movies'
|
|
467
|
-
},
|
|
468
|
-
win : {},
|
|
469
|
-
linux : {},
|
|
470
|
-
};
|
|
471
187
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if(isBrowser || isJsDom){
|
|
491
|
-
const base = document.getElementsByTagName('base')[0];
|
|
492
|
-
let basedir = null;
|
|
493
|
-
if(base && (basedir = base.getAttribute('href'))){
|
|
494
|
-
return basedir;
|
|
495
|
-
}else{
|
|
496
|
-
let path = window.location.pathname;
|
|
497
|
-
path = path.split('/');
|
|
498
|
-
path.pop(); // drop the top one
|
|
499
|
-
return path .join('/');
|
|
500
|
-
}
|
|
501
|
-
}else{
|
|
502
|
-
return process.cwd();
|
|
503
|
-
}
|
|
504
|
-
},
|
|
505
|
-
set(newValue) {
|
|
506
|
-
//do nothing
|
|
507
|
-
},
|
|
508
|
-
enumerable: true,
|
|
509
|
-
configurable: true,
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
Object.defineProperty(File, 'agent', {
|
|
513
|
-
get() {
|
|
514
|
-
if(isBrowser || isJsDom){
|
|
515
|
-
//var nVer = navigator.appVersion;
|
|
516
|
-
var nAgt = navigator.userAgent;
|
|
517
|
-
var browserName = navigator.appName;
|
|
518
|
-
var fullVersion = ''+parseFloat(navigator.appVersion);
|
|
519
|
-
var majorVersion = parseInt(navigator.appVersion,10);
|
|
520
|
-
var nameOffset,verOffset,ix;
|
|
521
|
-
|
|
522
|
-
// In Opera, the true version is after "Opera" or after "Version"
|
|
523
|
-
if ((verOffset=nAgt.indexOf('Opera'))!=-1) {
|
|
524
|
-
browserName = 'Opera';
|
|
525
|
-
fullVersion = nAgt.substring(verOffset+6);
|
|
526
|
-
if ((verOffset=nAgt.indexOf('Version'))!=-1)
|
|
527
|
-
fullVersion = nAgt.substring(verOffset+8);
|
|
528
|
-
}
|
|
529
|
-
// In MSIE, the true version is after 'MSIE' in userAgent
|
|
530
|
-
else if ((verOffset=nAgt.indexOf('MSIE'))!=-1) {
|
|
531
|
-
browserName = 'Microsoft Internet Explorer';
|
|
532
|
-
fullVersion = nAgt.substring(verOffset+5);
|
|
533
|
-
}
|
|
534
|
-
// In Chrome, the true version is after 'Chrome'
|
|
535
|
-
else if ((verOffset=nAgt.indexOf('Chrome'))!=-1) {
|
|
536
|
-
browserName = 'Chrome';
|
|
537
|
-
fullVersion = nAgt.substring(verOffset+7);
|
|
538
|
-
}
|
|
539
|
-
// In Safari, the true version is after 'Safari' or after 'Version'
|
|
540
|
-
else if ((verOffset=nAgt.indexOf('Safari'))!=-1) {
|
|
541
|
-
browserName = 'Safari';
|
|
542
|
-
fullVersion = nAgt.substring(verOffset+7);
|
|
543
|
-
if ((verOffset=nAgt.indexOf('Version'))!=-1)
|
|
544
|
-
fullVersion = nAgt.substring(verOffset+8);
|
|
545
|
-
}
|
|
546
|
-
// In Firefox, the true version is after 'Firefox'
|
|
547
|
-
else if ((verOffset=nAgt.indexOf('Firefox'))!=-1) {
|
|
548
|
-
browserName = 'Firefox';
|
|
549
|
-
fullVersion = nAgt.substring(verOffset+8);
|
|
550
|
-
}
|
|
551
|
-
// In most other browsers, 'name/version' is at the end of userAgent
|
|
552
|
-
else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) < (verOffset=nAgt.lastIndexOf('/')) ) {
|
|
553
|
-
browserName = nAgt.substring(nameOffset,verOffset);
|
|
554
|
-
fullVersion = nAgt.substring(verOffset+1);
|
|
555
|
-
if (browserName.toLowerCase()==browserName.toUpperCase()) {
|
|
556
|
-
browserName = navigator.appName;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
// trim the fullVersion string at semicolon/space if present
|
|
560
|
-
if ((ix=fullVersion.indexOf(';'))!=-1)
|
|
561
|
-
fullVersion=fullVersion.substring(0,ix);
|
|
562
|
-
if ((ix=fullVersion.indexOf(' '))!=-1)
|
|
563
|
-
fullVersion=fullVersion.substring(0,ix);
|
|
564
|
-
|
|
565
|
-
majorVersion = parseInt(''+fullVersion,10);
|
|
566
|
-
if (isNaN(majorVersion)) {
|
|
567
|
-
fullVersion = ''+parseFloat(navigator.appVersion);
|
|
568
|
-
majorVersion = parseInt(navigator.appVersion,10);
|
|
569
|
-
}
|
|
570
|
-
return { name: browserName.toLowerCase(), version: fullVersion, major: majorVersion };
|
|
571
|
-
}else{
|
|
572
|
-
return {};
|
|
188
|
+
let staticInstance = null;
|
|
189
|
+
export class Download{
|
|
190
|
+
constructor(){
|
|
191
|
+
if(staticInstance) return staticInstance;
|
|
192
|
+
this.promise = null;
|
|
193
|
+
staticInstance = this;
|
|
194
|
+
}
|
|
195
|
+
expect(){
|
|
196
|
+
this.promise = new Promise((resolve)=>{
|
|
197
|
+
this.resolve = resolve;
|
|
198
|
+
});
|
|
199
|
+
//if(isServer) this.flushPromise();
|
|
200
|
+
return this.promise;
|
|
201
|
+
}
|
|
202
|
+
flushPromise(result){
|
|
203
|
+
if(this.resolve){
|
|
204
|
+
this.resolve(result);
|
|
205
|
+
this.resolve = null;
|
|
573
206
|
}
|
|
574
|
-
|
|
575
|
-
set(newValue) {
|
|
576
|
-
//do nothing
|
|
577
|
-
},
|
|
578
|
-
enumerable: true,
|
|
579
|
-
configurable: true,
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
const directoryGet = (type)=>{
|
|
583
|
-
if(isBrowser || isJsDom){
|
|
584
|
-
return handleCanonicalPath('home', File.os, File.user);
|
|
585
|
-
}else{
|
|
586
|
-
return process.cwd();
|
|
207
|
+
this.promise = null;
|
|
587
208
|
}
|
|
588
|
-
|
|
209
|
+
async observe(download){
|
|
210
|
+
const text = await download.text();
|
|
211
|
+
this.flushPromise(text);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
589
214
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
215
|
+
(()=>{
|
|
216
|
+
globalThis.handleDownload = async (download)=>{
|
|
217
|
+
const root = Path.location('downloads');
|
|
218
|
+
const file = download.suggestedFilename();
|
|
219
|
+
const path = Path.join(root, file);
|
|
220
|
+
await download.saveAs(path);
|
|
221
|
+
};
|
|
222
|
+
if(variables.moka && variables.moka.bind){
|
|
223
|
+
//bind the input mechanics of File to moka
|
|
224
|
+
variables.moka.bind(bindInput());
|
|
225
|
+
}
|
|
226
|
+
})();
|