mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-724: Replace per-client clipboard with shared clipboard.
This commit is contained in:
@@ -207,6 +207,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
if (_.isEmpty(_.intersection(previousClients, $scope.clientGroup.clients)))
|
if (_.isEmpty(_.intersection(previousClients, $scope.clientGroup.clients)))
|
||||||
$scope.menu.shown = false;
|
$scope.menu.shown = false;
|
||||||
|
|
||||||
|
// Update newly-attached clients with current contents of clipboard
|
||||||
|
clipboardService.resyncClipboard();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,15 +344,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
*/
|
*/
|
||||||
var substituteKeysPressed = {};
|
var substituteKeysPressed = {};
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of all currently pressed keys (by keysym) to the clipboard contents
|
|
||||||
* received from the remote desktop while those keys were pressed. All keys
|
|
||||||
* not currently pressed will not have entries within this map.
|
|
||||||
*
|
|
||||||
* @type Object.<Number, ClipboardData>
|
|
||||||
*/
|
|
||||||
var clipboardDataFromKey = {};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check to see if all currently pressed keys are in the set of menu keys.
|
* Check to see if all currently pressed keys are in the set of menu keys.
|
||||||
*/
|
*/
|
||||||
@@ -486,11 +480,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
// Update client state/behavior as visibility of the Guacamole menu changes
|
// Update client state/behavior as visibility of the Guacamole menu changes
|
||||||
$scope.$watch('menu.shown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
|
$scope.$watch('menu.shown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
|
||||||
|
|
||||||
// Send clipboard and argument value data once menu is hidden
|
// Send any argument value data once menu is hidden
|
||||||
if (!menuShown && menuShownPreviousState) {
|
if (!menuShown && menuShownPreviousState)
|
||||||
$scope.$broadcast('guacClipboard', $scope.client.clipboardData);
|
|
||||||
$scope.applyParameterChanges();
|
$scope.applyParameterChanges();
|
||||||
}
|
|
||||||
|
|
||||||
// Disable client keyboard if the menu is shown
|
// Disable client keyboard if the menu is shown
|
||||||
angular.forEach($scope.clientGroup.clients, function updateKeyboardEnabled(client) {
|
angular.forEach($scope.clientGroup.clients, function updateKeyboardEnabled(client) {
|
||||||
@@ -521,19 +513,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
iconService.setIcons(canvas);
|
iconService.setIcons(canvas);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch clipboard for new data, associating it with any pressed keys
|
|
||||||
$scope.$watch('client.clipboardData', function clipboardChanged(data) {
|
|
||||||
|
|
||||||
// Sync local clipboard as long as the menu is not open
|
|
||||||
if (!$scope.menu.shown)
|
|
||||||
clipboardService.setLocalClipboard(data)['catch'](angular.noop);
|
|
||||||
|
|
||||||
// Associate new clipboard data with any currently-pressed key
|
|
||||||
for (var keysym in keysCurrentlyPressed)
|
|
||||||
clipboardDataFromKey[keysym] = data;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pull sharing profiles once the tunnel UUID is known
|
// Pull sharing profiles once the tunnel UUID is known
|
||||||
$scope.$watch('focusedClient.tunnel.uuid', function retrieveSharingProfiles(uuid) {
|
$scope.$watch('focusedClient.tunnel.uuid', function retrieveSharingProfiles(uuid) {
|
||||||
|
|
||||||
@@ -651,16 +630,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update pressed keys as they are released, synchronizing the clipboard
|
// Update pressed keys as they are released
|
||||||
// with any data that appears to have come from those key presses
|
|
||||||
$scope.$on('guacKeyup', function keyupListener(event, keysym, keyboard) {
|
$scope.$on('guacKeyup', function keyupListener(event, keysym, keyboard) {
|
||||||
|
|
||||||
// Sync local clipboard with any clipboard data received while this
|
|
||||||
// key was pressed (if any) as long as the menu is not open
|
|
||||||
var clipboardData = clipboardDataFromKey[keysym];
|
|
||||||
if (clipboardData && !$scope.menu.shown)
|
|
||||||
clipboardService.setLocalClipboard(clipboardData)['catch'](angular.noop);
|
|
||||||
|
|
||||||
// Deal with substitute key presses
|
// Deal with substitute key presses
|
||||||
if (substituteKeysPressed[keysym]) {
|
if (substituteKeysPressed[keysym]) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -669,10 +641,8 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mark key as released
|
// Mark key as released
|
||||||
else {
|
else
|
||||||
delete clipboardDataFromKey[keysym];
|
|
||||||
delete keysCurrentlyPressed[keysym];
|
delete keysCurrentlyPressed[keysym];
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -413,10 +413,7 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
|
|
||||||
// Update remote clipboard if local clipboard changes
|
// Update remote clipboard if local clipboard changes
|
||||||
$scope.$on('guacClipboard', function onClipboard(event, data) {
|
$scope.$on('guacClipboard', function onClipboard(event, data) {
|
||||||
if (client) {
|
ManagedClient.setClipboard($scope.client, data);
|
||||||
ManagedClient.setClipboard($scope.client, data);
|
|
||||||
$scope.client.clipboardData = data;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Translate local keydown events to remote keydown events if keyboard is enabled
|
// Translate local keydown events to remote keydown events if keyboard is enabled
|
||||||
|
@@ -305,23 +305,6 @@ angular.module('client').directive('guacClientNotification', [function guacClien
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide status and sync local clipboard once connected
|
|
||||||
else if (connectionState === ManagedClientState.ConnectionState.CONNECTED) {
|
|
||||||
|
|
||||||
// TODO: Move clipboard sync elsewhere
|
|
||||||
|
|
||||||
// Sync with local clipboard
|
|
||||||
/*
|
|
||||||
clipboardService.getLocalClipboard().then(function clipboardRead(data) {
|
|
||||||
$scope.$broadcast('guacClipboard', data);
|
|
||||||
}, angular.noop);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Hide status notification
|
|
||||||
$scope.status = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide status for all other states
|
// Hide status for all other states
|
||||||
else
|
else
|
||||||
$scope.status = false;
|
$scope.status = false;
|
||||||
|
@@ -100,7 +100,7 @@
|
|||||||
<h3>{{'CLIENT.SECTION_HEADER_CLIPBOARD' | translate}}</h3>
|
<h3>{{'CLIENT.SECTION_HEADER_CLIPBOARD' | translate}}</h3>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p class="description">{{'CLIENT.HELP_CLIPBOARD' | translate}}</p>
|
<p class="description">{{'CLIENT.HELP_CLIPBOARD' | translate}}</p>
|
||||||
<guac-clipboard data="client.clipboardData"></guac-clipboard>
|
<guac-clipboard></guac-clipboard>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -42,6 +42,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
var $window = $injector.get('$window');
|
var $window = $injector.get('$window');
|
||||||
var activeConnectionService = $injector.get('activeConnectionService');
|
var activeConnectionService = $injector.get('activeConnectionService');
|
||||||
var authenticationService = $injector.get('authenticationService');
|
var authenticationService = $injector.get('authenticationService');
|
||||||
|
var clipboardService = $injector.get('clipboardService');
|
||||||
var connectionGroupService = $injector.get('connectionGroupService');
|
var connectionGroupService = $injector.get('connectionGroupService');
|
||||||
var connectionService = $injector.get('connectionService');
|
var connectionService = $injector.get('connectionService');
|
||||||
var preferenceService = $injector.get('preferenceService');
|
var preferenceService = $injector.get('preferenceService');
|
||||||
@@ -146,16 +147,6 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
*/
|
*/
|
||||||
this.thumbnail = template.thumbnail;
|
this.thumbnail = template.thumbnail;
|
||||||
|
|
||||||
/**
|
|
||||||
* The current clipboard contents.
|
|
||||||
*
|
|
||||||
* @type ClipboardData
|
|
||||||
*/
|
|
||||||
this.clipboardData = template.clipboardData || new ClipboardData({
|
|
||||||
type : 'text/plain',
|
|
||||||
data : ''
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current state of all parameters requested by the server via
|
* The current state of all parameters requested by the server via
|
||||||
* "required" instructions, where each object key is the name of a
|
* "required" instructions, where each object key is the name of a
|
||||||
@@ -448,9 +439,10 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
ManagedClientState.setConnectionState(managedClient.clientState,
|
ManagedClientState.setConnectionState(managedClient.clientState,
|
||||||
ManagedClientState.ConnectionState.CONNECTED);
|
ManagedClientState.ConnectionState.CONNECTED);
|
||||||
|
|
||||||
// Send any clipboard data already provided
|
// Sync current clipboard data
|
||||||
if (managedClient.clipboardData)
|
clipboardService.getClipboard().then((data) => {
|
||||||
ManagedClient.setClipboard(managedClient, managedClient.clipboardData);
|
ManagedClient.setClipboard(managedClient, data);
|
||||||
|
}, angular.noop);
|
||||||
|
|
||||||
// Begin streaming audio input if possible
|
// Begin streaming audio input if possible
|
||||||
requestAudioStream(client);
|
requestAudioStream(client);
|
||||||
@@ -545,12 +537,11 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
|
|
||||||
// Set clipboard contents once stream is finished
|
// Set clipboard contents once stream is finished
|
||||||
reader.onend = function textComplete() {
|
reader.onend = function textComplete() {
|
||||||
$rootScope.$apply(function updateClipboard() {
|
clipboardService.setClipboard(new ClipboardData({
|
||||||
managedClient.clipboardData = new ClipboardData({
|
source : managedClient.id,
|
||||||
type : mimetype,
|
type : mimetype,
|
||||||
data : data
|
data : data
|
||||||
});
|
}), managedClient)['catch'](angular.noop);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -559,12 +550,11 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
else {
|
else {
|
||||||
reader = new Guacamole.BlobReader(stream, mimetype);
|
reader = new Guacamole.BlobReader(stream, mimetype);
|
||||||
reader.onend = function blobComplete() {
|
reader.onend = function blobComplete() {
|
||||||
$rootScope.$apply(function updateClipboard() {
|
clipboardService.setClipboard(new ClipboardData({
|
||||||
managedClient.clipboardData = new ClipboardData({
|
source : managedClient.id,
|
||||||
type : mimetype,
|
type : mimetype,
|
||||||
data : reader.getBlob()
|
data : reader.getBlob()
|
||||||
});
|
}), managedClient)['catch'](angular.noop);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,7 +683,9 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the given clipboard data over the given Guacamole client, setting
|
* Sends the given clipboard data over the given Guacamole client, setting
|
||||||
* the contents of the remote clipboard to the data provided.
|
* the contents of the remote clipboard to the data provided. If the given
|
||||||
|
* clipboard data was originally received from that client, the data is
|
||||||
|
* ignored and this function has no effect.
|
||||||
*
|
*
|
||||||
* @param {ManagedClient} managedClient
|
* @param {ManagedClient} managedClient
|
||||||
* The ManagedClient over which the given clipboard data is to be sent.
|
* The ManagedClient over which the given clipboard data is to be sent.
|
||||||
@@ -703,6 +695,10 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
*/
|
*/
|
||||||
ManagedClient.setClipboard = function setClipboard(managedClient, data) {
|
ManagedClient.setClipboard = function setClipboard(managedClient, data) {
|
||||||
|
|
||||||
|
// Ignore clipboard data that was received from this connection
|
||||||
|
if (data.source === managedClient.id)
|
||||||
|
return;
|
||||||
|
|
||||||
var writer;
|
var writer;
|
||||||
|
|
||||||
// Create stream with proper mimetype
|
// Create stream with proper mimetype
|
||||||
|
@@ -18,10 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A directive provides an editor whose contents are exposed via a
|
* A directive provides an editor for the clipboard content maintained by
|
||||||
* ClipboardData object via the "data" attribute. If this data should also be
|
* clipboardService. Changes to the clipboard by clipboardService will
|
||||||
* synced to the local clipboard, or sent via a connected Guacamole client
|
* automatically be reflected in the editor, and changes in the editor will
|
||||||
* using a "guacClipboard" event, it is up to external code to do so.
|
* automatically be reflected in the clipboard by clipboardService.
|
||||||
*/
|
*/
|
||||||
angular.module('clipboard').directive('guacClipboard', ['$injector',
|
angular.module('clipboard').directive('guacClipboard', ['$injector',
|
||||||
function guacClipboard($injector) {
|
function guacClipboard($injector) {
|
||||||
@@ -29,6 +29,9 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
// Required types
|
// Required types
|
||||||
var ClipboardData = $injector.get('ClipboardData');
|
var ClipboardData = $injector.get('ClipboardData');
|
||||||
|
|
||||||
|
// Required services
|
||||||
|
var clipboardService = $injector.get('clipboardService');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration object for the guacClipboard directive.
|
* Configuration object for the guacClipboard directive.
|
||||||
*
|
*
|
||||||
@@ -40,20 +43,6 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
templateUrl : 'app/clipboard/templates/guacClipboard.html'
|
templateUrl : 'app/clipboard/templates/guacClipboard.html'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scope properties exposed by the guacClipboard directive
|
|
||||||
config.scope = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The data to display within the field provided by this directive. This
|
|
||||||
* data will modified or replaced when the user manually alters the
|
|
||||||
* contents of the field.
|
|
||||||
*
|
|
||||||
* @type ClipboardData
|
|
||||||
*/
|
|
||||||
data : '='
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// guacClipboard directive controller
|
// guacClipboard directive controller
|
||||||
config.controller = ['$scope', '$injector', '$element',
|
config.controller = ['$scope', '$injector', '$element',
|
||||||
function guacClipboardController($scope, $injector, $element) {
|
function guacClipboardController($scope, $injector, $element) {
|
||||||
@@ -75,12 +64,27 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
var updateClipboardData = function updateClipboardData() {
|
var updateClipboardData = function updateClipboardData() {
|
||||||
|
|
||||||
// Read contents of clipboard textarea
|
// Read contents of clipboard textarea
|
||||||
$scope.$evalAsync(function assignClipboardText() {
|
clipboardService.setClipboard(new ClipboardData({
|
||||||
$scope.data = new ClipboardData({
|
type : 'text/plain',
|
||||||
type : 'text/plain',
|
data : element.value
|
||||||
data : element.value
|
}));
|
||||||
});
|
|
||||||
});
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the contents of the clipboard editor to the given data.
|
||||||
|
*
|
||||||
|
* @param {ClipboardData} data
|
||||||
|
* The ClipboardData to display within the clipboard editor for
|
||||||
|
* editing.
|
||||||
|
*/
|
||||||
|
var updateClipboardEditor = function updateClipboardEditor(data) {
|
||||||
|
|
||||||
|
// If the clipboard data is a string, render it as text
|
||||||
|
if (typeof data.data === 'string')
|
||||||
|
element.value = data.data;
|
||||||
|
|
||||||
|
// Ignore other data types for now
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,17 +93,15 @@ angular.module('clipboard').directive('guacClipboard', ['$injector',
|
|||||||
element.addEventListener('input', updateClipboardData);
|
element.addEventListener('input', updateClipboardData);
|
||||||
element.addEventListener('change', updateClipboardData);
|
element.addEventListener('change', updateClipboardData);
|
||||||
|
|
||||||
// Watch clipboard for new data, updating the clipboard textarea as
|
// Update remote clipboard if local clipboard changes
|
||||||
// necessary
|
$scope.$on('guacClipboard', function clipboardChanged(event, data) {
|
||||||
$scope.$watch('data', function clipboardDataChanged(data) {
|
updateClipboardEditor(data);
|
||||||
|
});
|
||||||
|
|
||||||
// If the clipboard data is a string, render it as text
|
// Init clipboard editor with current clipboard contents
|
||||||
if (typeof data.data === 'string')
|
clipboardService.getClipboard().then((data) => {
|
||||||
element.value = data.data;
|
updateClipboardEditor(data);
|
||||||
|
}, angular.noop);
|
||||||
// Ignore other data types for now
|
|
||||||
|
|
||||||
}); // end $scope.data watch
|
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@@ -18,18 +18,32 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service for accessing local clipboard data.
|
* A service for maintaining and accessing clipboard data. If possible, this
|
||||||
|
* service will leverage the local clipboard. If the local clipboard is not
|
||||||
|
* available, an internal in-memory clipboard will be used instead.
|
||||||
*/
|
*/
|
||||||
angular.module('clipboard').factory('clipboardService', ['$injector',
|
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');
|
var $window = $injector.get('$window');
|
||||||
|
var $rootScope = $injector.get('$rootScope');
|
||||||
|
var sessionStorageFactory = $injector.get('sessionStorageFactory');
|
||||||
|
|
||||||
// Required types
|
// Required types
|
||||||
var ClipboardData = $injector.get('ClipboardData');
|
var ClipboardData = $injector.get('ClipboardData');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter/setter which retrieves or sets the current stored clipboard
|
||||||
|
* contents. The stored clipboard contents are strictly internal to
|
||||||
|
* Guacamole, and may not reflect the local clipboard if local clipboard
|
||||||
|
* access is unavailable.
|
||||||
|
*
|
||||||
|
* @type Function
|
||||||
|
*/
|
||||||
|
var storedClipboardData = sessionStorageFactory.create(new ClipboardData());
|
||||||
|
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,7 +189,7 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
* A promise that will resolve if setting the clipboard was successful,
|
* A promise that will resolve if setting the clipboard was successful,
|
||||||
* and will reject if it failed.
|
* and will reject if it failed.
|
||||||
*/
|
*/
|
||||||
service.setLocalClipboard = function setLocalClipboard(data) {
|
var setLocalClipboard = function setLocalClipboard(data) {
|
||||||
|
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
@@ -423,7 +437,7 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
* if getting the clipboard was successful, and will reject if it
|
* if getting the clipboard was successful, and will reject if it
|
||||||
* failed.
|
* failed.
|
||||||
*/
|
*/
|
||||||
service.getLocalClipboard = function getLocalClipboard() {
|
var getLocalClipboard = function getLocalClipboard() {
|
||||||
|
|
||||||
// If the clipboard is already being read, do not overlap the read
|
// If the clipboard is already being read, do not overlap the read
|
||||||
// attempts; instead share the result across all requests
|
// attempts; instead share the result across all requests
|
||||||
@@ -548,6 +562,68 @@ angular.module('clipboard').factory('clipboardService', ['$injector',
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current value of the internal clipboard shared across all
|
||||||
|
* active Guacamole connections running within the current browser tab. If
|
||||||
|
* access to the local clipboard is available, the internal clipboard is
|
||||||
|
* first synchronized with the current local clipboard contents. If access
|
||||||
|
* to the local clipboard is unavailable, only the internal clipboard will
|
||||||
|
* be used.
|
||||||
|
*
|
||||||
|
* @return {Promise.<ClipboardData>}
|
||||||
|
* A promise that will resolve with the contents of the internal
|
||||||
|
* clipboard, first retrieving those contents from the local clipboard
|
||||||
|
* if permission to do so has been granted. This promise is always
|
||||||
|
* resolved.
|
||||||
|
*/
|
||||||
|
service.getClipboard = function getClipboard() {
|
||||||
|
return getLocalClipboard().then((data) => storedClipboardData(data), () => storedClipboardData());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content of the internal clipboard shared across all active
|
||||||
|
* Guacamole connections running within the current browser tab. If
|
||||||
|
* access to the local clipboard is available, the local clipboard is
|
||||||
|
* first set to the provided clipboard content. If access to the local
|
||||||
|
* clipboard is unavailable, only the internal clipboard will be used. A
|
||||||
|
* "guacClipboard" event will be broadcast with the assigned data once the
|
||||||
|
* operation has completed.
|
||||||
|
*
|
||||||
|
* @param {ClipboardData} data
|
||||||
|
* The data to assign to the clipboard.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
* A promise that will resolve after the clipboard content has been
|
||||||
|
* set. This promise is always resolved.
|
||||||
|
*/
|
||||||
|
service.setClipboard = function setClipboard(data) {
|
||||||
|
return setLocalClipboard(data).finally(() => {
|
||||||
|
|
||||||
|
// Update internal clipboard and broadcast event notifying of
|
||||||
|
// updated contents
|
||||||
|
storedClipboardData(data);
|
||||||
|
$rootScope.$broadcast('guacClipboard', data);
|
||||||
|
|
||||||
|
// Ensure promise is resolved (this function may be called from
|
||||||
|
// the promise rejection handler)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resynchronizes the local and internal clipboards, setting the contents
|
||||||
|
* of the internal clipboard to that of the local clipboard (if local
|
||||||
|
* clipboard access is granted) and broadcasting a "guacClipboard" event
|
||||||
|
* with the current internal clipboard contents for consumption by external
|
||||||
|
* components like the "guacClient" directive.
|
||||||
|
*/
|
||||||
|
service.resyncClipboard = function resyncClipboard() {
|
||||||
|
service.getClipboard().then(function clipboardRead(data) {
|
||||||
|
return service.setClipboard(data);
|
||||||
|
}, angular.noop);
|
||||||
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
@@ -36,6 +36,15 @@ angular.module('clipboard').factory('ClipboardData', [function defineClipboardDa
|
|||||||
// Use empty object by default
|
// Use empty object by default
|
||||||
template = template || {};
|
template = template || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the ManagedClient handling the remote desktop connection
|
||||||
|
* that originated this clipboard data, or null if the data originated
|
||||||
|
* from the clipboard editor or local clipboard.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.source = template.source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mimetype of the data currently stored within the clipboard.
|
* The mimetype of the data currently stored within the clipboard.
|
||||||
*
|
*
|
||||||
|
@@ -208,25 +208,15 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
keyboard.reset();
|
keyboard.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the clipboard data has changed, firing a new
|
|
||||||
* "guacClipboard" event if it has.
|
|
||||||
*/
|
|
||||||
var checkClipboard = function checkClipboard() {
|
|
||||||
clipboardService.getLocalClipboard().then(function clipboardRead(data) {
|
|
||||||
$scope.$broadcast('guacClipboard', data);
|
|
||||||
}, angular.noop);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attempt to read the clipboard if it may have changed
|
// Attempt to read the clipboard if it may have changed
|
||||||
$window.addEventListener('load', checkClipboard, true);
|
$window.addEventListener('load', clipboardService.resyncClipboard, true);
|
||||||
$window.addEventListener('copy', checkClipboard);
|
$window.addEventListener('copy', clipboardService.resyncClipboard);
|
||||||
$window.addEventListener('cut', checkClipboard);
|
$window.addEventListener('cut', clipboardService.resyncClipboard);
|
||||||
$window.addEventListener('focus', function focusGained(e) {
|
$window.addEventListener('focus', function focusGained(e) {
|
||||||
|
|
||||||
// Only recheck clipboard if it's the window itself that gained focus
|
// Only recheck clipboard if it's the window itself that gained focus
|
||||||
if (e.target === $window)
|
if (e.target === $window)
|
||||||
checkClipboard();
|
clipboardService.resyncClipboard();
|
||||||
|
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user