refactor: refactor clip board

This commit is contained in:
QiShaoXuan
2022-08-17 19:47:54 +08:00
parent fcabeb919e
commit 8ccc997062
3 changed files with 142 additions and 138 deletions
@@ -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';
@@ -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<InnerClipInfo> {
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;
@@ -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<InnerClipInfo> {
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 };