diff --git a/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts b/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts index 64eecbc5ca..687c5280ee 100644 --- a/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts +++ b/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts @@ -1,18 +1,6 @@ import { HooksRunner } from '../types'; -import { - OFFICE_CLIPBOARD_MIMETYPE, - InnerClipInfo, - ClipBlockInfo, -} from './types'; import { Editor } from '../editor'; -import { AsyncBlock } from '../block'; import ClipboardParse from './clipboard-parse'; -import { SelectInfo } from '../selection'; -import { - Protocol, - BlockFlavorKeys, - services, -} from '@toeverything/datasource/db-service'; import { MarkdownParser } from './markdown-parse'; import { shouldHandlerContinue } from './utils'; import { Paste } from './paste'; diff --git a/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts b/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts index 2f05866ab9..d7a8172566 100644 --- a/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts +++ b/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts @@ -1,23 +1,16 @@ import { Editor } from '../editor'; -import { SelectionManager, SelectInfo, SelectBlock } from '../selection'; +import { SelectionManager } from '../selection'; import { HookType, PluginHooks } from '../types'; -import { - ClipBlockInfo, - OFFICE_CLIPBOARD_MIMETYPE, - InnerClipInfo, -} from './types'; -import { Clip } from './clip'; -import assert from 'assert'; import ClipboardParse from './clipboard-parse'; import { Subscription } from 'rxjs'; - +import { Copy } from './copy'; class ClipboardPopulator { private _editor: Editor; private _hooks: PluginHooks; private _selectionManager: SelectionManager; private _clipboardParse: ClipboardParse; private _sub = new Subscription(); - + private _copy: Copy; constructor( editor: Editor, hooks: PluginHooks, @@ -27,128 +20,15 @@ class ClipboardPopulator { this._hooks = hooks; this._selectionManager = selectionManager; this._clipboardParse = new ClipboardParse(editor); + this._copy = new Copy(editor); this._sub.add( - hooks - .get(HookType.BEFORE_COPY) - .subscribe(this._populateAppClipboard) + hooks.get(HookType.BEFORE_COPY).subscribe(this._copy.handleCopy) ); this._sub.add( - hooks.get(HookType.BEFORE_CUT).subscribe(this._populateAppClipboard) + hooks.get(HookType.BEFORE_CUT).subscribe(this._copy.handleCopy) ); } - private _populateAppClipboard = async (e: ClipboardEvent) => { - e.preventDefault(); - e.stopPropagation(); - const clips = await this.getClips(); - if (!clips.length) { - return; - } - - // TODO: is not compatible with safari - const success = this._copyToClipboardFromPc(clips); - if (!success) { - // This way, not compatible with firefox - const clipboardData = e.clipboardData; - if (clipboardData) { - try { - clips.forEach(clip => { - clipboardData.setData( - clip.getMimeType(), - clip.getData() - ); - }); - } catch (e) { - // TODO handle exception - } - } - } - }; - - private _copyToClipboardFromPc(clips: any[]) { - let success = false; - const tempElem = document.createElement('textarea'); - tempElem.value = 'temp'; - document.body.appendChild(tempElem); - tempElem.select(); - tempElem.setSelectionRange(0, tempElem.value.length); - - const listener = function (e: any) { - const clipboardData = e.clipboardData; - if (clipboardData) { - clips.forEach(clip => { - clipboardData.setData(clip.getMimeType(), clip.getData()); - }); - } - - e.preventDefault(); - e.stopPropagation(); - tempElem.removeEventListener('copy', listener); - } as any; - - tempElem.addEventListener('copy', listener); - try { - success = document.execCommand('copy'); - } finally { - tempElem.removeEventListener('copy', listener); - document.body.removeChild(tempElem); - } - return success; - } - - private async _getClipBlockInfo(selBlock: SelectBlock) { - const block = await this._editor.getBlockById(selBlock.blockId); - const blockView = this._editor.getView(block.type); - assert(blockView); - const blockInfo: ClipBlockInfo = { - type: block.type, - properties: blockView.getSelProperties(block, selBlock), - children: [] as any[], - }; - - for (let i = 0; i < selBlock.children.length; i++) { - const childInfo = await this._getClipBlockInfo( - selBlock.children[i] - ); - blockInfo.children.push(childInfo); - } - - return blockInfo; - } - - private async _getInnerClip(): Promise { - const clips: ClipBlockInfo[] = []; - const selectInfo: SelectInfo = - await this._selectionManager.getSelectInfo(); - for (let i = 0; i < selectInfo.blocks.length; i++) { - const selBlock = selectInfo.blocks[i]; - const clipBlockInfo = await this._getClipBlockInfo(selBlock); - clips.push(clipBlockInfo); - } - return { - select: selectInfo, - data: clips, - }; - } - - async getClips() { - const clips: any[] = []; - - const innerClip = await this._getInnerClip(); - clips.push( - new Clip( - OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, - JSON.stringify(innerClip) - ) - ); - - const htmlClip = await this._clipboardParse.generateHtml(); - htmlClip && - clips.push(new Clip(OFFICE_CLIPBOARD_MIMETYPE.HTML, htmlClip)); - - return clips; - } - disposeInternal() { this._sub.unsubscribe(); this._hooks = null; diff --git a/libs/components/editor-core/src/editor/clipboard/copy.ts b/libs/components/editor-core/src/editor/clipboard/copy.ts new file mode 100644 index 0000000000..54ee46b26e --- /dev/null +++ b/libs/components/editor-core/src/editor/clipboard/copy.ts @@ -0,0 +1,136 @@ +import { Editor } from '../editor'; +import { SelectInfo, SelectBlock } from '../selection'; +import { + ClipBlockInfo, + OFFICE_CLIPBOARD_MIMETYPE, + InnerClipInfo, +} from './types'; +import { Clip } from './clip'; +import assert from 'assert'; +import ClipboardParse from './clipboard-parse'; + +class Copy { + private _editor: Editor; + private _clipboardParse: ClipboardParse; + + constructor(editor: Editor) { + this._editor = editor; + this._clipboardParse = new ClipboardParse(editor); + + this.handleCopy = this.handleCopy.bind(this); + } + public async handleCopy(e: ClipboardEvent) { + e.preventDefault(); + e.stopPropagation(); + const clips = await this.getClips(); + if (!clips.length) { + return; + } + // TODO: is not compatible with safari + const success = this._copyToClipboardFromPc(clips); + if (!success) { + // This way, not compatible with firefox + const clipboardData = e.clipboardData; + if (clipboardData) { + try { + clips.forEach(clip => { + clipboardData.setData( + clip.getMimeType(), + clip.getData() + ); + }); + } catch (e) { + // TODO handle exception + } + } + } + } + + async getClips() { + const clips: any[] = []; + + // get custom clip + const affineClip = await this._getAffineClip(); + clips.push( + new Clip( + OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, + JSON.stringify(affineClip) + ) + ); + + // get common html clip + const htmlClip = await this._clipboardParse.generateHtml(); + htmlClip && + clips.push(new Clip(OFFICE_CLIPBOARD_MIMETYPE.HTML, htmlClip)); + + return clips; + } + + private async _getAffineClip(): Promise { + const clips: ClipBlockInfo[] = []; + const selectInfo: SelectInfo = + await this._editor.selectionManager.getSelectInfo(); + for (let i = 0; i < selectInfo.blocks.length; i++) { + const selBlock = selectInfo.blocks[i]; + const clipBlockInfo = await this._getClipInfoOfBlock(selBlock); + clips.push(clipBlockInfo); + } + return { + select: selectInfo, + data: clips, + }; + } + + private async _getClipInfoOfBlock(selBlock: SelectBlock) { + const block = await this._editor.getBlockById(selBlock.blockId); + const blockView = this._editor.getView(block.type); + assert(blockView); + const blockInfo: ClipBlockInfo = { + type: block.type, + properties: blockView.getSelProperties(block, selBlock), + children: [] as any[], + }; + + for (let i = 0; i < selBlock.children.length; i++) { + const childInfo = await this._getClipInfoOfBlock( + selBlock.children[i] + ); + blockInfo.children.push(childInfo); + } + + return blockInfo; + } + + private _copyToClipboardFromPc(clips: any[]) { + let success = false; + const tempElem = document.createElement('textarea'); + tempElem.value = 'temp'; + document.body.appendChild(tempElem); + tempElem.select(); + tempElem.setSelectionRange(0, tempElem.value.length); + + const listener = function (e: any) { + const clipboardData = e.clipboardData; + if (clipboardData) { + clips.forEach(clip => { + clipboardData.setData(clip.getMimeType(), clip.getData()); + }); + } + + e.preventDefault(); + e.stopPropagation(); + tempElem.removeEventListener('copy', listener); + } as any; + + tempElem.addEventListener('copy', listener); + try { + success = document.execCommand('copy'); + } finally { + tempElem.removeEventListener('copy', listener); + document.body.removeChild(tempElem); + } + return success; + } +} + +export { Copy };