mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUACAMOLE-55: Update clipboardService to support non-text contents.
This commit is contained in:
@@ -113,160 +113,6 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
*/
|
*/
|
||||||
var element = $element[0];
|
var element = $element[0];
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies the contents of the given element such that it contains
|
|
||||||
* only plain text. All non-text child elements will be stripped and
|
|
||||||
* replaced with their text equivalents. As this function performs the
|
|
||||||
* conversion through incremental changes only, cursor position within
|
|
||||||
* the given element is preserved.
|
|
||||||
*
|
|
||||||
* @param {Element} element
|
|
||||||
* The elements whose contents should be converted to plain text.
|
|
||||||
*/
|
|
||||||
var convertToText = function convertToText(element) {
|
|
||||||
|
|
||||||
// For each child of the given element
|
|
||||||
var current = element.firstChild;
|
|
||||||
while (current) {
|
|
||||||
|
|
||||||
// Preserve the next child in the list, in case the current
|
|
||||||
// node is replaced
|
|
||||||
var next = current.nextSibling;
|
|
||||||
|
|
||||||
// If the child is not already a text node, replace it with its
|
|
||||||
// own text contents
|
|
||||||
if (current.nodeType !== Node.TEXT_NODE) {
|
|
||||||
var textNode = document.createTextNode(current.textContent);
|
|
||||||
current.parentElement.replaceChild(textNode, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to next child
|
|
||||||
current = next;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the given data URL, returning its decoded contents as a new
|
|
||||||
* Blob. If the URL is not a valid data URL, null will be returned
|
|
||||||
* instead.
|
|
||||||
*
|
|
||||||
* @param {String} url
|
|
||||||
* The data URL to parse.
|
|
||||||
*
|
|
||||||
* @returns {Blob}
|
|
||||||
* A new Blob containing the decoded contents of the data URL, or
|
|
||||||
* null if the URL is not a valid data URL.
|
|
||||||
*/
|
|
||||||
var parseDataURL = function parseDataURL(url) {
|
|
||||||
|
|
||||||
// Parse given string as a data URL
|
|
||||||
var result = /^data:([^;]*);base64,([a-zA-Z0-9+/]*[=]*)$/.exec(url);
|
|
||||||
if (!result)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Pull the mimetype and base64 contents of the data URL
|
|
||||||
var type = result[1];
|
|
||||||
var data = $window.atob(result[2]);
|
|
||||||
|
|
||||||
// Convert the decoded binary string into a typed array
|
|
||||||
var buffer = new Uint8Array(data.length);
|
|
||||||
for (var i = 0; i < data.length; i++)
|
|
||||||
buffer[i] = data.charCodeAt(i);
|
|
||||||
|
|
||||||
// Produce a proper blob containing the data and type provided in
|
|
||||||
// the data URL
|
|
||||||
return new Blob([buffer], { type : type });
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the current text content of the given element with the
|
|
||||||
* given text. To avoid affecting the position of the cursor within an
|
|
||||||
* editable element, or firing unnecessary DOM modification events, the
|
|
||||||
* underlying <code>textContent</code> property of the element is only
|
|
||||||
* touched if doing so would actually change the text.
|
|
||||||
*
|
|
||||||
* @param {Element} element
|
|
||||||
* The element whose text content should be changed.
|
|
||||||
*
|
|
||||||
* @param {String} text
|
|
||||||
* The text content to assign to the given element.
|
|
||||||
*/
|
|
||||||
var setTextContent = function setTextContent(element, text) {
|
|
||||||
|
|
||||||
// Strip out any non-text content while preserving cursor position
|
|
||||||
convertToText(element);
|
|
||||||
|
|
||||||
// Reset text content only if doing so will actually change the content
|
|
||||||
if (element.textContent !== text)
|
|
||||||
element.textContent = text;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the URL of the single image within the given element, if the
|
|
||||||
* element truly contains only one child and that child is an image. If
|
|
||||||
* the content of the element is mixed or not an image, null is
|
|
||||||
* returned.
|
|
||||||
*
|
|
||||||
* @param {Element} element
|
|
||||||
* The element whose image content should be retrieved.
|
|
||||||
*
|
|
||||||
* @returns {String}
|
|
||||||
* The URL of the image contained within the given element, if that
|
|
||||||
* element contains only a single child element which happens to be
|
|
||||||
* an image, or null if the content of the element is not purely an
|
|
||||||
* image.
|
|
||||||
*/
|
|
||||||
var getImageContent = function getImageContent(element) {
|
|
||||||
|
|
||||||
// Return the source of the single child element, if it is an image
|
|
||||||
var firstChild = element.firstChild;
|
|
||||||
if (firstChild && firstChild.nodeName === 'IMG' && !firstChild.nextSibling)
|
|
||||||
return firstChild.getAttribute('src');
|
|
||||||
|
|
||||||
// Otherwise, the content of this element is not simply an image
|
|
||||||
return null;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the current contents of the given element with a single
|
|
||||||
* image having the given URL. To avoid affecting the position of the
|
|
||||||
* cursor within an editable element, or firing unnecessary DOM
|
|
||||||
* modification events, the content of the element is only touched if
|
|
||||||
* doing so would actually change content.
|
|
||||||
*
|
|
||||||
* @param {Element} element
|
|
||||||
* The element whose image content should be changed.
|
|
||||||
*
|
|
||||||
* @param {String} url
|
|
||||||
* The URL of the image which should be assigned as the contents of
|
|
||||||
* the given element.
|
|
||||||
*/
|
|
||||||
var setImageContent = function setImageContent(element, url) {
|
|
||||||
|
|
||||||
// Retrieve the URL of the current image contents, if any
|
|
||||||
var currentImage = getImageContent(element);
|
|
||||||
|
|
||||||
// If the current contents are not the given image (or not an image
|
|
||||||
// at all), reassign the contents
|
|
||||||
if (currentImage !== url) {
|
|
||||||
|
|
||||||
// Clear current contents
|
|
||||||
element.innerHTML = '';
|
|
||||||
|
|
||||||
// Add a new image as the sole contents of the element
|
|
||||||
var img = document.createElement('img');
|
|
||||||
img.src = url;
|
|
||||||
element.appendChild(img);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Intercept paste events, handling image data specifically
|
// Intercept paste events, handling image data specifically
|
||||||
element.addEventListener('paste', function dataPasted(e) {
|
element.addEventListener('paste', function dataPasted(e) {
|
||||||
|
|
||||||
@@ -320,11 +166,11 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
|
|
||||||
// If the clipboard contains a single image, parse and assign the
|
// If the clipboard contains a single image, parse and assign the
|
||||||
// image data to the internal clipboard
|
// image data to the internal clipboard
|
||||||
var currentImage = getImageContent(element);
|
var currentImage = clipboardService.getImageContent(element);
|
||||||
if (currentImage) {
|
if (currentImage) {
|
||||||
|
|
||||||
// Convert the image's data URL into a blob
|
// Convert the image's data URL into a blob
|
||||||
var blob = parseDataURL(currentImage);
|
var blob = clipboardService.parseDataURL(currentImage);
|
||||||
if (blob) {
|
if (blob) {
|
||||||
|
|
||||||
// Complete the assignment if conversion was successful
|
// Complete the assignment if conversion was successful
|
||||||
@@ -372,7 +218,7 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
|
|
||||||
// If the clipboard data is a string, render it as text
|
// If the clipboard data is a string, render it as text
|
||||||
if (typeof data.data === 'string')
|
if (typeof data.data === 'string')
|
||||||
setTextContent(element, data.data);
|
clipboardService.setTextContent(element, data.data);
|
||||||
|
|
||||||
// Render Blob/File contents based on mimetype
|
// Render Blob/File contents based on mimetype
|
||||||
else if (data.data instanceof Blob) {
|
else if (data.data instanceof Blob) {
|
||||||
@@ -380,7 +226,7 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
// If the copied data was an image, display it as such
|
// If the copied data was an image, display it as such
|
||||||
if (/^image\//.exec(data.type)) {
|
if (/^image\//.exec(data.type)) {
|
||||||
reader.onload = function updateImageURL() {
|
reader.onload = function updateImageURL() {
|
||||||
setImageContent(element, reader.result);
|
clipboardService.setImageContent(element, reader.result);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(data.data);
|
reader.readAsDataURL(data.data);
|
||||||
}
|
}
|
||||||
|
@@ -24,36 +24,134 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
function clipboardService($injector) {
|
function clipboardService($injector) {
|
||||||
|
|
||||||
// Get required services
|
// Get required services
|
||||||
var $q = $injector.get('$q');
|
var $q = $injector.get('$q');
|
||||||
|
var $window = $injector.get('$window');
|
||||||
|
|
||||||
|
// Required types
|
||||||
|
var ClipboardData = $injector.get('ClipboardData');
|
||||||
|
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A div which is used to hide the clipboard textarea and remove it from
|
* Reference to the window.document object.
|
||||||
* document flow.
|
|
||||||
*
|
*
|
||||||
* @type Element
|
* @private
|
||||||
|
* @type HTMLDocument
|
||||||
*/
|
*/
|
||||||
var clipElement = document.createElement('div');
|
var document = $window.document;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The textarea that will be used to hold the local clipboard contents.
|
* The textarea that will be used to hold the local clipboard contents.
|
||||||
*
|
*
|
||||||
* @type Element
|
* @type Element
|
||||||
*/
|
*/
|
||||||
var clipboardContent = document.createElement('textarea');
|
var clipboardContent = document.createElement('div');
|
||||||
|
|
||||||
// Ensure textarea is selectable but not visible
|
// Ensure clipboard target is selectable but not visible
|
||||||
clipElement.appendChild(clipboardContent);
|
clipboardContent.setAttribute('contenteditable', 'true');
|
||||||
clipElement.style.position = 'fixed';
|
clipboardContent.className = 'clipboard-service-target';
|
||||||
clipElement.style.width = '1px';
|
|
||||||
clipElement.style.height = '1px';
|
|
||||||
clipElement.style.left = '-1px';
|
|
||||||
clipElement.style.top = '-1px';
|
|
||||||
clipElement.style.overflow = 'hidden';
|
|
||||||
|
|
||||||
// Add textarea to DOM
|
// Add clipboard target to DOM
|
||||||
document.body.appendChild(clipElement);
|
document.body.appendChild(clipboardContent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stack of past node selection ranges. A range convering the nodes
|
||||||
|
* currently selected within the document can be pushed onto this stack
|
||||||
|
* with pushSelection(), and the most recently pushed selection can be
|
||||||
|
* popped off the stack (and thus re-selected) with popSelection().
|
||||||
|
*
|
||||||
|
* @type Range[]
|
||||||
|
*/
|
||||||
|
var selectionStack = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the current selection range to the selection stack such that it
|
||||||
|
* can later be restored with popSelection().
|
||||||
|
*/
|
||||||
|
var pushSelection = function pushSelection() {
|
||||||
|
|
||||||
|
// Add a range representing the current selection to the stack
|
||||||
|
var selection = $window.getSelection();
|
||||||
|
if (selection.getRangeAt && selection.rangeCount)
|
||||||
|
selectionStack.push(selection.getRangeAt(0));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops a selection range off the selection stack restoring the document's
|
||||||
|
* previous selection state. The selection range will be the most recent
|
||||||
|
* selection range pushed by pushSelection(). If there are no selection
|
||||||
|
* ranges currently on the stack, this function has no effect.
|
||||||
|
*/
|
||||||
|
var popSelection = function popSelection() {
|
||||||
|
|
||||||
|
// Pull one selection range from the stack
|
||||||
|
var range = selectionStack.pop();
|
||||||
|
if (!range)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Replace any current selection with the retrieved selection
|
||||||
|
var selection = $window.getSelection();
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects all nodes within the given element. This will replace the
|
||||||
|
* current selection with a new selection range that covers the element's
|
||||||
|
* contents. If the original selection should be preserved, use
|
||||||
|
* pushSelection() and popSelection().
|
||||||
|
*
|
||||||
|
* @param {Element} element
|
||||||
|
* The element whose contents should be selected.
|
||||||
|
*/
|
||||||
|
var selectAll = function selectAll(element) {
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the contents of the given element such that it contains only
|
||||||
|
* plain text. All non-text child elements will be stripped and replaced
|
||||||
|
* with their text equivalents. As this function performs the conversion
|
||||||
|
* through incremental changes only, cursor position within the given
|
||||||
|
* element is preserved.
|
||||||
|
*
|
||||||
|
* @param {Element} element
|
||||||
|
* The elements whose contents should be converted to plain text.
|
||||||
|
*/
|
||||||
|
var convertToText = function convertToText(element) {
|
||||||
|
|
||||||
|
// For each child of the given element
|
||||||
|
var current = element.firstChild;
|
||||||
|
while (current) {
|
||||||
|
|
||||||
|
// Preserve the next child in the list, in case the current
|
||||||
|
// node is replaced
|
||||||
|
var next = current.nextSibling;
|
||||||
|
|
||||||
|
// If the child is not already a text node, replace it with its
|
||||||
|
// own text contents
|
||||||
|
if (current.nodeType !== Node.TEXT_NODE) {
|
||||||
|
var textNode = document.createTextNode(current.textContent);
|
||||||
|
current.parentElement.replaceChild(textNode, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to next child
|
||||||
|
current = next;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the local clipboard, if possible, to the given text.
|
* Sets the local clipboard, if possible, to the given text.
|
||||||
@@ -71,30 +169,20 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
|
|
||||||
// Track the originally-focused element prior to changing focus
|
// Track the originally-focused element prior to changing focus
|
||||||
var originalElement = document.activeElement;
|
var originalElement = document.activeElement;
|
||||||
|
pushSelection();
|
||||||
|
|
||||||
// Copy the given value into the clipboard DOM element
|
// Copy the given value into the clipboard DOM element
|
||||||
clipboardContent.value = 'X';
|
if (typeof data.data === 'string')
|
||||||
clipboardContent.select();
|
clipboardContent.textContent = data.data;
|
||||||
|
else {
|
||||||
|
clipboardContent.innerHTML = '';
|
||||||
|
var img = document.createElement('img');
|
||||||
|
img.src = URL.createObjectURL(data.data);
|
||||||
|
clipboardContent.appendChild(img);
|
||||||
|
}
|
||||||
|
|
||||||
// Override copied contents of clipboard with the provided value
|
// Select all data within the clipboard target
|
||||||
clipboardContent.oncopy = function overrideContent(e) {
|
selectAll(clipboardContent);
|
||||||
|
|
||||||
// Override the contents of the clipboard
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Remove anything already present within the clipboard
|
|
||||||
var items = e.clipboardData.items;
|
|
||||||
items.clear();
|
|
||||||
|
|
||||||
// If the provided data is a string, add it as such
|
|
||||||
if (typeof data.data === 'string')
|
|
||||||
items.add(data.data, data.type);
|
|
||||||
|
|
||||||
// Otherwise, add as a File
|
|
||||||
else
|
|
||||||
items.add(new File([data.data], 'data', { type : data.type }));
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attempt to copy data from clipboard element into local clipboard
|
// Attempt to copy data from clipboard element into local clipboard
|
||||||
if (document.execCommand('copy'))
|
if (document.execCommand('copy'))
|
||||||
@@ -106,10 +194,128 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
// restoring whichever element was originally focused
|
// restoring whichever element was originally focused
|
||||||
clipboardContent.blur();
|
clipboardContent.blur();
|
||||||
originalElement.focus();
|
originalElement.focus();
|
||||||
|
popSelection();
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given data URL, returning its decoded contents as a new Blob.
|
||||||
|
* If the URL is not a valid data URL, null will be returned instead.
|
||||||
|
*
|
||||||
|
* @param {String} url
|
||||||
|
* The data URL to parse.
|
||||||
|
*
|
||||||
|
* @returns {Blob}
|
||||||
|
* A new Blob containing the decoded contents of the data URL, or null
|
||||||
|
* if the URL is not a valid data URL.
|
||||||
|
*/
|
||||||
|
service.parseDataURL = function parseDataURL(url) {
|
||||||
|
|
||||||
|
// Parse given string as a data URL
|
||||||
|
var result = /^data:([^;]*);base64,([a-zA-Z0-9+/]*[=]*)$/.exec(url);
|
||||||
|
if (!result)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Pull the mimetype and base64 contents of the data URL
|
||||||
|
var type = result[1];
|
||||||
|
var data = $window.atob(result[2]);
|
||||||
|
|
||||||
|
// Convert the decoded binary string into a typed array
|
||||||
|
var buffer = new Uint8Array(data.length);
|
||||||
|
for (var i = 0; i < data.length; i++)
|
||||||
|
buffer[i] = data.charCodeAt(i);
|
||||||
|
|
||||||
|
// Produce a proper blob containing the data and type provided in
|
||||||
|
// the data URL
|
||||||
|
return new Blob([buffer], { type : type });
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the current text content of the given element with the given
|
||||||
|
* text. To avoid affecting the position of the cursor within an editable
|
||||||
|
* element, or firing unnecessary DOM modification events, the underlying
|
||||||
|
* <code>textContent</code> property of the element is only touched if
|
||||||
|
* doing so would actually change the text.
|
||||||
|
*
|
||||||
|
* @param {Element} element
|
||||||
|
* The element whose text content should be changed.
|
||||||
|
*
|
||||||
|
* @param {String} text
|
||||||
|
* The text content to assign to the given element.
|
||||||
|
*/
|
||||||
|
service.setTextContent = function setTextContent(element, text) {
|
||||||
|
|
||||||
|
// Strip out any non-text content while preserving cursor position
|
||||||
|
convertToText(element);
|
||||||
|
|
||||||
|
// Reset text content only if doing so will actually change the content
|
||||||
|
if (element.textContent !== text)
|
||||||
|
element.textContent = text;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of the single image within the given element, if the
|
||||||
|
* element truly contains only one child and that child is an image. If the
|
||||||
|
* content of the element is mixed or not an image, null is returned.
|
||||||
|
*
|
||||||
|
* @param {Element} element
|
||||||
|
* The element whose image content should be retrieved.
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
* The URL of the image contained within the given element, if that
|
||||||
|
* element contains only a single child element which happens to be an
|
||||||
|
* image, or null if the content of the element is not purely an image.
|
||||||
|
*/
|
||||||
|
service.getImageContent = function getImageContent(element) {
|
||||||
|
|
||||||
|
// Return the source of the single child element, if it is an image
|
||||||
|
var firstChild = element.firstChild;
|
||||||
|
if (firstChild && firstChild.nodeName === 'IMG' && !firstChild.nextSibling)
|
||||||
|
return firstChild.getAttribute('src');
|
||||||
|
|
||||||
|
// Otherwise, the content of this element is not simply an image
|
||||||
|
return null;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the current contents of the given element with a single image
|
||||||
|
* having the given URL. To avoid affecting the position of the cursor
|
||||||
|
* within an editable element, or firing unnecessary DOM modification
|
||||||
|
* events, the content of the element is only touched if doing so would
|
||||||
|
* actually change content.
|
||||||
|
*
|
||||||
|
* @param {Element} element
|
||||||
|
* The element whose image content should be changed.
|
||||||
|
*
|
||||||
|
* @param {String} url
|
||||||
|
* The URL of the image which should be assigned as the contents of the
|
||||||
|
* given element.
|
||||||
|
*/
|
||||||
|
service.setImageContent = function setImageContent(element, url) {
|
||||||
|
|
||||||
|
// Retrieve the URL of the current image contents, if any
|
||||||
|
var currentImage = service.getImageContent(element);
|
||||||
|
|
||||||
|
// If the current contents are not the given image (or not an image
|
||||||
|
// at all), reassign the contents
|
||||||
|
if (currentImage !== url) {
|
||||||
|
|
||||||
|
// Clear current contents
|
||||||
|
element.innerHTML = '';
|
||||||
|
|
||||||
|
// Add a new image as the sole contents of the element
|
||||||
|
var img = document.createElement('img');
|
||||||
|
img.src = url;
|
||||||
|
element.appendChild(img);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current value of the local clipboard.
|
* Get the current value of the local clipboard.
|
||||||
*
|
*
|
||||||
@@ -124,24 +330,50 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
|
|
||||||
// Wait for the next event queue run before attempting to read
|
// Wait for the next event queue run before attempting to read
|
||||||
// clipboard data (in case the copy/cut has not yet completed)
|
// clipboard data (in case the copy/cut has not yet completed)
|
||||||
window.setTimeout(function deferredClipboardRead() {
|
$window.setTimeout(function deferredClipboardRead() {
|
||||||
|
|
||||||
// Track the originally-focused element prior to changing focus
|
// Track the originally-focused element prior to changing focus
|
||||||
var originalElement = document.activeElement;
|
var originalElement = document.activeElement;
|
||||||
|
pushSelection();
|
||||||
|
|
||||||
// Clear and select the clipboard DOM element
|
// Clear and select the clipboard DOM element
|
||||||
clipboardContent.value = '';
|
clipboardContent.innerHTML = '';
|
||||||
clipboardContent.focus();
|
clipboardContent.focus();
|
||||||
clipboardContent.select();
|
selectAll(clipboardContent);
|
||||||
|
|
||||||
// FIXME: Only handling text data
|
|
||||||
|
|
||||||
// Attempt paste local clipboard into clipboard DOM element
|
// Attempt paste local clipboard into clipboard DOM element
|
||||||
if (document.activeElement === clipboardContent && document.execCommand('paste'))
|
if (document.activeElement === clipboardContent && document.execCommand('paste')) {
|
||||||
deferred.resolve(new ClipboardData({
|
|
||||||
type : 'text/plain',
|
// If the pasted data is a single image, resolve with a blob
|
||||||
data : clipboardContent.value
|
// 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
|
else
|
||||||
deferred.reject();
|
deferred.reject();
|
||||||
|
|
||||||
@@ -149,6 +381,7 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
// restoring whichever element was originally focused
|
// restoring whichever element was originally focused
|
||||||
clipboardContent.blur();
|
clipboardContent.blur();
|
||||||
originalElement.focus();
|
originalElement.focus();
|
||||||
|
popSelection();
|
||||||
|
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
@@ -48,3 +48,12 @@
|
|||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
background: url('images/checker.png');
|
background: url('images/checker.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clipboard-service-target {
|
||||||
|
position: fixed;
|
||||||
|
left: -1px;
|
||||||
|
right: -1px;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user