GUAC-1480: Use AllowClipboard and execCommand for local/remote clipboard 
integration.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/7ec53c94
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/7ec53c94
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/7ec53c94

Branch: refs/heads/master
Commit: 7ec53c94ce889b6f5bdcedb689f580dff0eb5fbe
Parents: 14889c7
Author: James Muehlner <[email protected]>
Authored: Mon Feb 1 22:03:44 2016 -0800
Committer: James Muehlner <[email protected]>
Committed: Mon Feb 1 22:03:44 2016 -0800

----------------------------------------------------------------------
 .../app/client/controllers/clientController.js  |  19 ++-
 .../app/client/services/clipboardService.js     | 171 +++++++++++++++++++
 .../webapp/app/client/types/ManagedClient.js    |   3 +
 3 files changed, 188 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/7ec53c94/guacamole/src/main/webapp/app/client/controllers/clientController.js
----------------------------------------------------------------------
diff --git 
a/guacamole/src/main/webapp/app/client/controllers/clientController.js 
b/guacamole/src/main/webapp/app/client/controllers/clientController.js
index fd445d1..99bf723 100644
--- a/guacamole/src/main/webapp/app/client/controllers/clientController.js
+++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js
@@ -35,6 +35,7 @@ angular.module('client').controller('clientController', 
['$scope', '$routeParams
     // Required services
     var $location             = $injector.get('$location');
     var authenticationService = $injector.get('authenticationService');
+    var clipboardService      = $injector.get('clipboardService');
     var guacClientManager     = $injector.get('guacClientManager');
     var guacNotification      = $injector.get('guacNotification');
     var preferenceService     = $injector.get('preferenceService');
@@ -230,11 +231,6 @@ angular.module('client').controller('clientController', 
['$scope', '$routeParams
         $scope.menu.shown = false;
     };
 
-    // Update the model when clipboard data received from client
-    $scope.$on('guacClientClipboard', function clientClipboardListener(event, 
client, mimetype, clipboardData) {
-       $scope.clipboardData = clipboardData; 
-    });
-
     /**
      * The client which should be attached to the client UI.
      *
@@ -526,6 +522,19 @@ angular.module('client').controller('clientController', 
['$scope', '$routeParams
             });
         }
 
+        // Hide status and sync local clipboard once connected
+        else if (connectionState === 
ManagedClientState.ConnectionState.CONNECTED) {
+
+            // Sync with local clipboard
+            clipboardService.getLocalClipboard().then(function 
clipboardRead(data) {
+                $scope.$broadcast('guacClipboard', 'text/plain', data);
+            });
+
+            // Hide status notification
+            guacNotification.showStatus(false);
+
+        }
+
         // Hide status for all other states
         else
             guacNotification.showStatus(false);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/7ec53c94/guacamole/src/main/webapp/app/client/services/clipboardService.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/client/services/clipboardService.js 
b/guacamole/src/main/webapp/app/client/services/clipboardService.js
new file mode 100644
index 0000000..7880159
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/services/clipboardService.js
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * A service for accessing local clipboard data.
+ */
+angular.module('client').factory('clipboardService', ['$injector',
+        function clipboardService($injector) {
+
+    // Get required services
+    var $q         = $injector.get('$q');
+    var $rootScope = $injector.get('$rootScope');
+
+    var service = {};
+
+    /**
+     * A div which is used to hide the clipboard textarea and remove it from
+     * document flow.
+     *
+     * @type Element
+     */
+    var clipElement = document.createElement('div');
+
+    /**
+     * The textarea that will be used to hold the local clipboard contents.
+     *
+     * @type Element
+     */
+    var clipboardContent = document.createElement('textarea');
+
+    /**
+     * The contents of the last clipboard event broadcast by this service when
+     * the clipboard contents changed.
+     *
+     * @type String
+     */
+    var lastClipboardEvent = '';
+
+    clipElement.appendChild(clipboardContent);
+    clipElement.style.position = 'absolute';
+    clipElement.style.width = '0px';
+    clipElement.style.height = '0px';
+    clipElement.style.overflow = 'hidden';
+
+    document.body.appendChild(clipElement);
+
+    /**
+     * Sets the local clipboard, if possible, to the given text.
+     *
+     * @param {String} text
+     *     The text to which the local clipboard should be set.
+     *
+     * @return {Promise}
+     *     A promise that will resolve if setting the clipboard was successful,
+     *     and will reject if it failed.
+     */
+    service.setLocalClipboard = function setLocalClipboard(text) {
+
+        var deferred = $q.defer();
+
+        // First, see if allow clipboard extension is installed
+        if (window.AllowClipboard) {
+
+            var clipboardClient = new AllowClipboard.Client.ClipboardClient();
+
+            clipboardClient.write(text, function(success) {
+                if (success)
+                    deferred.resolve();
+                else
+                    deferred.reject();
+            });
+
+        }
+
+        // Otherwise, try execCommand
+        else {
+
+            // Copy the given value into the clipboard DOM element
+            clipboardContent.value = text;
+            clipboardContent.select();
+
+            // Attempt to copy data from clipboard element into local clipboard
+            if (document.execCommand('copy'))
+                deferred.resolve();
+            else
+                deferred.reject();
+
+        }
+
+        return deferred.promise;
+    };
+
+    /**
+     * Get the current value of the local clipboard.
+     *
+     * @return {Promise}
+     *     A promise that will resolve with the contents of the local clipboard
+     *     if getting the clipboard was successful, and will reject if it
+     *     failed.
+     */
+    service.getLocalClipboard = function getLocalClipboard() {
+
+        var deferred = $q.defer();
+
+        // First, see if allow clipboard extension is installed
+        if (window.AllowClipboard) {
+
+            var clipboardClient = new AllowClipboard.Client.ClipboardClient();
+
+            clipboardClient.read(function(success, data) {
+                if (success)
+                    deferred.resolve(data);
+                else
+                    deferred.reject();
+            });
+
+        }
+
+        // Otherwise, try execCommand
+        else {
+
+            // Clear and select the clipboard DOM element
+            clipboardContent.value = '';
+            clipboardContent.select();
+
+            // Attempt paste local clipboard into clipboard DOM element
+            if (document.execCommand('paste'))
+                deferred.resolve(clipboardContent.value);
+            else
+                deferred.reject();
+
+        }
+
+        return deferred.promise;
+    };
+
+    // Periodically attempt to read the clipboard, firing an event if 
successful
+    window.setInterval(function periodicallyReadClipboard() {
+        service.getLocalClipboard().then(function clipboardRead(data) {
+
+            // Fire clipboard event if the data has changed
+            if (data !== lastClipboardEvent) {
+               $rootScope.$broadcast('guacClipboard', 'text/plain', data);
+               lastClipboardEvent = data;
+            }
+
+        });
+    }, 100);
+
+    return service;
+
+}]);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/7ec53c94/guacamole/src/main/webapp/app/client/types/ManagedClient.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js 
b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
index a518ca8..5187cb0 100644
--- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
@@ -38,8 +38,10 @@ angular.module('client').factory('ManagedClient', 
['$rootScope', '$injector',
     // Required services
     var $document              = $injector.get('$document');
     var $q                     = $injector.get('$q');
+    var $rootScope             = $injector.get('$rootScope');
     var $window                = $injector.get('$window');
     var authenticationService  = $injector.get('authenticationService');
+    var clipboardService       = $injector.get('clipboardService');
     var connectionGroupService = $injector.get('connectionGroupService');
     var connectionService      = $injector.get('connectionService');
     var guacAudio              = $injector.get('guacAudio');
@@ -403,6 +405,7 @@ angular.module('client').factory('ManagedClient', 
['$rootScope', '$injector',
             reader.onend = function clipboard_text_end() {
                 $rootScope.$apply(function updateClipboard() {
                     managedClient.clipboardData = data;
+                    clipboardService.setLocalClipboard(data);
                 });
             };
 

Reply via email to