GUACAMOLE-310: Merge fix clipboard handling of newlines.

This commit is contained in:
Nick Couchman
2017-09-03 20:42:50 -04:00
2 changed files with 92 additions and 55 deletions

View File

@@ -57,10 +57,9 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
* *
* @type Element * @type Element
*/ */
var clipboardContent = document.createElement('div'); var clipboardContent = document.createElement('textarea');
// Ensure clipboard target is selectable but not visible // Ensure clipboard target is selectable but not visible
clipboardContent.setAttribute('contenteditable', 'true');
clipboardContent.className = 'clipboard-service-target'; clipboardContent.className = 'clipboard-service-target';
// Add clipboard target to DOM // Add clipboard target to DOM
@@ -136,14 +135,23 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
*/ */
var selectAll = function selectAll(element) { var selectAll = function selectAll(element) {
// Generate a range which selects all nodes within the given element // Use the select() function defined for input elements, if available
var range = document.createRange(); if (element.select)
range.selectNodeContents(element); element.select();
// Replace any current selection with the generated range // Fallback to manual manipulation of the selection
var selection = $window.getSelection(); else {
selection.removeAllRanges();
selection.addRange(range); // Generate a range which selects all nodes within the given element
var range = document.createRange();
range.selectNodeContents(element);
// Replace any current selection with the generated range
var selection = $window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}; };
@@ -167,7 +175,7 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
// Copy the given value into the clipboard DOM element // Copy the given value into the clipboard DOM element
if (typeof data.data === 'string') if (typeof data.data === 'string')
clipboardContent.textContent = data.data; clipboardContent.value = data.data;
else { else {
clipboardContent.innerHTML = ''; clipboardContent.innerHTML = '';
var img = document.createElement('img'); var img = document.createElement('img');
@@ -176,6 +184,7 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
} }
// Select all data within the clipboard target // Select all data within the clipboard target
clipboardContent.focus();
selectAll(clipboardContent); selectAll(clipboardContent);
// Attempt to copy data from clipboard element into local clipboard // Attempt to copy data from clipboard element into local clipboard
@@ -399,53 +408,81 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
var originalElement = document.activeElement; var originalElement = document.activeElement;
pushSelection(); pushSelection();
/**
* Attempts to paste the clipboard contents into the
* currently-focused element. The promise related to the current
* attempt to read the clipboard will be resolved or rejected
* depending on whether the attempt to paste succeeds.
*/
var performPaste = function performPaste() {
// Attempt paste local clipboard into clipboard DOM element
if (document.execCommand('paste')) {
// If the pasted data is a single image, resolve with a blob
// containing that image
var currentImage = service.getImageContent(clipboardContent);
if (currentImage) {
// Convert the image's data URL into a blob
var blob = service.parseDataURL(currentImage);
if (blob) {
deferred.resolve(new ClipboardData({
type : blob.type,
data : blob
}));
}
// Reject if conversion fails
else
deferred.reject();
} // end if clipboard is an image
// Otherwise, assume the clipboard contains plain text
else
deferred.resolve(new ClipboardData({
type : 'text/plain',
data : clipboardContent.value
}));
}
// Otherwise, reading from the clipboard has failed
else
deferred.reject();
};
// Clean up event listener and selection once the paste attempt has
// completed
deferred.promise['finally'](function cleanupReadAttempt() {
// Do not use future changes in focus
clipboardContent.removeEventListener('focus', performPaste);
// Unfocus the clipboard DOM event to avoid mobile keyboard opening,
// restoring whichever element was originally focused
clipboardContent.blur();
originalElement.focus();
popSelection();
});
// Ensure clipboard element is blurred (and that the "focus" event
// will fire)
clipboardContent.blur();
clipboardContent.addEventListener('focus', performPaste);
// Clear and select the clipboard DOM element // Clear and select the clipboard DOM element
clipboardContent.innerHTML = ''; clipboardContent.value = '';
clipboardContent.focus(); clipboardContent.focus();
selectAll(clipboardContent); selectAll(clipboardContent);
// Attempt paste local clipboard into clipboard DOM element // If focus failed to be set, we cannot read the clipboard
if (document.activeElement === clipboardContent && document.execCommand('paste')) { if (document.activeElement !== clipboardContent)
// If the pasted data is a single image, resolve with a blob
// containing that image
var currentImage = service.getImageContent(clipboardContent);
if (currentImage) {
// Convert the image's data URL into a blob
var blob = service.parseDataURL(currentImage);
if (blob) {
deferred.resolve(new ClipboardData({
type : blob.type,
data : blob
}));
}
// Reject if conversion fails
else
deferred.reject();
} // end if clipboard is an image
// Otherwise, assume the clipboard contains plain text
else
deferred.resolve(new ClipboardData({
type : 'text/plain',
data : clipboardContent.textContent
}));
}
// Otherwise, reading from the clipboard has failed
else
deferred.reject(); deferred.reject();
// Unfocus the clipboard DOM event to avoid mobile keyboard opening,
// restoring whichever element was originally focused
clipboardContent.blur();
originalElement.focus();
popSelection();
}, CLIPBOARD_READ_DELAY); }, CLIPBOARD_READ_DELAY);
return deferred.promise; return deferred.promise;

View File

@@ -52,10 +52,10 @@
.clipboard-service-target { .clipboard-service-target {
position: fixed; position: fixed;
left: -1px; left: -1em;
right: -1px; right: -1em;
width: 1px; width: 1em;
height: 1px; height: 1em;
white-space: pre; white-space: pre;
overflow: hidden; overflow: hidden;
} }