Add automatic clipboard support
This commit is contained in:
parent
9985950bfa
commit
dc41145770
|
|
@ -0,0 +1,46 @@
|
|||
export default class Clipboard {
|
||||
constructor(target) {
|
||||
this._target = target;
|
||||
|
||||
this._eventHandlers = {
|
||||
'copy': this._handleCopy.bind(this),
|
||||
'paste': this._handlePaste.bind(this)
|
||||
};
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
this.onpaste = () => {};
|
||||
}
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_handleCopy(e) {
|
||||
if (navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(e.clipboardData.getData('text/plain')).catch(() => {/* Do nothing */});
|
||||
}
|
||||
}
|
||||
|
||||
_handlePaste(e) {
|
||||
if (navigator.clipboard.readText) {
|
||||
navigator.clipboard.readText().then(this.onpaste).catch(() => {/* Do nothing */});
|
||||
} else if (e.clipboardData) {
|
||||
this.onpaste(e.clipboardData.getData('text/plain'));
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
grab() {
|
||||
if (!Clipboard.isSupported) return;
|
||||
this._target.addEventListener('copy', this._eventHandlers.copy);
|
||||
this._target.addEventListener('paste', this._eventHandlers.paste);
|
||||
}
|
||||
|
||||
ungrab() {
|
||||
if (!Clipboard.isSupported) return;
|
||||
this._target.removeEventListener('copy', this._eventHandlers.copy);
|
||||
this._target.removeEventListener('paste', this._eventHandlers.paste);
|
||||
}
|
||||
}
|
||||
|
||||
Clipboard.isSupported = (navigator && navigator.clipboard) ? true : false;
|
||||
22
core/rfb.js
22
core/rfb.js
|
|
@ -15,6 +15,7 @@ import { clientToElement } from './util/element.js';
|
|||
import { setCapture } from './util/events.js';
|
||||
import EventTargetMixin from './util/eventtarget.js';
|
||||
import Display from "./display.js";
|
||||
import Clipboard from "./clipboard.js";
|
||||
import Inflator from "./inflator.js";
|
||||
import Deflator from "./deflator.js";
|
||||
import Keyboard from "./input/keyboard.js";
|
||||
|
|
@ -158,6 +159,7 @@ export default class RFB extends EventTargetMixin {
|
|||
this._sock = null; // Websock object
|
||||
this._display = null; // Display object
|
||||
this._flushing = false; // Display flushing state
|
||||
this._clipboard = null; // Clipboard object
|
||||
this._keyboard = null; // Keyboard input handler object
|
||||
this._gestures = null; // Gesture input handler object
|
||||
this._resizeObserver = null; // Resize observer object
|
||||
|
|
@ -259,6 +261,9 @@ export default class RFB extends EventTargetMixin {
|
|||
}
|
||||
this._display.onflush = this._onFlush.bind(this);
|
||||
|
||||
this._clipboard = new Clipboard(this._canvas);
|
||||
this._clipboard.onpaste = this.clipboardPasteFrom.bind(this);
|
||||
|
||||
this._keyboard = new Keyboard(this._canvas);
|
||||
this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
|
||||
|
||||
|
|
@ -310,8 +315,10 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbConnectionState === "connected") {
|
||||
if (viewOnly) {
|
||||
this._keyboard.ungrab();
|
||||
this._clipboard.ungrab();
|
||||
} else {
|
||||
this._keyboard.grab();
|
||||
this._clipboard.grab();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2062,7 +2069,10 @@ export default class RFB extends EventTargetMixin {
|
|||
this._setDesktopName(name);
|
||||
this._resize(width, height);
|
||||
|
||||
if (!this._viewOnly) { this._keyboard.grab(); }
|
||||
if (!this._viewOnly) {
|
||||
this._keyboard.grab();
|
||||
this._clipboard.grab();
|
||||
}
|
||||
|
||||
this._fbDepth = 24;
|
||||
|
||||
|
|
@ -2189,9 +2199,13 @@ export default class RFB extends EventTargetMixin {
|
|||
return true;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent(
|
||||
"clipboard",
|
||||
{ detail: { text: text } }));
|
||||
this.dispatchEvent(new CustomEvent("clipboard", { detail: { text: text } }));
|
||||
|
||||
if (Clipboard.isSupported) {
|
||||
const clipboardData = new DataTransfer();
|
||||
clipboardData.setData("text/plain", text);
|
||||
this._canvas.dispatchEvent(new ClipboardEvent('copy', { clipboardData }));
|
||||
}
|
||||
|
||||
} else {
|
||||
//Extended msg.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ keysym values.
|
|||
* __Display__ (core/display.js): Efficient 2D rendering abstraction
|
||||
layered on the HTML5 canvas element.
|
||||
|
||||
* __Clipboard__ (core/clipboard.js): Clipboard event handler.
|
||||
|
||||
* __Websock__ (core/websock.js): Websock client from websockify
|
||||
with transparent binary data support.
|
||||
[Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page.
|
||||
|
|
@ -25,7 +27,7 @@ with transparent binary data support.
|
|||
|
||||
## 1.2 Callbacks
|
||||
|
||||
For the Mouse, Keyboard and Display objects the callback functions are
|
||||
For the Mouse, Keyboard, Display and Clipboard objects the callback functions are
|
||||
assigned to configuration attributes, just as for the RFB object. The
|
||||
WebSock module has a method named 'on' that takes two parameters: the
|
||||
callback event name, and the callback function.
|
||||
|
|
@ -87,3 +89,23 @@ None
|
|||
| name | parameters | description
|
||||
| ------- | ---------- | ------------
|
||||
| onflush | () | A display flush has been requested and we are now ready to resume FBU processing
|
||||
|
||||
|
||||
## 2.4 Clipboard Module
|
||||
|
||||
### 2.4.1 Configuration Attributes
|
||||
|
||||
None
|
||||
|
||||
### 2.4.2 Methods
|
||||
|
||||
| name | parameters | description
|
||||
| ------------------ | ----------------- | ------------
|
||||
| grab | () | Begin capturing clipboard events
|
||||
| ungrab | () | Stop capturing clipboard events
|
||||
|
||||
### 2.3.3 Callbacks
|
||||
|
||||
| name | parameters | description
|
||||
| ------- | ---------- | ------------
|
||||
| onpaste | (text) | Called with the text content of the clipboard when the user paste something
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
const expect = chai.expect;
|
||||
|
||||
import Clipboard from '../core/clipboard.js';
|
||||
|
||||
describe('Automatic Clipboard Sync', function () {
|
||||
"use strict";
|
||||
|
||||
if (Clipboard.isSupported) {
|
||||
beforeEach(function () {
|
||||
if (navigator.clipboard.writeText) {
|
||||
sinon.spy(navigator.clipboard, 'writeText');
|
||||
}
|
||||
if (navigator.clipboard.readText) {
|
||||
sinon.spy(navigator.clipboard, 'readText');
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText.restore();
|
||||
}
|
||||
if (navigator.clipboard.readText) {
|
||||
navigator.clipboard.readText.restore();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it('incoming clipboard data from the server is copied to the local clipboard', function () {
|
||||
const text = 'Random string for testing';
|
||||
const clipboard = new Clipboard();
|
||||
if (Clipboard.isSupported) {
|
||||
const clipboardData = new DataTransfer();
|
||||
clipboardData.setData("text/plain", text);
|
||||
clipboard._handleCopy(new ClipboardEvent('paste', { clipboardData }));
|
||||
if (navigator.clipboard.writeText) {
|
||||
expect(navigator.clipboard.writeText).to.have.been.calledWith(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should copy local pasted data to the server clipboard', function () {
|
||||
const text = 'Another random string for testing';
|
||||
const clipboard = new Clipboard();
|
||||
clipboard.onpaste = pasterText => expect(pasterText).to.equal(text);
|
||||
if (Clipboard.isSupported) {
|
||||
const clipboardData = new DataTransfer();
|
||||
clipboardData.setData("text/plain", text);
|
||||
clipboard._handlePaste(new ClipboardEvent('paste', { clipboardData }));
|
||||
if (navigator.clipboard.readText) {
|
||||
expect(navigator.clipboard.readText).to.have.been.called;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue