]>
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 PK |
37 | */ |
38 | function copyHandler (ev) { | |
39 | var copiedText = window.getSelection().toString(), | |
40 | text = prepareTextForClipboard(copiedText); | |
41 | ||
a6f04733 | 42 | ev.clipboardData.setData('text/plain', text); |
42a1e4ef PK |
43 | ev.preventDefault(); // Prevent or the original text will be copied. |
44 | } | |
45 | ||
46 | /** | |
a224acb5 PK |
47 | * Redirect the clipboard's data to the terminal's input handler. |
48 | * @param {ClipboardEvent} ev The original paste event to be handled | |
49 | * @param {Terminal} term The terminal on which to apply the handled paste event | |
42a1e4ef PK |
50 | */ |
51 | function pasteHandler(ev, term) { | |
52 | ev.stopPropagation(); | |
77ca1549 | 53 | |
54 | var dispatchPaste = function(text) { | |
42a1e4ef PK |
55 | term.handler(text); |
56 | term.textarea.value = ''; | |
57 | return term.cancel(ev); | |
77ca1549 | 58 | }; |
59 | ||
60 | var userAgent = window.navigator.userAgent.toLowerCase(); | |
61 | if (userAgent.match(/msie|MSIE/) || userAgent.match(/(T|t)rident/)) { | |
62 | if (window.clipboardData) { | |
63 | var text = window.clipboardData.getData('Text'); | |
64 | dispatchPaste(text); | |
65 | } | |
66 | } else { | |
67 | if (ev.clipboardData) { | |
68 | var text = ev.clipboardData.getData('text/plain'); | |
69 | dispatchPaste(text); | |
70 | } | |
42a1e4ef PK |
71 | } |
72 | } | |
73 | ||
a224acb5 PK |
74 | /** |
75 | * Bind to right-click event and allow right-click copy and paste. | |
76 | * | |
77 | * **Logic** | |
78 | * If text is selected and right-click happens on selected text, then | |
79 | * do nothing to allow seamless copying. | |
80 | * If no text is selected or right-click is outside of the selection | |
81 | * area, then bring the terminal's input below the cursor, in order to | |
82 | * trigger the event on the textarea and allow-right click paste, without | |
83 | * caring about disappearing selection. | |
84 | * @param {ClipboardEvent} ev The original paste event to be handled | |
85 | * @param {Terminal} term The terminal on which to apply the handled paste event | |
86 | */ | |
87 | function rightClickHandler(ev, term) { | |
42a1e4ef | 88 | var s = document.getSelection(), |
35637797 | 89 | selectedText = prepareTextForClipboard(s.toString()), |
00dcb4f6 | 90 | clickIsOnSelection = false; |
42a1e4ef | 91 | |
00dcb4f6 PK |
92 | if (s.rangeCount) { |
93 | var r = s.getRangeAt(0), | |
94 | cr = r.getClientRects(), | |
95 | x = ev.clientX, | |
96 | y = ev.clientY, | |
97 | i, rect; | |
42a1e4ef | 98 | |
00dcb4f6 PK |
99 | for (i=0; i<cr.length; i++) { |
100 | rect = cr[i]; | |
101 | clickIsOnSelection = ( | |
102 | (x > rect.left) && (x < rect.right) && | |
103 | (y > rect.top) && (y < rect.bottom) | |
104 | ); | |
35637797 PK |
105 | |
106 | if (clickIsOnSelection) { | |
00dcb4f6 PK |
107 | break; |
108 | } | |
42a1e4ef | 109 | } |
35637797 PK |
110 | // If we clicked on selection and selection is not a single space, |
111 | // then mark the right click as copy-only. We check for the single | |
112 | // space selection, as this can happen when clicking on an | |
113 | // and there is not much pointing in copying a single space. | |
114 | if (selectedText.match(/^\s$/) || !selectedText.length) { | |
115 | clickIsOnSelection = false; | |
116 | } | |
42a1e4ef PK |
117 | } |
118 | ||
119 | // Bring textarea at the cursor position | |
120 | if (!clickIsOnSelection) { | |
121 | term.textarea.style.position = 'fixed'; | |
35637797 PK |
122 | term.textarea.style.width = '20px'; |
123 | term.textarea.style.height = '20px'; | |
124 | term.textarea.style.left = (x - 10) + 'px'; | |
125 | term.textarea.style.top = (y - 10) + 'px'; | |
42a1e4ef PK |
126 | term.textarea.style.zIndex = 1000; |
127 | term.textarea.focus(); | |
128 | ||
129 | // Reset the terminal textarea's styling | |
130 | setTimeout(function () { | |
131 | term.textarea.style.position = null; | |
132 | term.textarea.style.width = null; | |
133 | term.textarea.style.height = null; | |
134 | term.textarea.style.left = null; | |
135 | term.textarea.style.top = null; | |
136 | term.textarea.style.zIndex = null; | |
35637797 | 137 | }, 4); |
42a1e4ef PK |
138 | } |
139 | } | |
140 | ||
141 | export { | |
142 | prepareTextForClipboard, copyHandler, pasteHandler, rightClickHandler | |
143 | }; |