import { Arr, Obj } from '@ephox/katamari'; import { SugarBody, type SugarElement } from '@ephox/sugar'; import { createCopyEvent, createCutEvent, createPasteEvent } from '../clipboard/ClipboardEvents'; import { createDataTransfer } from '../datatransfer/DataTransfer'; import { getWindowFromElement } from '../dragndrop/DndEvents'; import * as BlobReader from '../file/BlobReader'; import { Chain } from './Chain'; import * as ChainSequence from './ChainSequence'; import type { Step } from './Step'; import { cFindIn } from './UiFinder'; export interface PasteUrlItem { readonly kind: 'string' | 'file'; readonly url: string; } const pasteDataTransfer = (target: SugarElement, mutator: (dataTransfer: DataTransfer) => void): void => { const win = getWindowFromElement(target); const dataTransfer = createDataTransfer(); const event = createPasteEvent(win, 0, 0, dataTransfer); mutator(dataTransfer); target.dom.dispatchEvent(event); }; const pasteItems = (target: SugarElement, items: Record): void => pasteDataTransfer(target, (dataTransfer) => { Obj.each(items, (data, mime) => { dataTransfer.setData(mime, data); }); }); const pasteFiles = (target: SugarElement, files: File[]): void => pasteDataTransfer(target, (dataTransfer) => { Arr.each(files, (file) => { dataTransfer.items.add(file); }); }); const cPasteDataTransfer = (mutator: (dataTransfer: DataTransfer) => void): Chain, SugarElement> => Chain.op((target) => pasteDataTransfer(target, mutator)); const cPasteItems = (items: Record): Chain, SugarElement> => Chain.op((target) => pasteItems(target, items)); const cPasteFiles = (files: File[]): Chain, SugarElement> => Chain.op((target) => pasteFiles(target, files)); const sPasteDataTransfer = (mutator: (dataTransfer: DataTransfer) => void, selector: string): Step => Chain.isolate({}, ChainSequence.sequence([ Chain.injectThunked(SugarBody.body), cFindIn(selector), cPasteDataTransfer(mutator) ])); const sPasteItems = (items: Record, selector: string): Step => Chain.isolate({}, ChainSequence.sequence([ Chain.injectThunked(SugarBody.body), cFindIn(selector), cPasteItems(items) ])); const sPasteFiles = (files: File[], selector: string): Step => Chain.isolate({}, ChainSequence.sequence([ Chain.injectThunked(SugarBody.body), cFindIn(selector), cPasteFiles(files) ])); const pPasteUrlItems = async (target: SugarElement, items: PasteUrlItem[]): Promise => { const dataItems = await Promise.all(Arr.map(items, async (item) => { const resp = await window.fetch(item.url); if (resp.ok) { const blob = await resp.blob(); const fileName = Arr.last(item.url.split('/')).getOr('filename.dat'); const mime = blob.type.split(';')[0]; // Only grab mime type not charset encoding if (item.kind === 'string') { return { kind: 'string', mime, text: await BlobReader.readBlobAsString(blob) }; } else { return { kind: item.kind, file: new window.File([ blob ], fileName, { type: mime }) }; } } else { return Promise.reject(new Error(`Failed to load paste URL item: "${item.url}", status: ${resp.status}`)); } })); pasteDataTransfer(target, (dataTransfer) => { Arr.each(dataItems, (dataItem) => { if (dataItem.kind === 'string') { dataTransfer.items.add(dataItem.text, dataItem.mime); } else { dataTransfer.items.add(dataItem.file); } }); }); }; const cut = (target: SugarElement): DataTransfer => { const win = getWindowFromElement(target); const dataTransfer = createDataTransfer(); const event = createCutEvent(win, 0, 0, dataTransfer); target.dom.dispatchEvent(event); return dataTransfer; }; const copy = (target: SugarElement): DataTransfer => { const win = getWindowFromElement(target); const dataTransfer = createDataTransfer(); const event = createCopyEvent(win, 0, 0, dataTransfer); target.dom.dispatchEvent(event); return dataTransfer; }; const cCut: Chain, DataTransfer> = Chain.mapper(cut); const cCopy: Chain, DataTransfer> = Chain.mapper(copy); export { pasteDataTransfer, pasteItems, pasteFiles, cPasteDataTransfer, cPasteItems, cPasteFiles, sPasteDataTransfer, sPasteItems, sPasteFiles, pPasteUrlItems, cut, copy, cCut, cCopy };