]> git.proxmox.com Git - mirror_xterm.js.git/blame - src/handlers/Clipboard.ts
Merge remote-tracking branch 'ups/master' into set_row_height_explicitly
[mirror_xterm.js.git] / src / handlers / Clipboard.ts
CommitLineData
42a1e4ef 1/**
1d300911
PK
2 * Clipboard handler module: exports methods for handling all clipboard-related events in the
3 * terminal.
a224acb5 4 * @module xterm/handlers/Clipboard
1d300911 5 * @license MIT
a224acb5
PK
6 */
7
824a9c6d
PK
8import { ITerminal } from '../Interfaces';
9
a34bb972
PK
10interface IWindow extends Window {
11 clipboardData?: {
12 getData(format: string): string;
13 setData(format: string, data: string);
14 };
15}
16
17declare var window: IWindow;
18
42a1e4ef
PK
19/**
20 * Prepares text copied from terminal selection, to be saved in the clipboard by:
21 * 1. stripping all trailing white spaces
22 * 2. converting all non-breaking spaces to regular spaces
23 * @param {string} text The copied text that needs processing for storing in clipboard
24 * @returns {string}
42a1e4ef 25 */
a34bb972 26export function prepareTextForClipboard(text: string): string {
824a9c6d 27 let space = String.fromCharCode(32),
42a1e4ef
PK
28 nonBreakingSpace = String.fromCharCode(160),
29 allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
30 processedText = text.split('\n').map(function (line) {
31 // Strip all trailing white spaces and convert all non-breaking spaces
32 // to regular spaces.
824a9c6d 33 let processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
42a1e4ef
PK
34
35 return processedLine;
36 }).join('\n');
37
38 return processedText;
39}
40
0532e5cb
JD
41/**
42 * Prepares text to be pasted into the terminal by normalizing the line endings
43 * @param text The pasted text that needs processing before inserting into the terminal
44 */
45export function prepareTextForTerminal(text: string, isMSWindows: boolean): string {
46 if (isMSWindows) {
47 return text.replace(/\r?\n/g, '\n');
48 }
49 return text;
50}
51
42a1e4ef
PK
52/**
53 * Binds copy functionality to the given terminal.
a224acb5 54 * @param {ClipboardEvent} ev The original copy event to be handled
42a1e4ef 55 */
69e02739 56export function copyHandler(ev: ClipboardEvent, term: ITerminal) {
824a9c6d
PK
57 // We cast `window` to `any` type, because TypeScript has not declared the `clipboardData`
58 // property that we use below for Internet Explorer.
a34bb972 59 let copiedText = window.getSelection().toString(),
42a1e4ef
PK
60 text = prepareTextForClipboard(copiedText);
61
5808de64 62 if (term.browser.isMSIE) {
a34bb972 63 window.clipboardData.setData('Text', text);
5808de64 64 } else {
65 ev.clipboardData.setData('text/plain', text);
66 }
67
42a1e4ef
PK
68 ev.preventDefault(); // Prevent or the original text will be copied.
69}
70
71/**
a224acb5
PK
72 * Redirect the clipboard's data to the terminal's input handler.
73 * @param {ClipboardEvent} ev The original paste event to be handled
74 * @param {Terminal} term The terminal on which to apply the handled paste event
42a1e4ef 75 */
69e02739 76export function pasteHandler(ev: ClipboardEvent, term: ITerminal) {
42a1e4ef 77 ev.stopPropagation();
77ca1549 78
a34bb972 79 let text: string;
824a9c6d
PK
80
81 let dispatchPaste = function(text) {
0532e5cb 82 text = prepareTextForTerminal(text, term.browser.isMSWindows);
42a1e4ef
PK
83 term.handler(text);
84 term.textarea.value = '';
616f0695
PK
85 term.emit('paste', text);
86
42a1e4ef 87 return term.cancel(ev);
77ca1549 88 };
89
5808de64 90 if (term.browser.isMSIE) {
a34bb972
PK
91 if (window.clipboardData) {
92 text = window.clipboardData.getData('Text');
77ca1549 93 dispatchPaste(text);
94 }
95 } else {
96 if (ev.clipboardData) {
824a9c6d 97 text = ev.clipboardData.getData('text/plain');
77ca1549 98 dispatchPaste(text);
99 }
42a1e4ef
PK
100 }
101}
102
a224acb5
PK
103/**
104 * Bind to right-click event and allow right-click copy and paste.
105 *
106 * **Logic**
107 * If text is selected and right-click happens on selected text, then
108 * do nothing to allow seamless copying.
109 * If no text is selected or right-click is outside of the selection
110 * area, then bring the terminal's input below the cursor, in order to
111 * trigger the event on the textarea and allow-right click paste, without
112 * caring about disappearing selection.
69e02739 113 * @param {MouseEvent} ev The original right click event to be handled
a224acb5
PK
114 * @param {Terminal} term The terminal on which to apply the handled paste event
115 */
69e02739 116export function rightClickHandler(ev: MouseEvent, term: ITerminal) {
824a9c6d 117 let s = document.getSelection(),
35637797 118 selectedText = prepareTextForClipboard(s.toString()),
824a9c6d
PK
119 clickIsOnSelection = false,
120 x = ev.clientX,
121 y = ev.clientY;
42a1e4ef 122
00dcb4f6 123 if (s.rangeCount) {
824a9c6d 124 let r = s.getRangeAt(0),
20b6f209
PK
125 cr = r.getClientRects();
126
127 for (let i = 0; i < cr.length; i++) {
128 let rect = cr[i];
42a1e4ef 129
00dcb4f6
PK
130 clickIsOnSelection = (
131 (x > rect.left) && (x < rect.right) &&
132 (y > rect.top) && (y < rect.bottom)
133 );
35637797
PK
134
135 if (clickIsOnSelection) {
00dcb4f6
PK
136 break;
137 }
42a1e4ef 138 }
35637797
PK
139 // If we clicked on selection and selection is not a single space,
140 // then mark the right click as copy-only. We check for the single
141 // space selection, as this can happen when clicking on an &nbsp;
142 // and there is not much pointing in copying a single space.
143 if (selectedText.match(/^\s$/) || !selectedText.length) {
144 clickIsOnSelection = false;
145 }
42a1e4ef
PK
146 }
147
148 // Bring textarea at the cursor position
149 if (!clickIsOnSelection) {
150 term.textarea.style.position = 'fixed';
35637797
PK
151 term.textarea.style.width = '20px';
152 term.textarea.style.height = '20px';
153 term.textarea.style.left = (x - 10) + 'px';
154 term.textarea.style.top = (y - 10) + 'px';
824a9c6d 155 term.textarea.style.zIndex = '1000';
42a1e4ef
PK
156 term.textarea.focus();
157
158 // Reset the terminal textarea's styling
159 setTimeout(function () {
160 term.textarea.style.position = null;
161 term.textarea.style.width = null;
162 term.textarea.style.height = null;
163 term.textarea.style.left = null;
164 term.textarea.style.top = null;
165 term.textarea.style.zIndex = null;
35637797 166 }, 4);
42a1e4ef
PK
167 }
168}