fix file system
parent
3a282494d6
commit
a47df8e6a8
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
import supported from './supported.mjs';
|
||||||
|
|
||||||
|
const implementation = !supported
|
||||||
|
? import('./legacy/directory-open.mjs')
|
||||||
|
: supported === 'chooseFileSystemEntries'
|
||||||
|
? import('./fs-access-legacy/directory-open.mjs')
|
||||||
|
: import('./fs-access/directory-open.mjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For opening directories, dynamically either loads the File System Access API
|
||||||
|
* module or the legacy method.
|
||||||
|
*/
|
||||||
|
export async function directoryOpen(...args) {
|
||||||
|
return (await implementation).default(...args);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
import supported from './supported.mjs';
|
||||||
|
|
||||||
|
const implementation = !supported
|
||||||
|
? import('./legacy/file-open.mjs')
|
||||||
|
: supported === 'chooseFileSystemEntries'
|
||||||
|
? import('./fs-access-legacy/file-open.mjs')
|
||||||
|
: import('./fs-access/file-open.mjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For opening files, dynamically either loads the File System Access API module
|
||||||
|
* or the legacy method.
|
||||||
|
*/
|
||||||
|
export async function fileOpen(...args) {
|
||||||
|
return (await implementation).default(...args);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
import supported from './supported.mjs';
|
||||||
|
|
||||||
|
const implementation = !supported
|
||||||
|
? import('./legacy/file-save.mjs')
|
||||||
|
: supported === 'chooseFileSystemEntries'
|
||||||
|
? import('./fs-access-legacy/file-save.mjs')
|
||||||
|
: import('./fs-access/file-save.mjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For saving files, dynamically either loads the File System Access API module
|
||||||
|
* or the legacy method.
|
||||||
|
*/
|
||||||
|
export async function fileSave(...args) {
|
||||||
|
return (await implementation).default(...args);
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFiles = async (dirHandle, recursive, path = dirHandle.name) => {
|
||||||
|
const dirs = [];
|
||||||
|
const files = [];
|
||||||
|
for await (const entry of dirHandle.getEntries()) {
|
||||||
|
const nestedPath = `${path}/${entry.name}`;
|
||||||
|
if (entry.isFile) {
|
||||||
|
files.push(
|
||||||
|
entry.getFile().then((file) =>
|
||||||
|
Object.defineProperty(file, 'webkitRelativePath', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => nestedPath,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (entry.isDirectory && recursive) {
|
||||||
|
dirs.push(getFiles(entry, recursive, nestedPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a directory from disk using the (legacy) File System Access API.
|
||||||
|
* @type { typeof import("../../index").directoryOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
options.recursive = options.recursive || false;
|
||||||
|
const handle = await window.chooseFileSystemEntries({
|
||||||
|
type: 'open-directory',
|
||||||
|
});
|
||||||
|
return getFiles(handle, options.recursive);
|
||||||
|
};
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFileWithHandle = async (handle) => {
|
||||||
|
const file = await handle.getFile();
|
||||||
|
file.handle = handle;
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file from disk using the (legacy) File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
const handleOrHandles = await window.chooseFileSystemEntries({
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
mimeTypes: options.mimeTypes || ['*/*'],
|
||||||
|
extensions: options.extensions || [''],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
multiple: options.multiple || false,
|
||||||
|
});
|
||||||
|
if (options.multiple) {
|
||||||
|
return Promise.all(handleOrHandles.map(getFileWithHandle));
|
||||||
|
}
|
||||||
|
return getFileWithHandle(handleOrHandles);
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a file to disk using the (legacy) File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileSave }
|
||||||
|
*/
|
||||||
|
export default async (blob, options = {}, handle = null) => {
|
||||||
|
options.fileName = options.fileName || 'Untitled';
|
||||||
|
handle =
|
||||||
|
handle ||
|
||||||
|
(await window.chooseFileSystemEntries({
|
||||||
|
type: 'save-file',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
mimeTypes: [blob.type],
|
||||||
|
extensions: options.extensions || [''],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
await writable.write(blob);
|
||||||
|
await writable.close();
|
||||||
|
return handle;
|
||||||
|
};
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFiles = async (dirHandle, recursive, path = dirHandle.name) => {
|
||||||
|
const dirs = [];
|
||||||
|
const files = [];
|
||||||
|
for await (const entry of dirHandle.values()) {
|
||||||
|
const nestedPath = `${path}/${entry.name}`;
|
||||||
|
if (entry.kind === 'file') {
|
||||||
|
files.push(
|
||||||
|
entry.getFile().then((file) =>
|
||||||
|
Object.defineProperty(file, 'webkitRelativePath', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => nestedPath,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (entry.kind === 'directory' && recursive) {
|
||||||
|
dirs.push(getFiles(entry, recursive, nestedPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a directory from disk using the File System Access API.
|
||||||
|
* @type { typeof import("../../index").directoryOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
options.recursive = options.recursive || false;
|
||||||
|
const handle = await window.showDirectoryPicker();
|
||||||
|
return getFiles(handle, options.recursive);
|
||||||
|
};
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFileWithHandle = async (handle) => {
|
||||||
|
const file = await handle.getFile();
|
||||||
|
file.handle = handle;
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file from disk using the File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
const accept = {};
|
||||||
|
if (options.mimeTypes) {
|
||||||
|
options.mimeTypes.map((mimeType) => {
|
||||||
|
accept[mimeType] = options.extensions || [];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
accept['*/*'] = options.extensions || [];
|
||||||
|
}
|
||||||
|
const handleOrHandles = await window.showOpenFilePicker({
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
accept: accept,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
multiple: options.multiple || false,
|
||||||
|
});
|
||||||
|
const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
|
||||||
|
if (options.multiple) {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
return files[0];
|
||||||
|
};
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a file to disk using the File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileSave }
|
||||||
|
*/
|
||||||
|
export default async (
|
||||||
|
blob,
|
||||||
|
options = {},
|
||||||
|
existingHandle = null,
|
||||||
|
throwIfExistingHandleNotGood = false
|
||||||
|
) => {
|
||||||
|
options.fileName = options.fileName || 'Untitled';
|
||||||
|
const accept = {};
|
||||||
|
if (options.mimeTypes) {
|
||||||
|
options.mimeTypes.push(blob.type);
|
||||||
|
options.mimeTypes.map((mimeType) => {
|
||||||
|
accept[mimeType] = options.extensions || [];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
accept[blob.type] = options.extensions || [];
|
||||||
|
}
|
||||||
|
if (existingHandle) {
|
||||||
|
try {
|
||||||
|
// Check if the file still exists.
|
||||||
|
await existingHandle.getFile();
|
||||||
|
} catch (err) {
|
||||||
|
existingHandle = null;
|
||||||
|
if (throwIfExistingHandleNotGood) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handle =
|
||||||
|
existingHandle ||
|
||||||
|
(await window.showSaveFilePicker({
|
||||||
|
suggestedName: options.fileName,
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
accept: accept,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
await writable.write(blob);
|
||||||
|
await writable.close();
|
||||||
|
return handle;
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module browser-fs-access
|
||||||
|
*/
|
||||||
|
export { fileOpen } from './file-open.mjs';
|
||||||
|
export { directoryOpen } from './directory-open.mjs';
|
||||||
|
export { fileSave } from './file-save.mjs';
|
||||||
|
export { default as supported } from './supported.mjs';
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a directory from disk using the legacy
|
||||||
|
* `<input type="file" webkitdirectory>` method.
|
||||||
|
* @type { typeof import("../../index").directoryOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
options.recursive = options.recursive || false;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
input.webkitdirectory = true;
|
||||||
|
|
||||||
|
// ToDo: Remove this workaround once
|
||||||
|
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||||
|
const rejectOnPageInteraction = () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
reject(new DOMException('The user aborted a request.', 'AbortError'));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
let files = Array.from(input.files);
|
||||||
|
if (!options.recursive) {
|
||||||
|
files = files.filter((file) => {
|
||||||
|
return file.webkitRelativePath.split('/').length === 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve(files);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file from disk using the legacy `<input type="file">` method.
|
||||||
|
* @type { typeof import("../../index").fileOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
const accept = [
|
||||||
|
...(options.mimeTypes ? options.mimeTypes : []),
|
||||||
|
options.extensions ? options.extensions : [],
|
||||||
|
].join();
|
||||||
|
input.multiple = options.multiple || false;
|
||||||
|
// Empty string allows everything.
|
||||||
|
input.accept = accept || '';
|
||||||
|
|
||||||
|
// ToDo: Remove this workaround once
|
||||||
|
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||||
|
const rejectOnPageInteraction = () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
reject(new DOMException('The user aborted a request.', 'AbortError'));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
resolve(input.multiple ? input.files : input.files[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a file to disk using the legacy `<a download>` method.
|
||||||
|
* @type { typeof import("../../index").fileSave }
|
||||||
|
*/
|
||||||
|
export default async (blob, options = {}) => {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.download = options.fileName || 'Untitled';
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.addEventListener('click', () => {
|
||||||
|
// `setTimeout()` due to
|
||||||
|
// https://github.com/LLK/scratch-gui/issues/1783#issuecomment-426286393
|
||||||
|
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
|
||||||
|
});
|
||||||
|
a.click();
|
||||||
|
};
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the File System Access API is supported and usable in the
|
||||||
|
* current context (for example cross-origin iframes).
|
||||||
|
* @returns {boolean} Returns `true` if the File System Access API is supported and usable, else returns `false`.
|
||||||
|
*/
|
||||||
|
const supported = (() => {
|
||||||
|
// ToDo: Remove this check once Permissions Policy integration
|
||||||
|
// has happened, tracked in
|
||||||
|
// https://github.com/WICG/file-system-access/issues/245.
|
||||||
|
if ('top' in self && self !== top) {
|
||||||
|
try {
|
||||||
|
// This will succeed on same-origin iframes,
|
||||||
|
// but fail on cross-origin iframes.
|
||||||
|
top.location + '';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if ('chooseFileSystemEntries' in self) {
|
||||||
|
return 'chooseFileSystemEntries';
|
||||||
|
} else if ('showOpenFilePicker' in self) {
|
||||||
|
return 'showOpenFilePicker';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default supported;
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
import supported from './supported.mjs';
|
||||||
|
|
||||||
|
const implementation = !supported
|
||||||
|
? import('./legacy/directory-open.mjs')
|
||||||
|
: supported === 'chooseFileSystemEntries'
|
||||||
|
? import('./fs-access-legacy/directory-open.mjs')
|
||||||
|
: import('./fs-access/directory-open.mjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For opening directories, dynamically either loads the File System Access API
|
||||||
|
* module or the legacy method.
|
||||||
|
*/
|
||||||
|
export async function directoryOpen(...args) {
|
||||||
|
return (await implementation).default(...args);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
import supported from './supported.mjs';
|
||||||
|
|
||||||
|
const implementation = !supported
|
||||||
|
? import('./legacy/file-open.mjs')
|
||||||
|
: supported === 'chooseFileSystemEntries'
|
||||||
|
? import('./fs-access-legacy/file-open.mjs')
|
||||||
|
: import('./fs-access/file-open.mjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For opening files, dynamically either loads the File System Access API module
|
||||||
|
* or the legacy method.
|
||||||
|
*/
|
||||||
|
export async function fileOpen(...args) {
|
||||||
|
return (await implementation).default(...args);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
import supported from './supported.mjs';
|
||||||
|
|
||||||
|
const implementation = !supported
|
||||||
|
? import('./legacy/file-save.mjs')
|
||||||
|
: supported === 'chooseFileSystemEntries'
|
||||||
|
? import('./fs-access-legacy/file-save.mjs')
|
||||||
|
: import('./fs-access/file-save.mjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For saving files, dynamically either loads the File System Access API module
|
||||||
|
* or the legacy method.
|
||||||
|
*/
|
||||||
|
export async function fileSave(...args) {
|
||||||
|
return (await implementation).default(...args);
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFiles = async (dirHandle, recursive, path = dirHandle.name) => {
|
||||||
|
const dirs = [];
|
||||||
|
const files = [];
|
||||||
|
for await (const entry of dirHandle.getEntries()) {
|
||||||
|
const nestedPath = `${path}/${entry.name}`;
|
||||||
|
if (entry.isFile) {
|
||||||
|
files.push(
|
||||||
|
entry.getFile().then((file) =>
|
||||||
|
Object.defineProperty(file, 'webkitRelativePath', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => nestedPath,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (entry.isDirectory && recursive) {
|
||||||
|
dirs.push(getFiles(entry, recursive, nestedPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a directory from disk using the (legacy) File System Access API.
|
||||||
|
* @type { typeof import("../../index").directoryOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
options.recursive = options.recursive || false;
|
||||||
|
const handle = await window.chooseFileSystemEntries({
|
||||||
|
type: 'open-directory',
|
||||||
|
});
|
||||||
|
return getFiles(handle, options.recursive);
|
||||||
|
};
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFileWithHandle = async (handle) => {
|
||||||
|
const file = await handle.getFile();
|
||||||
|
file.handle = handle;
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file from disk using the (legacy) File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
const handleOrHandles = await window.chooseFileSystemEntries({
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
mimeTypes: options.mimeTypes || ['*/*'],
|
||||||
|
extensions: options.extensions || [''],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
multiple: options.multiple || false,
|
||||||
|
});
|
||||||
|
if (options.multiple) {
|
||||||
|
return Promise.all(handleOrHandles.map(getFileWithHandle));
|
||||||
|
}
|
||||||
|
return getFileWithHandle(handleOrHandles);
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a file to disk using the (legacy) File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileSave }
|
||||||
|
*/
|
||||||
|
export default async (blob, options = {}, handle = null) => {
|
||||||
|
options.fileName = options.fileName || 'Untitled';
|
||||||
|
handle =
|
||||||
|
handle ||
|
||||||
|
(await window.chooseFileSystemEntries({
|
||||||
|
type: 'save-file',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
mimeTypes: [blob.type],
|
||||||
|
extensions: options.extensions || [''],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
await writable.write(blob);
|
||||||
|
await writable.close();
|
||||||
|
return handle;
|
||||||
|
};
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFiles = async (dirHandle, recursive, path = dirHandle.name) => {
|
||||||
|
const dirs = [];
|
||||||
|
const files = [];
|
||||||
|
for await (const entry of dirHandle.values()) {
|
||||||
|
const nestedPath = `${path}/${entry.name}`;
|
||||||
|
if (entry.kind === 'file') {
|
||||||
|
files.push(
|
||||||
|
entry.getFile().then((file) =>
|
||||||
|
Object.defineProperty(file, 'webkitRelativePath', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => nestedPath,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (entry.kind === 'directory' && recursive) {
|
||||||
|
dirs.push(getFiles(entry, recursive, nestedPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a directory from disk using the File System Access API.
|
||||||
|
* @type { typeof import("../../index").directoryOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
options.recursive = options.recursive || false;
|
||||||
|
const handle = await window.showDirectoryPicker();
|
||||||
|
return getFiles(handle, options.recursive);
|
||||||
|
};
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
const getFileWithHandle = async (handle) => {
|
||||||
|
const file = await handle.getFile();
|
||||||
|
file.handle = handle;
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file from disk using the File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
const accept = {};
|
||||||
|
if (options.mimeTypes) {
|
||||||
|
options.mimeTypes.map((mimeType) => {
|
||||||
|
accept[mimeType] = options.extensions || [];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
accept['*/*'] = options.extensions || [];
|
||||||
|
}
|
||||||
|
const handleOrHandles = await window.showOpenFilePicker({
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
accept: accept,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
multiple: options.multiple || false,
|
||||||
|
});
|
||||||
|
const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
|
||||||
|
if (options.multiple) {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
return files[0];
|
||||||
|
};
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a file to disk using the File System Access API.
|
||||||
|
* @type { typeof import("../../index").fileSave }
|
||||||
|
*/
|
||||||
|
export default async (
|
||||||
|
blob,
|
||||||
|
options = {},
|
||||||
|
existingHandle = null,
|
||||||
|
throwIfExistingHandleNotGood = false
|
||||||
|
) => {
|
||||||
|
options.fileName = options.fileName || 'Untitled';
|
||||||
|
const accept = {};
|
||||||
|
if (options.mimeTypes) {
|
||||||
|
options.mimeTypes.push(blob.type);
|
||||||
|
options.mimeTypes.map((mimeType) => {
|
||||||
|
accept[mimeType] = options.extensions || [];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
accept[blob.type] = options.extensions || [];
|
||||||
|
}
|
||||||
|
if (existingHandle) {
|
||||||
|
try {
|
||||||
|
// Check if the file still exists.
|
||||||
|
await existingHandle.getFile();
|
||||||
|
} catch (err) {
|
||||||
|
existingHandle = null;
|
||||||
|
if (throwIfExistingHandleNotGood) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handle =
|
||||||
|
existingHandle ||
|
||||||
|
(await window.showSaveFilePicker({
|
||||||
|
suggestedName: options.fileName,
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: options.description || '',
|
||||||
|
accept: accept,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
await writable.write(blob);
|
||||||
|
await writable.close();
|
||||||
|
return handle;
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module browser-fs-access
|
||||||
|
*/
|
||||||
|
export { fileOpen } from './file-open.mjs';
|
||||||
|
export { directoryOpen } from './directory-open.mjs';
|
||||||
|
export { fileSave } from './file-save.mjs';
|
||||||
|
export { default as supported } from './supported.mjs';
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a directory from disk using the legacy
|
||||||
|
* `<input type="file" webkitdirectory>` method.
|
||||||
|
* @type { typeof import("../../index").directoryOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
options.recursive = options.recursive || false;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
input.webkitdirectory = true;
|
||||||
|
|
||||||
|
// ToDo: Remove this workaround once
|
||||||
|
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||||
|
const rejectOnPageInteraction = () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
reject(new DOMException('The user aborted a request.', 'AbortError'));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
let files = Array.from(input.files);
|
||||||
|
if (!options.recursive) {
|
||||||
|
files = files.filter((file) => {
|
||||||
|
return file.webkitRelativePath.split('/').length === 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve(files);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file from disk using the legacy `<input type="file">` method.
|
||||||
|
* @type { typeof import("../../index").fileOpen }
|
||||||
|
*/
|
||||||
|
export default async (options = {}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
const accept = [
|
||||||
|
...(options.mimeTypes ? options.mimeTypes : []),
|
||||||
|
options.extensions ? options.extensions : [],
|
||||||
|
].join();
|
||||||
|
input.multiple = options.multiple || false;
|
||||||
|
// Empty string allows everything.
|
||||||
|
input.accept = accept || '';
|
||||||
|
|
||||||
|
// ToDo: Remove this workaround once
|
||||||
|
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||||
|
const rejectOnPageInteraction = () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
reject(new DOMException('The user aborted a request.', 'AbortError'));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.addEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
window.removeEventListener('pointermove', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('pointerdown', rejectOnPageInteraction);
|
||||||
|
window.removeEventListener('keydown', rejectOnPageInteraction);
|
||||||
|
resolve(input.multiple ? input.files : input.files[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a file to disk using the legacy `<a download>` method.
|
||||||
|
* @type { typeof import("../../index").fileSave }
|
||||||
|
*/
|
||||||
|
export default async (blob, options = {}) => {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.download = options.fileName || 'Untitled';
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.addEventListener('click', () => {
|
||||||
|
// `setTimeout()` due to
|
||||||
|
// https://github.com/LLK/scratch-gui/issues/1783#issuecomment-426286393
|
||||||
|
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
|
||||||
|
});
|
||||||
|
a.click();
|
||||||
|
};
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the File System Access API is supported and usable in the
|
||||||
|
* current context (for example cross-origin iframes).
|
||||||
|
* @returns {boolean} Returns `true` if the File System Access API is supported and usable, else returns `false`.
|
||||||
|
*/
|
||||||
|
const supported = (() => {
|
||||||
|
// ToDo: Remove this check once Permissions Policy integration
|
||||||
|
// has happened, tracked in
|
||||||
|
// https://github.com/WICG/file-system-access/issues/245.
|
||||||
|
if ('top' in self && self !== top) {
|
||||||
|
try {
|
||||||
|
// This will succeed on same-origin iframes,
|
||||||
|
// but fail on cross-origin iframes.
|
||||||
|
top.location + '';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if ('chooseFileSystemEntries' in self) {
|
||||||
|
return 'chooseFileSystemEntries';
|
||||||
|
} else if ('showOpenFilePicker' in self) {
|
||||||
|
return 'showOpenFilePicker';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default supported;
|
|
@ -8,6 +8,7 @@
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"@tailwindcss/jit": "^0.1.18",
|
"@tailwindcss/jit": "^0.1.18",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
|
"browser-fs-access": "^0.16.4",
|
||||||
"css-loader": "^5.1.3",
|
"css-loader": "^5.1.3",
|
||||||
"gh-pages": "^3.1.0",
|
"gh-pages": "^3.1.0",
|
||||||
"immutability-helper": "^3.1.1",
|
"immutability-helper": "^3.1.1",
|
||||||
|
@ -1397,6 +1398,12 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/browser-fs-access": {
|
||||||
|
"version": "0.16.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.16.4.tgz",
|
||||||
|
"integrity": "sha512-c1A9Y3pHJTKPYFjwL5SXX3MZ0BQcK7He7l0csclr80SEADIFOUHUM5oJBdg49XUdlLmIFiWiE3tbr/5KcD5TsQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.16.3",
|
"version": "4.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
|
||||||
|
@ -9611,6 +9618,12 @@
|
||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"browser-fs-access": {
|
||||||
|
"version": "0.16.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.16.4.tgz",
|
||||||
|
"integrity": "sha512-c1A9Y3pHJTKPYFjwL5SXX3MZ0BQcK7He7l0csclr80SEADIFOUHUM5oJBdg49XUdlLmIFiWiE3tbr/5KcD5TsQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"version": "4.16.3",
|
"version": "4.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"@tailwindcss/jit": "^0.1.18",
|
"@tailwindcss/jit": "^0.1.18",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
|
"browser-fs-access": "^0.16.4",
|
||||||
"css-loader": "^5.1.3",
|
"css-loader": "^5.1.3",
|
||||||
"gh-pages": "^3.1.0",
|
"gh-pages": "^3.1.0",
|
||||||
"immutability-helper": "^3.1.1",
|
"immutability-helper": "^3.1.1",
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
|
||||||
|
|
||||||
|
// import {
|
||||||
|
// fileOpen,
|
||||||
|
// fileSave,
|
||||||
|
// } from '../../extlib/fs/index';
|
||||||
|
|
||||||
|
import {
|
||||||
|
fileOpen,
|
||||||
|
fileSave,
|
||||||
|
} from 'browser-fs-access';
|
||||||
|
|
||||||
// https://web.dev/file-system-access/
|
// https://web.dev/file-system-access/
|
||||||
|
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
|
@ -5,23 +17,15 @@ link.style.display = 'none';
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
|
|
||||||
function saveLegacy(blob, filename) {
|
function saveLegacy(blob, filename) {
|
||||||
|
|
||||||
link.href = URL.createObjectURL(blob);
|
link.href = URL.createObjectURL(blob);
|
||||||
link.download = filename;
|
link.download = filename;
|
||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var tzoffset = (new Date()).getTimezoneOffset() * 60000;
|
var tzoffset = (new Date()).getTimezoneOffset() * 60000;
|
||||||
|
|
||||||
|
|
||||||
export function STLExport(filename) {
|
export function STLExport(filename) {
|
||||||
|
|
||||||
const result = STLexp.parse(sc.selected[0], { binary: true });
|
const result = STLexp.parse(sc.selected[0], { binary: true });
|
||||||
|
|
||||||
const time = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -5).replace(/:/g, '-');
|
const time = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -5).replace(/:/g, '-');
|
||||||
|
|
||||||
saveLegacy(new Blob([result], { type: 'model/stl' }), `${filename}_${time}.stl`);
|
saveLegacy(new Blob([result], { type: 'model/stl' }), `${filename}_${time}.stl`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,47 +36,24 @@ export async function saveFile(fileHandle, file, dispatch) {
|
||||||
return await saveFileAs(file, dispatch);
|
return await saveFileAs(file, dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
const writable = await fileHandle.createWritable();
|
await fileSave(new Blob([file], { type: 'application/json' }), undefined, fileHandle, true)
|
||||||
await writable.write(file);
|
|
||||||
await writable.close();
|
|
||||||
|
|
||||||
dispatch({ type: 'set-modified', status: false })
|
dispatch({ type: 'set-modified', status: false })
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
const msg = 'Unable to save file';
|
const msg = 'Unable to save file';
|
||||||
console.error(msg, ex);
|
console.error(msg, ex);
|
||||||
console.log('heeeeeeeeerree')
|
|
||||||
alert(msg);
|
alert(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function saveFileAs(file, dispatch) {
|
export async function saveFileAs(file, dispatch) {
|
||||||
let fileHandle;
|
|
||||||
try {
|
|
||||||
|
|
||||||
const opts = {
|
|
||||||
types: [{
|
|
||||||
description: 'Text file',
|
|
||||||
accept: { 'application/json': ['.json'] },
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
fileHandle = await showSaveFilePicker(opts)
|
|
||||||
|
|
||||||
|
|
||||||
} catch (ex) {
|
|
||||||
if (ex.name === 'AbortError') {
|
|
||||||
console.log('aborted')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msg = 'An error occured trying to open the file.';
|
|
||||||
console.error(msg, ex);
|
|
||||||
alert(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const writable = await fileHandle.createWritable();
|
const fileHandle = await fileSave(new Blob([file], { type: 'application/json' }), {
|
||||||
await writable.write(file);
|
fileName: 'untitled.json',
|
||||||
await writable.close()
|
extensions: ['.json'],
|
||||||
|
})
|
||||||
|
|
||||||
dispatch({ type: 'set-file-handle', fileHandle, modified: false })
|
dispatch({ type: 'set-file-handle', fileHandle, modified: false })
|
||||||
|
|
||||||
|
@ -88,12 +69,19 @@ export async function saveFileAs(file, dispatch) {
|
||||||
|
|
||||||
|
|
||||||
export async function openFile(dispatch) {
|
export async function openFile(dispatch) {
|
||||||
let fileHandle
|
let file
|
||||||
|
|
||||||
// If a fileHandle is provided, verify we have permission to read/write it,
|
|
||||||
// otherwise, show the file open prompt and allow the user to select the file.
|
|
||||||
try {
|
try {
|
||||||
fileHandle = await getFileHandle();
|
|
||||||
|
const options = {
|
||||||
|
mimeTypes: ['application/json'],
|
||||||
|
extensions: ['.json'],
|
||||||
|
multiple: false,
|
||||||
|
description: 'Part files',
|
||||||
|
};
|
||||||
|
|
||||||
|
file = await fileOpen(options);
|
||||||
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.name === 'AbortError') {
|
if (ex.name === 'AbortError') {
|
||||||
return;
|
return;
|
||||||
|
@ -103,17 +91,11 @@ export async function openFile(dispatch) {
|
||||||
alert(msg);
|
alert(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileHandle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const file = await fileHandle.getFile();
|
|
||||||
const text = await file.text();;
|
const text = await file.text();;
|
||||||
|
|
||||||
dispatch({ type: 'restore-state', state: sc.loadState(text) })
|
dispatch({ type: 'restore-state', state: sc.loadState(text) })
|
||||||
dispatch({ type: 'set-file-handle', fileHandle })
|
dispatch({ type: 'set-file-handle', fileHandle:file.handle })
|
||||||
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
const msg = `An error occured reading ${fileHandle}`;
|
const msg = `An error occured reading ${fileHandle}`;
|
||||||
|
|
Loading…
Reference in New Issue