diff --git a/guacamole/pom.xml b/guacamole/pom.xml
index 9fa9fce54..12d6c5c59 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -327,12 +327,6 @@
1.3.16
runtime
-
- org.webjars.bower
- angular-cookies
- 1.3.16
- runtime
-
org.webjars.bower
angular-route
diff --git a/guacamole/src/main/webapp/app/auth/authModule.js b/guacamole/src/main/webapp/app/auth/authModule.js
index ff8851ee0..7faaf87f7 100644
--- a/guacamole/src/main/webapp/app/auth/authModule.js
+++ b/guacamole/src/main/webapp/app/auth/authModule.js
@@ -20,4 +20,6 @@
/**
* The module for authentication and management of tokens.
*/
-angular.module('auth', ['ngCookies']);
+angular.module('auth', [
+ 'storage'
+]);
diff --git a/guacamole/src/main/webapp/app/auth/service/authenticationService.js b/guacamole/src/main/webapp/app/auth/service/authenticationService.js
index 74f0570b4..2b64a5b81 100644
--- a/guacamole/src/main/webapp/app/auth/service/authenticationService.js
+++ b/guacamole/src/main/webapp/app/auth/service/authenticationService.js
@@ -46,10 +46,10 @@ angular.module('auth').factory('authenticationService', ['$injector',
var Error = $injector.get('Error');
// Required services
- var $cookieStore = $injector.get('$cookieStore');
- var $http = $injector.get('$http');
- var $q = $injector.get('$q');
- var $rootScope = $injector.get('$rootScope');
+ var $http = $injector.get('$http');
+ var $q = $injector.get('$q');
+ var $rootScope = $injector.get('$rootScope');
+ var localStorageService = $injector.get('localStorageService');
var service = {};
@@ -62,12 +62,12 @@ angular.module('auth').factory('authenticationService', ['$injector',
var cachedResult = null;
/**
- * The unique identifier of the local cookie which stores the result of the
- * last authentication attempt.
+ * The unique identifier of the local storage key which stores the result
+ * of the last authentication attempt.
*
* @type String
*/
- var AUTH_COOKIE_ID = "GUAC_AUTH";
+ var AUTH_STORAGE_KEY = 'GUAC_AUTH';
/**
* Retrieves the last successful authentication result. If the user has not
@@ -85,7 +85,7 @@ angular.module('auth').factory('authenticationService', ['$injector',
return cachedResult;
// Return explicit null if no auth data is currently stored
- var data = $cookieStore.get(AUTH_COOKIE_ID);
+ var data = localStorageService.getItem(AUTH_STORAGE_KEY);
if (!data)
return null;
@@ -107,7 +107,7 @@ angular.module('auth').factory('authenticationService', ['$injector',
// Clear the currently-stored result if the last attempt failed
if (!data) {
cachedResult = null;
- $cookieStore.remove(AUTH_COOKIE_ID);
+ localStorageService.removeItem(AUTH_STORAGE_KEY);
}
// Otherwise store the authentication attempt directly
@@ -116,9 +116,9 @@ angular.module('auth').factory('authenticationService', ['$injector',
// Always store in cache
cachedResult = data;
- // Store cookie ONLY if not anonymous
+ // Persist result past tab/window closure ONLY if not anonymous
if (data.username !== AuthenticationResult.ANONYMOUS_USERNAME)
- $cookieStore.put(AUTH_COOKIE_ID, data);
+ localStorageService.setItem(AUTH_STORAGE_KEY, data);
}
diff --git a/guacamole/src/main/webapp/app/history/historyModule.js b/guacamole/src/main/webapp/app/history/historyModule.js
index ce2ab7358..c7ec7e103 100644
--- a/guacamole/src/main/webapp/app/history/historyModule.js
+++ b/guacamole/src/main/webapp/app/history/historyModule.js
@@ -20,4 +20,6 @@
/**
* The module for code relating to connection history.
*/
-angular.module('history', []);
+angular.module('history', [
+ 'storage'
+]);
diff --git a/guacamole/src/main/webapp/app/history/services/guacHistory.js b/guacamole/src/main/webapp/app/history/services/guacHistory.js
index b47f9f509..74046ef9f 100644
--- a/guacamole/src/main/webapp/app/history/services/guacHistory.js
+++ b/guacamole/src/main/webapp/app/history/services/guacHistory.js
@@ -20,7 +20,14 @@
/**
* A service for reading and manipulating the Guacamole connection history.
*/
-angular.module('history').factory('guacHistory', ['HistoryEntry', function guacHistory(HistoryEntry) {
+angular.module('history').factory('guacHistory', ['$injector',
+ function guacHistory($injector) {
+
+ // Required types
+ var HistoryEntry = $injector.get('HistoryEntry');
+
+ // Required services
+ var localStorageService = $injector.get('localStorageService');
var service = {};
@@ -73,27 +80,15 @@ angular.module('history').factory('guacHistory', ['HistoryEntry', function guacH
if (service.recentConnections.length > IDEAL_LENGTH)
service.recentConnections.length = IDEAL_LENGTH;
- // Save updated history, ignore inability to use localStorage
- try {
- if (localStorage)
- localStorage.setItem(GUAC_HISTORY_STORAGE_KEY, JSON.stringify(service.recentConnections));
- }
- catch (ignore) {}
+ // Save updated history
+ localStorageService.setItem(GUAC_HISTORY_STORAGE_KEY, service.recentConnections);
};
- // Get stored connection history, ignore inability to use localStorage
- try {
-
- if (localStorage) {
- var storedHistory = JSON.parse(localStorage.getItem(GUAC_HISTORY_STORAGE_KEY) || "[]");
- if (storedHistory instanceof Array)
- service.recentConnections = storedHistory;
-
- }
-
- }
- catch (ignore) {}
+ // Init stored connection history from localStorage
+ var storedHistory = localStorageService.getItem(GUAC_HISTORY_STORAGE_KEY) || [];
+ if (storedHistory instanceof Array)
+ service.recentConnections = storedHistory;
return service;
diff --git a/guacamole/src/main/webapp/app/settings/services/preferenceService.js b/guacamole/src/main/webapp/app/settings/services/preferenceService.js
index 9af428169..bcd86336b 100644
--- a/guacamole/src/main/webapp/app/settings/services/preferenceService.js
+++ b/guacamole/src/main/webapp/app/settings/services/preferenceService.js
@@ -21,7 +21,11 @@
* A service for setting and retrieving browser-local preferences. Preferences
* may be any JSON-serializable type.
*/
-angular.module('settings').provider('preferenceService', function preferenceServiceProvider() {
+angular.module('settings').provider('preferenceService', ['$injector',
+ function preferenceServiceProvider($injector) {
+
+ // Required providers
+ var localStorageServiceProvider = $injector.get('localStorageServiceProvider');
/**
* Reference to the provider itself.
@@ -128,24 +132,18 @@ angular.module('settings').provider('preferenceService', function preferenceServ
};
- // Get stored preferences, ignore inability to use localStorage
- try {
-
- if (localStorage) {
- var preferencesJSON = localStorage.getItem(GUAC_PREFERENCES_STORAGE_KEY);
- if (preferencesJSON)
- angular.extend(provider.preferences, JSON.parse(preferencesJSON));
- }
-
- }
- catch (ignore) {}
+ // Get stored preferences from localStorage
+ var storedPreferences = localStorageServiceProvider.getItem(GUAC_PREFERENCES_STORAGE_KEY);
+ if (storedPreferences)
+ angular.extend(provider.preferences, storedPreferences);
// Factory method required by provider
this.$get = ['$injector', function preferenceServiceFactory($injector) {
// Required services
- var $rootScope = $injector.get('$rootScope');
- var $window = $injector.get('$window');
+ var $rootScope = $injector.get('$rootScope');
+ var $window = $injector.get('$window');
+ var localStorageService = $injector.get('localStorageService');
var service = {};
@@ -168,14 +166,7 @@ angular.module('settings').provider('preferenceService', function preferenceServ
* Persists the current values of all preferences, if possible.
*/
service.save = function save() {
-
- // Save updated preferences, ignore inability to use localStorage
- try {
- if (localStorage)
- localStorage.setItem(GUAC_PREFERENCES_STORAGE_KEY, JSON.stringify(service.preferences));
- }
- catch (ignore) {}
-
+ localStorageService.setItem(GUAC_PREFERENCES_STORAGE_KEY, service.preferences);
};
// Persist settings when window is unloaded
@@ -195,4 +186,4 @@ angular.module('settings').provider('preferenceService', function preferenceServ
}];
-});
+}]);
diff --git a/guacamole/src/main/webapp/app/settings/settingsModule.js b/guacamole/src/main/webapp/app/settings/settingsModule.js
index 7adc0d164..62ad1c813 100644
--- a/guacamole/src/main/webapp/app/settings/settingsModule.js
+++ b/guacamole/src/main/webapp/app/settings/settingsModule.js
@@ -26,5 +26,6 @@ angular.module('settings', [
'list',
'navigation',
'notification',
- 'rest'
+ 'rest',
+ 'storage'
]);
diff --git a/guacamole/src/main/webapp/app/storage/services/localStorageService.js b/guacamole/src/main/webapp/app/storage/services/localStorageService.js
new file mode 100644
index 000000000..a8c68460e
--- /dev/null
+++ b/guacamole/src/main/webapp/app/storage/services/localStorageService.js
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Service for setting, removing, and retrieving localStorage keys. If access
+ * to localStorage is disabled, or the browser does not support localStorage,
+ * key values are temporarily stored in memory instead. If necessary, the same
+ * functionality is also available at the localStorageServiceProvider level.
+ */
+angular.module('storage').provider('localStorageService', [function localStorageServiceProvider() {
+
+ /**
+ * Reference to this provider.
+ *
+ * @type localStorageServiceProvider
+ */
+ var provider = this;
+
+ /**
+ * Internal cache of key/value pairs stored within localStorage, updated
+ * lazily as keys are retrieved, updated, or removed. If localStorage is
+ * not actually available, then this cache will be the sole storage
+ * location for these key/value pairs.
+ *
+ * @type Object.
+ */
+ var storedItems = {};
+
+ /**
+ * Stores the given value within localStorage under the given key. If access
+ * to localStorage is not provided/implemented by the browser, the key/value
+ * pair will be stored internally (in memory) only, with the stored value
+ * remaining retrievable via getItem() until the browser tab/window is
+ * closed.
+ *
+ * @param {String} key
+ * The arbitrary, unique key under which the value should be stored.
+ *
+ * @param {Object} value
+ * The object to store under the given key. This may be any object that
+ * can be serialized as JSON, and will automatically be serialized as
+ * JSON prior to storage.
+ */
+ provider.setItem = function setItem(key, value) {
+
+ // Store given value internally
+ var data = JSON.stringify(value);
+ storedItems[key] = data;
+
+ // Additionally store value within localStorage if allowed
+ try {
+ if (window.localStorage)
+ localStorage.setItem(key, data);
+ }
+ catch (ignore) {}
+
+ };
+
+ /**
+ * Removes the item having the given key from localStorage. If access to
+ * localStorage is not provided/implemented by the browser, the item is
+ * removed solely from internal, in-memory storage. If no such item exists,
+ * this function has no effect.
+ *
+ * @param {String} key
+ * The arbitrary, unique key of the item to remove from localStorage.
+ */
+ provider.removeItem = function removeItem(key) {
+
+ // Evict key from internal storage
+ delete storedItems[key];
+
+ // Remove key from localStorage if allowed
+ try {
+ if (window.localStorage)
+ localStorage.removeItem(key);
+ }
+ catch (ignore) {}
+
+ };
+
+ /**
+ * Retrieves the value currently stored within localStorage for the item
+ * having the given key. If access to localStorage is not
+ * provided/implemented by the browser, the item is retrieved from
+ * internal, in-memory storage. The retrieved value is automatically
+ * deserialized from JSON prior to being returned.
+ *
+ * @param {String} key
+ * The arbitrary, unique key of the item to retrieve from localStorage.
+ *
+ * @returns {Object}
+ * The value stored within localStorage under the given key,
+ * automatically deserialized from JSON, or null if no such item is
+ * present.
+ */
+ provider.getItem = function getItem(key) {
+
+ // Attempt to refresh internal storage from localStorage
+ try {
+ if (window.localStorage)
+ storedItems[key] = localStorage.getItem(key);
+ }
+ catch (ignore) {}
+
+ // Pull and parse value from internal storage, if present
+ var data = storedItems[key];
+ if (data)
+ return JSON.parse(data);
+
+ // No value defined for given key
+ return null;
+
+ };
+
+ // Factory method required by provider
+ this.$get = ['$injector', function localStorageServiceFactory($injector) {
+
+ // Pass through all get/set/remove calls to the provider
+ // implementations of the same
+ return {
+ setItem : provider.setItem,
+ removeItem : provider.removeItem,
+ getItem : provider.getItem
+ };
+
+ }];
+
+}]);
diff --git a/guacamole/src/main/webapp/index.html b/guacamole/src/main/webapp/index.html
index 376245fa8..14321de42 100644
--- a/guacamole/src/main/webapp/index.html
+++ b/guacamole/src/main/webapp/index.html
@@ -62,7 +62,6 @@
-