mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-19 19:16:29 +08:00
init: the first public commit for AFFiNE
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.js';
|
||||
|
||||
const implementation = !supported
|
||||
? import('./legacy/directory-open.js')
|
||||
: import('./fs-access/directory-open.js');
|
||||
|
||||
/**
|
||||
* 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,30 @@
|
||||
/**
|
||||
* 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.js';
|
||||
|
||||
const implementation = !supported
|
||||
? import('./legacy/file-open.js')
|
||||
: import('./fs-access/file-open.js');
|
||||
|
||||
/**
|
||||
* 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,30 @@
|
||||
/**
|
||||
* 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.js';
|
||||
|
||||
const implementation = !supported
|
||||
? import('./legacy/file-save.js')
|
||||
: import('./fs-access/file-save.js');
|
||||
|
||||
/**
|
||||
* 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,68 @@
|
||||
/**
|
||||
* 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,
|
||||
skipDirectory
|
||||
) => {
|
||||
const dirs = [];
|
||||
const files = [];
|
||||
for (const entry of dirHandle.values()) {
|
||||
const nestedPath = `${path}/${entry.name}`;
|
||||
if (entry.kind === 'file') {
|
||||
files.push(
|
||||
await entry.getFile().then(file => {
|
||||
file.directoryHandle = dirHandle;
|
||||
return Object.defineProperty(file, 'webkitRelativePath', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: () => nestedPath,
|
||||
});
|
||||
})
|
||||
);
|
||||
} else if (
|
||||
entry.kind === 'directory' &&
|
||||
recursive &&
|
||||
(!skipDirectory || !skipDirectory(entry))
|
||||
) {
|
||||
dirs.push(
|
||||
await getFiles(entry, recursive, nestedPath, skipDirectory)
|
||||
);
|
||||
}
|
||||
}
|
||||
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({
|
||||
id: options.id,
|
||||
startIn: options.startIn,
|
||||
});
|
||||
return getFiles(
|
||||
handle,
|
||||
options.recursive,
|
||||
undefined,
|
||||
options.skipDirectory
|
||||
);
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
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 = [{}]) => {
|
||||
if (!Array.isArray(options)) {
|
||||
options = [options];
|
||||
}
|
||||
const types = [];
|
||||
options.forEach((option, i) => {
|
||||
types[i] = {
|
||||
description: option.description || '',
|
||||
accept: {},
|
||||
};
|
||||
if (option.mimeTypes) {
|
||||
option.mimeTypes.map(mimeType => {
|
||||
types[i].accept[mimeType] = option.extensions || [];
|
||||
});
|
||||
} else {
|
||||
types[i].accept['*/*'] = option.extensions || [];
|
||||
}
|
||||
});
|
||||
const handleOrHandles = await window.showOpenFilePicker({
|
||||
id: options[0].id,
|
||||
startIn: options[0].startIn,
|
||||
types,
|
||||
multiple: options[0].multiple || false,
|
||||
excludeAcceptAllOption: options[0].excludeAcceptAllOption || false,
|
||||
});
|
||||
const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
|
||||
if (options[0].multiple) {
|
||||
return files;
|
||||
}
|
||||
return files[0];
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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 (
|
||||
blobOrResponse,
|
||||
options = [{}],
|
||||
existingHandle = null,
|
||||
throwIfExistingHandleNotGood = false
|
||||
) => {
|
||||
if (!Array.isArray(options)) {
|
||||
options = [options];
|
||||
}
|
||||
options[0].fileName = options[0].fileName || 'Untitled';
|
||||
const types = [];
|
||||
options.forEach((option, i) => {
|
||||
types[i] = {
|
||||
description: option.description || '',
|
||||
accept: {},
|
||||
};
|
||||
if (option.mimeTypes) {
|
||||
if (i === 0) {
|
||||
if (blobOrResponse.type) {
|
||||
option.mimeTypes.push(blobOrResponse.type);
|
||||
} else if (
|
||||
blobOrResponse.headers &&
|
||||
blobOrResponse.headers.get('content-type')
|
||||
) {
|
||||
option.mimeTypes.push(
|
||||
blobOrResponse.headers.get('content-type')
|
||||
);
|
||||
}
|
||||
}
|
||||
option.mimeTypes.map(mimeType => {
|
||||
types[i].accept[mimeType] = option.extensions || [];
|
||||
});
|
||||
} else if (blobOrResponse.type) {
|
||||
types[i].accept[blobOrResponse.type] = option.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[0].fileName,
|
||||
id: options[0].id,
|
||||
startIn: options[0].startIn,
|
||||
types,
|
||||
excludeAcceptAllOption: options[0].excludeAcceptAllOption || false,
|
||||
}));
|
||||
const writable = await handle.createWritable();
|
||||
// Use streaming on the `Blob` if the browser supports it.
|
||||
if ('stream' in blobOrResponse) {
|
||||
const stream = blobOrResponse.stream();
|
||||
await stream.pipeTo(writable);
|
||||
return handle;
|
||||
// Handle passed `ReadableStream`.
|
||||
} else if ('body' in blobOrResponse) {
|
||||
await blobOrResponse.body.pipeTo(writable);
|
||||
return handle;
|
||||
}
|
||||
// Default case of `Blob` passed and `Blob.stream()` not supported.
|
||||
await writable.write(blobOrResponse);
|
||||
await writable.close();
|
||||
return handle;
|
||||
};
|
||||
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Properties shared by all `options` provided to file save and open operations
|
||||
*/
|
||||
export interface CoreFileOptions {
|
||||
/** Acceptable file extensions. Defaults to [""]. */
|
||||
extensions?: string[];
|
||||
/** Suggested file description. Defaults to "". */
|
||||
description?: string;
|
||||
/** Acceptable MIME types. [] */
|
||||
mimeTypes?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties shared by the _first_ `options` object provided to file save and
|
||||
* open operations (any additional options objects provided to those operations
|
||||
* cannot have these properties)
|
||||
*/
|
||||
export interface FirstCoreFileOptions extends CoreFileOptions {
|
||||
startIn?: WellKnownDirectory | FileSystemHandle;
|
||||
/** By specifying an ID, the user agent can remember different directories for different IDs. */
|
||||
id?: string;
|
||||
excludeAcceptAllOption?: boolean | false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first `options` object passed to file save operations can also specify
|
||||
* a filename
|
||||
*/
|
||||
export interface FirstFileSaveOptions extends FirstCoreFileOptions {
|
||||
/** Suggested file name. Defaults to "Untitled". */
|
||||
fileName?: string;
|
||||
/**
|
||||
* Configurable cleanup and `Promise` rejector usable with legacy API for
|
||||
* determining when (and reacting if) a user cancels the operation. The
|
||||
* method will be passed a reference to the internal `rejectionHandler` that
|
||||
* can, e.g., be attached to/removed from the window or called after a
|
||||
* timeout. The method should return a function that will be called when
|
||||
* either the user chooses to open a file or the `rejectionHandler` is
|
||||
* called. In the latter case, the returned function will also be passed a
|
||||
* reference to the `reject` callback for the `Promise` returned by
|
||||
* `fileOpen`, so that developers may reject the `Promise` when desired at
|
||||
* that time.
|
||||
* Example rejector:
|
||||
*
|
||||
* const file = await fileOpen({
|
||||
* legacySetup: (rejectionHandler) => {
|
||||
* const timeoutId = setTimeout(rejectionHandler, 10_000);
|
||||
* return (reject) => {
|
||||
* clearTimeout(timeoutId);
|
||||
* if (reject) {
|
||||
* reject('My error message here.');
|
||||
* }
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* ToDo: Remove this workaround once
|
||||
* https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||
*/
|
||||
legacySetup?: (
|
||||
resolve: (value: Blob) => void,
|
||||
rejectionHandler: () => void,
|
||||
anchor: HTMLAnchorElement
|
||||
) => (reject?: (reason?: any) => void) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first `options` object passed to file open operations can specify
|
||||
* whether multiple files can be selected (the return type of the operation
|
||||
* will be updated appropriately) and a way of handling cleanup and rejection
|
||||
* for legacy open operations.
|
||||
*/
|
||||
export interface FirstFileOpenOptions<M extends boolean | undefined>
|
||||
extends FirstCoreFileOptions {
|
||||
/** Allow multiple files to be selected. Defaults to false. */
|
||||
multiple?: M;
|
||||
/**
|
||||
* Configurable cleanup and `Promise` rejector usable with legacy API for
|
||||
* determining when (and reacting if) a user cancels the operation. The
|
||||
* method will be passed a reference to the internal `rejectionHandler` that
|
||||
* can, e.g., be attached to/removed from the window or called after a
|
||||
* timeout. The method should return a function that will be called when
|
||||
* either the user chooses to open a file or the `rejectionHandler` is
|
||||
* called. In the latter case, the returned function will also be passed a
|
||||
* reference to the `reject` callback for the `Promise` returned by
|
||||
* `fileOpen`, so that developers may reject the `Promise` when desired at
|
||||
* that time.
|
||||
* Example rejector:
|
||||
*
|
||||
* const file = await fileOpen({
|
||||
* legacySetup: (rejectionHandler) => {
|
||||
* const timeoutId = setTimeout(rejectionHandler, 10_000);
|
||||
* return (reject) => {
|
||||
* clearTimeout(timeoutId);
|
||||
* if (reject) {
|
||||
* reject('My error message here.');
|
||||
* }
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* ToDo: Remove this workaround once
|
||||
* https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||
*/
|
||||
legacySetup?: (
|
||||
resolve: (
|
||||
value: M extends false | undefined
|
||||
? FileWithHandle
|
||||
: FileWithHandle[]
|
||||
) => void,
|
||||
rejectionHandler: () => void,
|
||||
input: HTMLInputElement
|
||||
) => (reject?: (reason?: any) => void) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens file(s) from disk.
|
||||
*/
|
||||
export function fileOpen<M extends boolean | undefined = false>(
|
||||
options?:
|
||||
| [FirstFileOpenOptions<M>, ...CoreFileOptions[]]
|
||||
| FirstFileOpenOptions<M>
|
||||
): M extends false | undefined
|
||||
? Promise<FileWithHandle>
|
||||
: Promise<FileWithHandle[]>;
|
||||
|
||||
export type WellKnownDirectory =
|
||||
| 'desktop'
|
||||
| 'documents'
|
||||
| 'downloads'
|
||||
| 'music'
|
||||
| 'pictures'
|
||||
| 'videos';
|
||||
|
||||
/**
|
||||
* Saves a file to disk.
|
||||
* @returns Optional file handle to save in place.
|
||||
*/
|
||||
export function fileSave(
|
||||
/** To-be-saved `Blob` or `Response` */
|
||||
blobOrResponse: Blob | Response,
|
||||
options?:
|
||||
| [FirstFileSaveOptions, ...CoreFileOptions[]]
|
||||
| FirstFileSaveOptions,
|
||||
/**
|
||||
* A potentially existing file handle for a file to save to. Defaults to
|
||||
* null.
|
||||
*/
|
||||
existingHandle?: FileSystemHandle | null,
|
||||
/**
|
||||
* Determines whether to throw (rather than open a new file save dialog)
|
||||
* when existingHandle is no longer good. Defaults to false.
|
||||
*/
|
||||
throwIfExistingHandleNotGood?: boolean | false
|
||||
): Promise<FileSystemHandle | null>;
|
||||
|
||||
/**
|
||||
* Opens a directory from disk using the File System Access API.
|
||||
* @returns Contained files.
|
||||
*/
|
||||
export function directoryOpen(options?: {
|
||||
/** Whether to recursively get subdirectories. */
|
||||
recursive: boolean;
|
||||
/** Suggested directory in which the file picker opens. */
|
||||
startIn?: WellKnownDirectory | FileSystemHandle;
|
||||
/** By specifying an ID, the user agent can remember different directories for different IDs. */
|
||||
id?: string;
|
||||
/** Callback to determine whether a directory should be entered, return `true` to skip. */
|
||||
skipDirectory?: (
|
||||
fileSystemDirectoryEntry: FileSystemDirectoryEntry
|
||||
) => boolean;
|
||||
/**
|
||||
* Configurable setup, cleanup and `Promise` rejector usable with legacy API
|
||||
* for determining when (and reacting if) a user cancels the operation. The
|
||||
* method will be passed a reference to the internal `rejectionHandler` that
|
||||
* can, e.g., be attached to/removed from the window or called after a
|
||||
* timeout. The method should return a function that will be called when
|
||||
* either the user chooses to open a file or the `rejectionHandler` is
|
||||
* called. In the latter case, the returned function will also be passed a
|
||||
* reference to the `reject` callback for the `Promise` returned by
|
||||
* `fileOpen`, so that developers may reject the `Promise` when desired at
|
||||
* that time.
|
||||
* Example rejector:
|
||||
*
|
||||
* const file = await directoryOpen({
|
||||
* legacySetup: (rejectionHandler) => {
|
||||
* const timeoutId = setTimeout(rejectionHandler, 10_000);
|
||||
* return (reject) => {
|
||||
* clearTimeout(timeoutId);
|
||||
* if (reject) {
|
||||
* reject('My error message here.');
|
||||
* }
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* ToDo: Remove this workaround once
|
||||
* https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||
*/
|
||||
legacySetup?: (
|
||||
resolve: (value: FileWithDirectoryHandle) => void,
|
||||
rejectionHandler: () => void,
|
||||
input: HTMLInputElement
|
||||
) => (reject?: (reason?: any) => void) => void;
|
||||
}): Promise<FileWithDirectoryHandle[]>;
|
||||
|
||||
/**
|
||||
* Whether the File System Access API is supported.
|
||||
*/
|
||||
export const supported: boolean;
|
||||
|
||||
export function imageToBlob(img: HTMLImageElement): Promise<Blob>;
|
||||
|
||||
export interface FileWithHandle extends File {
|
||||
handle?: FileSystemHandle;
|
||||
}
|
||||
|
||||
export interface FileWithDirectoryHandle extends File {
|
||||
directoryHandle?: FileSystemHandle;
|
||||
}
|
||||
|
||||
// The following typings implement the relevant parts of the File System Access
|
||||
// API. This can be removed once the specification reaches the Candidate phase
|
||||
// and is implemented as part of microsoft/TSJS-lib-generator.
|
||||
|
||||
export interface FileSystemHandlePermissionDescriptor {
|
||||
mode?: 'read' | 'readwrite';
|
||||
}
|
||||
|
||||
export interface FileSystemHandle {
|
||||
readonly kind: 'file' | 'directory';
|
||||
readonly name: string;
|
||||
|
||||
isSameEntry: (other: FileSystemHandle) => Promise<boolean>;
|
||||
|
||||
queryPermission: (
|
||||
descriptor?: FileSystemHandlePermissionDescriptor
|
||||
) => Promise<PermissionState>;
|
||||
requestPermission: (
|
||||
descriptor?: FileSystemHandlePermissionDescriptor
|
||||
) => Promise<PermissionState>;
|
||||
}
|
||||
@@ -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.js';
|
||||
export { directoryOpen } from './directory-open.js';
|
||||
export { fileSave } from './file-save.js';
|
||||
export { default as supported } from './supported.js';
|
||||
@@ -0,0 +1,71 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 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 = [{}]) => {
|
||||
if (!Array.isArray(options)) {
|
||||
options = [options];
|
||||
}
|
||||
options[0].recursive = options[0].recursive || false;
|
||||
return new Promise((resolve, reject) => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.webkitdirectory = true;
|
||||
|
||||
const _reject = () => cleanupListenersAndMaybeReject(reject);
|
||||
const _resolve = value => {
|
||||
if (typeof cleanupListenersAndMaybeReject === 'function') {
|
||||
cleanupListenersAndMaybeReject();
|
||||
}
|
||||
resolve(value);
|
||||
};
|
||||
// ToDo: Remove this workaround once
|
||||
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||
const cleanupListenersAndMaybeReject =
|
||||
options[0].legacySetup &&
|
||||
options[0].legacySetup(_resolve, _reject, input);
|
||||
|
||||
input.addEventListener('change', () => {
|
||||
let files = Array.from(input.files);
|
||||
if (!options[0].recursive) {
|
||||
files = files.filter(file => {
|
||||
return file.webkitRelativePath.split('/').length === 2;
|
||||
});
|
||||
} else if (options[0].recursive && options[0].skipDirectory) {
|
||||
files = files.filter(file => {
|
||||
const directoriesName = file.webkitRelativePath.split('/');
|
||||
return directoriesName.every(
|
||||
directoryName =>
|
||||
!options[0].skipDirectory({
|
||||
name: directoryName,
|
||||
kind: 'directory',
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_resolve(files);
|
||||
});
|
||||
|
||||
input.click();
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 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 = [{}]) => {
|
||||
if (!Array.isArray(options)) {
|
||||
options = [options];
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
const accept = [
|
||||
...options.map(option => option.mimeTypes || []).join(),
|
||||
options.map(option => option.extensions || []).join(),
|
||||
].join();
|
||||
input.multiple = options[0].multiple || false;
|
||||
// Empty string allows everything.
|
||||
input.accept = accept || '';
|
||||
const _reject = () => cleanupListenersAndMaybeReject(reject);
|
||||
const _resolve = value => {
|
||||
if (typeof cleanupListenersAndMaybeReject === 'function') {
|
||||
cleanupListenersAndMaybeReject();
|
||||
}
|
||||
resolve(value);
|
||||
};
|
||||
// ToDo: Remove this workaround once
|
||||
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||
const cleanupListenersAndMaybeReject =
|
||||
options[0].legacySetup &&
|
||||
options[0].legacySetup(_resolve, _reject, input);
|
||||
input.addEventListener('change', () => {
|
||||
_resolve(input.multiple ? Array.from(input.files) : input.files[0]);
|
||||
});
|
||||
|
||||
input.click();
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 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 (blobOrResponse, options = {}) => {
|
||||
if (Array.isArray(options)) {
|
||||
options = options[0];
|
||||
}
|
||||
const a = document.createElement('a');
|
||||
let data = blobOrResponse;
|
||||
// Handle the case where input is a `ReadableStream`.
|
||||
if ('body' in blobOrResponse) {
|
||||
data = await streamToBlob(
|
||||
blobOrResponse.body,
|
||||
blobOrResponse.headers.get('content-type')
|
||||
);
|
||||
}
|
||||
a.download = options.fileName || 'Untitled';
|
||||
a.href = URL.createObjectURL(data);
|
||||
|
||||
const _reject = () => cleanupListenersAndMaybeReject(reject);
|
||||
const _resolve = () => {
|
||||
if (typeof cleanupListenersAndMaybeReject === 'function') {
|
||||
cleanupListenersAndMaybeReject();
|
||||
}
|
||||
};
|
||||
// ToDo: Remove this workaround once
|
||||
// https://github.com/whatwg/html/issues/6376 is specified and supported.
|
||||
const cleanupListenersAndMaybeReject =
|
||||
options.legacySetup && options.legacySetup(_resolve, _reject, a);
|
||||
|
||||
a.addEventListener('click', () => {
|
||||
// `setTimeout()` due to
|
||||
// https://github.com/LLK/scratch-gui/issues/1783#issuecomment-426286393
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
|
||||
_resolve(null);
|
||||
});
|
||||
a.click();
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a passed `ReadableStream` to a `Blob`.
|
||||
* @param {ReadableStream} stream
|
||||
* @param {string} type
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
async function streamToBlob(stream, type) {
|
||||
const reader = stream.getReader();
|
||||
const pumpedStream = new ReadableStream({
|
||||
start(controller) {
|
||||
return pump();
|
||||
/**
|
||||
* Recursively pumps data chunks out of the `ReadableStream`.
|
||||
* @type { () => Promise<void> }
|
||||
*/
|
||||
async function pump() {
|
||||
return reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
controller.enqueue(value);
|
||||
return pump();
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const res = new Response(pumpedStream);
|
||||
reader.releaseLock();
|
||||
return new Blob([await res.blob()], { type });
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 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 = (() => {
|
||||
// When running in an SSR environment return `false`.
|
||||
if (typeof self === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
// 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 ('showOpenFilePicker' in self) {
|
||||
return 'showOpenFilePicker';
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
export default supported;
|
||||
Reference in New Issue
Block a user