]>
Commit | Line | Data |
---|---|---|
42a1e4ef PK |
1 | /** |
2 | * xterm.js: xterm, in the browser | |
3 | * Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License) | |
4 | */ | |
5 | ||
a224acb5 PK |
6 | /** |
7 | * Clipboard handler module. This module contains methods for handling all | |
8 | * clipboard-related events appropriately in the terminal. | |
9 | * @module xterm/handlers/Clipboard | |
10 | */ | |
11 | ||
42a1e4ef PK |
12 | /** |
13 | * Prepares text copied from terminal selection, to be saved in the clipboard by: | |
14 | * 1. stripping all trailing white spaces | |
15 | * 2. converting all non-breaking spaces to regular spaces | |
16 | * @param {string} text The copied text that needs processing for storing in clipboard | |
17 | * @returns {string} | |
42a1e4ef PK |
18 | */ |
19 | function prepareTextForClipboard(text) { | |
20 | var space = String.fromCharCode(32), | |
21 | nonBreakingSpace = String.fromCharCode(160), | |
22 | allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'), | |
23 | processedText = text.split('\n').map(function (line) { | |
24 | // Strip all trailing white spaces and convert all non-breaking spaces | |
25 | // to regular spaces. | |
26 | var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space); | |
27 | ||
28 | return processedLine; | |
29 | }).join('\n'); | |
30 | ||
31 | return processedText; | |
32 | } | |
33 | ||
34 | /** | |
35 | * Binds copy functionality to the given terminal. | |
a224acb5 | 36 | * @param {ClipboardEvent} ev The original copy event to be handled |
42a1e4ef | 37 | */ |
5808de64 | 38 | function copyHandler(ev, term) { |
42a1e4ef PK |
39 | var copiedText = window.getSelection().toString(), |
40 | text = prepareTextForClipboard(copiedText); | |
41 | ||
5808de64 | 42 | if (term.browser.isMSIE) { |
43 | window.clipboardData.setData('Text', text); | |
44 | } else { | |
45 | ev.clipboardData.setData('text/plain', text); | |
46 | } | |
47 | ||
42a1e4ef PK |
48 | ev.preventDefault(); // Prevent or the original text will be copied. |
49 | } | |
50 | ||
51 | /** | |
a224acb5 PK |
52 | * Redirect the clipboard's data to the terminal's input handler. |
53 | * @param {ClipboardEvent} ev The original paste event to be handled | |
54 | * @param {Terminal} term The terminal on which to apply the handled paste event | |
42a1e4ef PK |
55 | */ |
56 | function pasteHandler(ev, term) { | |
57 | ev.stopPropagation(); | |
77ca1549 | 58 | |
59 | var dispatchPaste = function(text) { | |
42a1e4ef PK |
60 | term.handler(text); |
61 | term.textarea.value = ''; | |
62 | return term.cancel(ev); | |
77ca1549 | 63 | }; |
64 | ||
5808de64 | 65 | if (term.browser.isMSIE) { |
77ca1549 | 66 | if (window.clipboardData) { |
67 | var text = window.clipboardData.getData('Text'); | |
68 | dispatchPaste(text); | |
69 | } | |
70 | } else { | |
71 | if (ev.clipboardData) { | |
72 | var text = ev.clipboardData.getData('text/plain'); | |
73 | dispatchPaste(text); | |
74 | } | |
42a1e4ef PK |
75 | } |
76 | } | |
77 | ||
a224acb5 PK |
78 | /** |
79 | * Bind to right-click event and allow right-click copy and paste. | |
80 | * | |
81 | * **Logic** | |
82 | * If text is selected and right-click happens on selected text, then | |
83 | * do nothing to allow seamless copying. | |
84 | * If no text is selected or right-click is outside of the selection | |
85 | * area, then bring the terminal's input below the cursor, in order to | |
86 | * trigger the event on the textarea and allow-right click paste, without | |
87 | * caring about disappearing selection. | |
88 | * @param {ClipboardEvent} ev The original paste event to be handled | |
89 | * @param {Terminal} term The terminal on which to apply the handled paste event | |
90 | */ | |
91 | function rightClickHandler(ev, term) { | |
42a1e4ef | 92 | var s = document.getSelection(), |
35637797 | 93 | selectedText = prepareTextForClipboard(s.toString()), |
00dcb4f6 | 94 | clickIsOnSelection = false; |
42a1e4ef | 95 | |
00dcb4f6 PK |
96 | if (s.rangeCount) { |
97 | var r = s.getRangeAt(0), | |
98 | cr = r.getClientRects(), | |
99 | x = ev.clientX, | |
100 | y = ev.clientY, | |
101 | i, rect; | |
42a1e4ef | 102 | |
00dcb4f6 PK |
103 | for (i=0; i<cr.length; i++) { |
104 | rect = cr[i]; | |
105 | clickIsOnSelection = ( | |
106 | (x > rect.left) && (x < rect.right) && | |
107 | (y > rect.top) && (y < rect.bottom) | |
108 | ); | |
35637797 PK |
109 | |
110 | if (clickIsOnSelection) { | |
00dcb4f6 PK |
111 | break; |
112 | } | |
42a1e4ef | 113 | } |
35637797 PK |
114 | // If we clicked on selection and selection is not a single space, |
115 | // then mark the right click as copy-only. We check for the single | |
116 | // space selection, as this can happen when clicking on an | |
117 | // and there is not much pointing in copying a single space. | |
118 | if (selectedText.match(/^\s$/) || !selectedText.length) { | |
119 | clickIsOnSelection = false; | |
120 | } | |
42a1e4ef PK |
121 | } |
122 | ||
123 | // Bring textarea at the cursor position | |
124 | if (!clickIsOnSelection) { | |
125 | term.textarea.style.position = 'fixed'; | |
35637797 PK |
126 | term.textarea.style.width = '20px'; |
127 | term.textarea.style.height = '20px'; | |
128 | term.textarea.style.left = (x - 10) + 'px'; | |
129 | term.textarea.style.top = (y - 10) + 'px'; | |
42a1e4ef PK |
130 | term.textarea.style.zIndex = 1000; |
131 | term.textarea.focus(); | |
132 | ||
133 | // Reset the terminal textarea's styling | |
134 | setTimeout(function () { | |
135 | term.textarea.style.position = null; | |
136 | term.textarea.style.width = null; | |
137 | term.textarea.style.height = null; | |
138 | term.textarea.style.left = null; | |
139 | term.textarea.style.top = null; | |
140 | term.textarea.style.zIndex = null; | |
35637797 | 141 | }, 4); |
42a1e4ef PK |
142 | } |
143 | } | |
144 | ||
145 | export { | |
146 | prepareTextForClipboard, copyHandler, pasteHandler, rightClickHandler | |
147 | }; |