mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 21:51:23 +00:00
Merge pull request #17 from glyptodon/cleanup-angular
GUAC-932: Initial cleanup of Angular code
This commit is contained in:
@@ -142,8 +142,6 @@
|
|||||||
<jsSourceFile>lib/messageformat/messageformat.js</jsSourceFile>
|
<jsSourceFile>lib/messageformat/messageformat.js</jsSourceFile>
|
||||||
<jsSourceFile>license.txt</jsSourceFile>
|
<jsSourceFile>license.txt</jsSourceFile>
|
||||||
<jsSourceFile>guacamole-common-js/all.js</jsSourceFile>
|
<jsSourceFile>guacamole-common-js/all.js</jsSourceFile>
|
||||||
<jsSourceFile>scripts/session.js</jsSourceFile>
|
|
||||||
<jsSourceFile>scripts/history.js</jsSourceFile>
|
|
||||||
</jsSourceFiles>
|
</jsSourceFiles>
|
||||||
|
|
||||||
<jsSourceIncludes>
|
<jsSourceIncludes>
|
||||||
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* The module for authentication and management of tokens.
|
* The module for authentication and management of tokens.
|
||||||
*/
|
*/
|
||||||
angular.module('auth', ['util']);
|
angular.module('auth', ['ngCookies']);
|
||||||
|
@@ -23,12 +23,13 @@
|
|||||||
/**
|
/**
|
||||||
* A service for authenticating a user against the REST API.
|
* A service for authenticating a user against the REST API.
|
||||||
*/
|
*/
|
||||||
angular.module('auth').factory('authenticationService', ['$http', '$injector',
|
angular.module('auth').factory('authenticationService', ['$http', '$cookieStore',
|
||||||
function authenticationService($http, $injector) {
|
function authenticationService($http, $cookieStore) {
|
||||||
|
|
||||||
var localStorageUtility = $injector.get("localStorageUtility");
|
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
|
var AUTH_COOKIE_ID = "GUAC_AUTH";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a request to authenticate a user using the token REST API endpoint,
|
* Makes a request to authenticate a user using the token REST API endpoint,
|
||||||
* returning a promise that can be used for processing the results of the call.
|
* returning a promise that can be used for processing the results of the call.
|
||||||
@@ -49,8 +50,10 @@ angular.module('auth').factory('authenticationService', ['$http', '$injector',
|
|||||||
password: password
|
password: password
|
||||||
})
|
})
|
||||||
}).success(function success(data, status, headers, config) {
|
}).success(function success(data, status, headers, config) {
|
||||||
localStorageUtility.set('authToken', data.authToken);
|
$cookieStore.put(AUTH_COOKIE_ID, {
|
||||||
localStorageUtility.set('userID', data.userID);
|
authToken : data.authToken,
|
||||||
|
userID : data.userID
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,22 +71,43 @@ angular.module('auth').factory('authenticationService', ['$http', '$injector',
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user ID of the current user.
|
* Returns the user ID of the current user. If the current user is not
|
||||||
|
* logged in, this ID may not be valid.
|
||||||
*
|
*
|
||||||
* @returns {String} The user ID of the current user.
|
* @returns {String}
|
||||||
|
* The user ID of the current user, or null if no authentication data
|
||||||
|
* is present.
|
||||||
*/
|
*/
|
||||||
service.getCurrentUserID = function getCurrentUserID() {
|
service.getCurrentUserID = function getCurrentUserID() {
|
||||||
return localStorageUtility.get('userID');
|
|
||||||
|
// Return user ID, if available
|
||||||
|
var authData = $cookieStore.get(AUTH_COOKIE_ID);
|
||||||
|
if (authData)
|
||||||
|
return authData.userID;
|
||||||
|
|
||||||
|
// No auth data present
|
||||||
|
return null;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the auth token associated with the current user. If the current
|
* Returns the auth token associated with the current user. If the current
|
||||||
* user is not logged in, this token may not be valid.
|
* user is not logged in, this token may not be valid.
|
||||||
*
|
*
|
||||||
* @returns {String} The auth token associated with the current user.
|
* @returns {String}
|
||||||
|
* The auth token associated with the current user, or null if no
|
||||||
|
* authentication data is present.
|
||||||
*/
|
*/
|
||||||
service.getCurrentToken = function getCurrentToken() {
|
service.getCurrentToken = function getCurrentToken() {
|
||||||
return localStorageUtility.get('authToken');
|
|
||||||
|
// Return auth token, if available
|
||||||
|
var authData = $cookieStore.get(AUTH_COOKIE_ID);
|
||||||
|
if (authData)
|
||||||
|
return authData.authToken;
|
||||||
|
|
||||||
|
// No auth data present
|
||||||
|
return null;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* The module for code used to connect to a connection or balancing group.
|
* The module for code used to connect to a connection or balancing group.
|
||||||
*/
|
*/
|
||||||
angular.module('client', []);
|
angular.module('client', ['auth', 'history']);
|
||||||
|
@@ -135,7 +135,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
|
|||||||
// Get DAO for reading connections and groups
|
// Get DAO for reading connections and groups
|
||||||
var connectionGroupDAO = $injector.get('connectionGroupDAO');
|
var connectionGroupDAO = $injector.get('connectionGroupDAO');
|
||||||
var connectionDAO = $injector.get('connectionDAO');
|
var connectionDAO = $injector.get('connectionDAO');
|
||||||
var ClientProperties = $injector.get('clientProperties');
|
var ClientProperties = $injector.get('ClientProperties');
|
||||||
|
|
||||||
// Client settings and state
|
// Client settings and state
|
||||||
$scope.clientProperties = new ClientProperties();
|
$scope.clientProperties = new ClientProperties();
|
||||||
@@ -265,9 +265,6 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
|
|||||||
// Show status dialog when client errors occur
|
// Show status dialog when client errors occur
|
||||||
$scope.$on('guacClientError', function clientErrorListener(event, client, status) {
|
$scope.$on('guacClientError', function clientErrorListener(event, client, status) {
|
||||||
|
|
||||||
// Disconnect
|
|
||||||
$scope.id = null;
|
|
||||||
|
|
||||||
// Determine translation name of error
|
// Determine translation name of error
|
||||||
var errorName = (status in CLIENT_ERRORS) ? status.toString(16).toUpperCase() : "DEFAULT";
|
var errorName = (status in CLIENT_ERRORS) ? status.toString(16).toUpperCase() : "DEFAULT";
|
||||||
|
|
||||||
@@ -293,6 +290,10 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
|
|||||||
|
|
||||||
// Show new status only if disconnected
|
// Show new status only if disconnected
|
||||||
if (status === "closed") {
|
if (status === "closed") {
|
||||||
|
|
||||||
|
// Disconnect
|
||||||
|
$scope.id = null;
|
||||||
|
|
||||||
$scope.showStatus({
|
$scope.showStatus({
|
||||||
title: "client.status.closedStatusTitle",
|
title: "client.status.closedStatusTitle",
|
||||||
text: "client.status.tunnelStates." + status
|
text: "client.status.tunnelStates." + status
|
||||||
@@ -304,9 +305,6 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
|
|||||||
// Show status dialog when tunnel errors occur
|
// Show status dialog when tunnel errors occur
|
||||||
$scope.$on('guacTunnelError', function tunnelErrorListener(event, tunnel, status) {
|
$scope.$on('guacTunnelError', function tunnelErrorListener(event, tunnel, status) {
|
||||||
|
|
||||||
// Disconnect
|
|
||||||
$scope.id = null;
|
|
||||||
|
|
||||||
// Determine translation name of error
|
// Determine translation name of error
|
||||||
var errorName = (status in TUNNEL_ERRORS) ? status.toString(16).toUpperCase() : "DEFAULT";
|
var errorName = (status in TUNNEL_ERRORS) ? status.toString(16).toUpperCase() : "DEFAULT";
|
||||||
|
|
||||||
|
@@ -30,12 +30,29 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
replace: true,
|
replace: true,
|
||||||
scope: {
|
scope: {
|
||||||
// Parameters for controlling client state
|
|
||||||
clientProperties : '=',
|
/**
|
||||||
|
* Parameters for controlling client state.
|
||||||
|
*
|
||||||
|
* @type ClientProperties|Object
|
||||||
|
*/
|
||||||
|
clientProperties : '=',
|
||||||
|
|
||||||
// Parameters for initially connecting
|
/**
|
||||||
id : '=',
|
* The ID of the Guacamole connection to connect to.
|
||||||
connectionParameters : '='
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
id : '=',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arbitrary URL-encoded parameters to append to the connection
|
||||||
|
* string when connecting.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
connectionParameters : '='
|
||||||
|
|
||||||
},
|
},
|
||||||
templateUrl: 'app/client/templates/guacClient.html',
|
templateUrl: 'app/client/templates/guacClient.html',
|
||||||
controller: ['$scope', '$injector', '$element', function guacClientController($scope, $injector, $element) {
|
controller: ['$scope', '$injector', '$element', function guacClientController($scope, $injector, $element) {
|
||||||
@@ -122,12 +139,13 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
*/
|
*/
|
||||||
var touchPad = new Guacamole.Mouse.Touchpad(displayContainer);
|
var touchPad = new Guacamole.Mouse.Touchpad(displayContainer);
|
||||||
|
|
||||||
var $window = $injector.get('$window'),
|
var $window = $injector.get('$window'),
|
||||||
guacAudio = $injector.get('guacAudio'),
|
guacAudio = $injector.get('guacAudio'),
|
||||||
guacVideo = $injector.get('guacVideo'),
|
guacVideo = $injector.get('guacVideo'),
|
||||||
guacTunnelFactory = $injector.get('guacTunnelFactory'),
|
guacHistory = $injector.get('guacHistory'),
|
||||||
guacClientFactory = $injector.get('guacClientFactory'),
|
guacTunnelFactory = $injector.get('guacTunnelFactory'),
|
||||||
localStorageUtility = $injector.get('localStorageUtility');
|
guacClientFactory = $injector.get('guacClientFactory'),
|
||||||
|
authenticationService = $injector.get('authenticationService');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the scale of the attached Guacamole.Client based on current window
|
* Updates the scale of the attached Guacamole.Client based on current window
|
||||||
@@ -175,7 +193,7 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
// Build base connect string
|
// Build base connect string
|
||||||
var connectString =
|
var connectString =
|
||||||
"id=" + encodeURIComponent($scope.id)
|
"id=" + encodeURIComponent($scope.id)
|
||||||
+ "&authToken=" + encodeURIComponent(localStorageUtility.get('authToken'))
|
+ "&authToken=" + encodeURIComponent(authenticationService.getCurrentToken())
|
||||||
+ "&width=" + Math.floor(optimal_width)
|
+ "&width=" + Math.floor(optimal_width)
|
||||||
+ "&height=" + Math.floor(optimal_height)
|
+ "&height=" + Math.floor(optimal_height)
|
||||||
+ "&dpi=" + Math.floor(optimal_dpi)
|
+ "&dpi=" + Math.floor(optimal_dpi)
|
||||||
@@ -298,12 +316,37 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Connect to given ID whenever ID changes
|
// Connect to given ID whenever ID changes
|
||||||
$scope.$watch('id', function(id) {
|
$scope.$watch('id', function(id, previousID) {
|
||||||
|
|
||||||
// If a client is already attached, ensure it is disconnected
|
// If a client is already attached, ensure it is disconnected
|
||||||
if (client)
|
if (client)
|
||||||
client.disconnect();
|
client.disconnect();
|
||||||
|
|
||||||
|
// Update stored thumbnail of previous connection
|
||||||
|
if (previousID && display && display.getWidth() > 0 && display.getHeight() > 0) {
|
||||||
|
|
||||||
|
// Get screenshot
|
||||||
|
var canvas = display.flatten();
|
||||||
|
|
||||||
|
// Calculate scale of thumbnail (max 320x240, max zoom 100%)
|
||||||
|
var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1);
|
||||||
|
|
||||||
|
// Create thumbnail canvas
|
||||||
|
var thumbnail = document.createElement("canvas");
|
||||||
|
thumbnail.width = canvas.width*scale;
|
||||||
|
thumbnail.height = canvas.height*scale;
|
||||||
|
|
||||||
|
// Scale screenshot to thumbnail
|
||||||
|
var context = thumbnail.getContext("2d");
|
||||||
|
context.drawImage(canvas,
|
||||||
|
0, 0, canvas.width, canvas.height,
|
||||||
|
0, 0, thumbnail.width, thumbnail.height
|
||||||
|
);
|
||||||
|
|
||||||
|
guacHistory.updateThumbnail(previousID, thumbnail.toDataURL("image/png"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Only proceed if a new client is attached
|
// Only proceed if a new client is attached
|
||||||
if (!id)
|
if (!id)
|
||||||
return;
|
return;
|
||||||
|
@@ -23,14 +23,20 @@
|
|||||||
/**
|
/**
|
||||||
* A service for generating new guacClient properties objects.
|
* A service for generating new guacClient properties objects.
|
||||||
*/
|
*/
|
||||||
angular.module('client').factory('clientProperties', [function clientProperties() {
|
angular.module('client').factory('ClientProperties', [function defineClientProperties() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object used for interacting with a guacClient directive.
|
* Object used for interacting with a guacClient directive.
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @param {ClientProperties|Object} [template={}]
|
||||||
|
* The object whose properties should be copied within the new
|
||||||
|
* ClientProperties.
|
||||||
*/
|
*/
|
||||||
return function() {
|
var ClientProperties = function ClientProperties(template) {
|
||||||
|
|
||||||
|
// Use empty object by default
|
||||||
|
template = template || {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the display should be scaled automatically to fit within the
|
* Whether the display should be scaled automatically to fit within the
|
||||||
@@ -38,7 +44,7 @@ angular.module('client').factory('clientProperties', [function clientProperties(
|
|||||||
*
|
*
|
||||||
* @type Boolean
|
* @type Boolean
|
||||||
*/
|
*/
|
||||||
this.autoFit = true;
|
this.autoFit = template.autoFit || true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current scale. If autoFit is true, the effect of setting this
|
* The current scale. If autoFit is true, the effect of setting this
|
||||||
@@ -46,28 +52,28 @@ angular.module('client').factory('clientProperties', [function clientProperties(
|
|||||||
*
|
*
|
||||||
* @type Number
|
* @type Number
|
||||||
*/
|
*/
|
||||||
this.scale = 1;
|
this.scale = template.scale || 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum scale value.
|
* The minimum scale value.
|
||||||
*
|
*
|
||||||
* @type Number
|
* @type Number
|
||||||
*/
|
*/
|
||||||
this.minScale = 1;
|
this.minScale = template.minScale || 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum scale value.
|
* The maximum scale value.
|
||||||
*
|
*
|
||||||
* @type Number
|
* @type Number
|
||||||
*/
|
*/
|
||||||
this.maxScale = 3;
|
this.maxScale = template.maxScale || 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the client should listen to keyboard events.
|
* Whether or not the client should listen to keyboard events.
|
||||||
*
|
*
|
||||||
* @type Boolean
|
* @type Boolean
|
||||||
*/
|
*/
|
||||||
this.keyboardEnabled = true;
|
this.keyboardEnabled = template.keyboardEnabled || true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether translation of touch to mouse events should emulate an
|
* Whether translation of touch to mouse events should emulate an
|
||||||
@@ -75,8 +81,10 @@ angular.module('client').factory('clientProperties', [function clientProperties(
|
|||||||
*
|
*
|
||||||
* @type Boolean
|
* @type Boolean
|
||||||
*/
|
*/
|
||||||
this.emulateAbsoluteMouse = true;
|
this.emulateAbsoluteMouse = template.emulateAbsoluteMouse || true;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return ClientProperties;
|
||||||
|
|
||||||
}]);
|
}]);
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* The module for code relating to connections.
|
* The module for code relating to connections.
|
||||||
*/
|
*/
|
||||||
angular.module('connection', ['util']);
|
angular.module('connection', ['auth']);
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
/**
|
/**
|
||||||
* The DAO for connection operations agains the REST API.
|
* The DAO for connection operations agains the REST API.
|
||||||
*/
|
*/
|
||||||
angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUtility',
|
angular.module('connection').factory('connectionDAO', ['$http', 'authenticationService',
|
||||||
function connectionDAO($http, localStorageUtility) {
|
function connectionDAO($http, authenticationService) {
|
||||||
|
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUti
|
|||||||
* @returns {promise} A promise for the HTTP call.
|
* @returns {promise} A promise for the HTTP call.
|
||||||
*/
|
*/
|
||||||
service.getConnection = function getConnection(id) {
|
service.getConnection = function getConnection(id) {
|
||||||
return $http.get("api/connection/" + id + "?token=" + localStorageUtility.get('authToken'));
|
return $http.get("api/connection/" + id + "?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +55,7 @@ angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUti
|
|||||||
if(parentID !== undefined)
|
if(parentID !== undefined)
|
||||||
parentIDParam = "&parentID=" + parentID;
|
parentIDParam = "&parentID=" + parentID;
|
||||||
|
|
||||||
return $http.get("api/connection?token=" + localStorageUtility.get('authToken') + parentIDParam);
|
return $http.get("api/connection?token=" + authenticationService.getCurrentToken() + parentIDParam);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,7 +74,7 @@ angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUti
|
|||||||
|
|
||||||
// This is a new connection
|
// This is a new connection
|
||||||
if(!connectionToSave.identifier) {
|
if(!connectionToSave.identifier) {
|
||||||
return $http.post("api/connection/?token=" + localStorageUtility.get('authToken'), connectionToSave).success(
|
return $http.post("api/connection/?token=" + authenticationService.getCurrentToken(), connectionToSave).success(
|
||||||
function setConnectionID(connectionID){
|
function setConnectionID(connectionID){
|
||||||
// Set the identifier on the new connection
|
// Set the identifier on the new connection
|
||||||
connection.identifier = connectionID;
|
connection.identifier = connectionID;
|
||||||
@@ -83,7 +83,7 @@ angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUti
|
|||||||
} else {
|
} else {
|
||||||
return $http.post(
|
return $http.post(
|
||||||
"api/connection/" + connectionToSave.identifier +
|
"api/connection/" + connectionToSave.identifier +
|
||||||
"?token=" + localStorageUtility.get('authToken'),
|
"?token=" + authenticationService.getCurrentToken(),
|
||||||
connectionToSave);
|
connectionToSave);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -100,7 +100,7 @@ angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUti
|
|||||||
|
|
||||||
return $http.put(
|
return $http.put(
|
||||||
"api/connection/" + connection.identifier +
|
"api/connection/" + connection.identifier +
|
||||||
"?token=" + localStorageUtility.get('authToken') +
|
"?token=" + authenticationService.getCurrentToken() +
|
||||||
"&parentID=" + connection.parentIdentifier,
|
"&parentID=" + connection.parentIdentifier,
|
||||||
connection);
|
connection);
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ angular.module('connection').factory('connectionDAO', ['$http', 'localStorageUti
|
|||||||
service.deleteConnection = function deleteConnection(connection) {
|
service.deleteConnection = function deleteConnection(connection) {
|
||||||
return $http['delete'](
|
return $http['delete'](
|
||||||
"api/connection/" + connection.identifier +
|
"api/connection/" + connection.identifier +
|
||||||
"?token=" + localStorageUtility.get('authToken'));
|
"?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* The module for code relating to connection groups.
|
* The module for code relating to connection groups.
|
||||||
*/
|
*/
|
||||||
angular.module('connectionGroup', ['util', 'connection']);
|
angular.module('connectionGroup', ['auth', 'util', 'connection']);
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
/**
|
/**
|
||||||
* The DAO for connection group operations agains the REST API.
|
* The DAO for connection group operations agains the REST API.
|
||||||
*/
|
*/
|
||||||
angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'localStorageUtility',
|
angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'authenticationService',
|
||||||
function connectionGrouDAO($http, localStorageUtility) {
|
function connectionGrouDAO($http, authenticationService) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID of the root connection group.
|
* The ID of the root connection group.
|
||||||
@@ -49,7 +49,7 @@ angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'local
|
|||||||
if(parentID !== undefined)
|
if(parentID !== undefined)
|
||||||
parentIDParam = "&parentID=" + parentID;
|
parentIDParam = "&parentID=" + parentID;
|
||||||
|
|
||||||
return $http.get("api/connectionGroup?token=" + localStorageUtility.get('authToken') + parentIDParam);
|
return $http.get("api/connectionGroup?token=" + authenticationService.getCurrentToken() + parentIDParam);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +67,7 @@ angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'local
|
|||||||
// Use the root connection group ID if no ID is passed in
|
// Use the root connection group ID if no ID is passed in
|
||||||
connectionGroupID = connectionGroupID || ROOT_CONNECTION_GROUP_ID;
|
connectionGroupID = connectionGroupID || ROOT_CONNECTION_GROUP_ID;
|
||||||
|
|
||||||
return $http.get("api/connectionGroup/" + connectionGroupID + "?token=" + localStorageUtility.get('authToken'));
|
return $http.get("api/connectionGroup/" + connectionGroupID + "?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +81,7 @@ angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'local
|
|||||||
service.saveConnectionGroup = function saveConnectionGroup(connectionGroup) {
|
service.saveConnectionGroup = function saveConnectionGroup(connectionGroup) {
|
||||||
// This is a new connection group
|
// This is a new connection group
|
||||||
if(!connectionGroup.identifier) {
|
if(!connectionGroup.identifier) {
|
||||||
return $http.post("api/connectionGroup/?token=" + localStorageUtility.get('authToken'), connectionGroup).success(
|
return $http.post("api/connectionGroup/?token=" + authenticationService.getCurrentToken(), connectionGroup).success(
|
||||||
function setConnectionGroupID(connectionGroupID){
|
function setConnectionGroupID(connectionGroupID){
|
||||||
// Set the identifier on the new connection
|
// Set the identifier on the new connection
|
||||||
connectionGroup.identifier = connectionGroupID;
|
connectionGroup.identifier = connectionGroupID;
|
||||||
@@ -90,7 +90,7 @@ angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'local
|
|||||||
} else {
|
} else {
|
||||||
return $http.post(
|
return $http.post(
|
||||||
"api/connectionGroup/" + connectionGroup.identifier +
|
"api/connectionGroup/" + connectionGroup.identifier +
|
||||||
"?token=" + localStorageUtility.get('authToken'),
|
"?token=" + authenticationService.getCurrentToken(),
|
||||||
connectionGroup);
|
connectionGroup);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -107,7 +107,7 @@ angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'local
|
|||||||
|
|
||||||
return $http.put(
|
return $http.put(
|
||||||
"api/connectionGroup/" + connectionGroup.identifier +
|
"api/connectionGroup/" + connectionGroup.identifier +
|
||||||
"?token=" + localStorageUtility.get('authToken') +
|
"?token=" + authenticationService.getCurrentToken() +
|
||||||
"&parentID=" + connectionGroup.parentIdentifier,
|
"&parentID=" + connectionGroup.parentIdentifier,
|
||||||
connectionGroup);
|
connectionGroup);
|
||||||
};
|
};
|
||||||
@@ -123,7 +123,7 @@ angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'local
|
|||||||
service.deleteConnectionGroup = function deleteConnectionGroup(connectionGroup) {
|
service.deleteConnectionGroup = function deleteConnectionGroup(connectionGroup) {
|
||||||
return $http['delete'](
|
return $http['delete'](
|
||||||
"api/connectionGroup/" + connectionGroup.identifier +
|
"api/connectionGroup/" + connectionGroup.identifier +
|
||||||
"?token=" + localStorageUtility.get('authToken'));
|
"?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
26
guacamole/src/main/webapp/app/history/historyModule.js
Normal file
26
guacamole/src/main/webapp/app/history/historyModule.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module for code relating to connection history.
|
||||||
|
*/
|
||||||
|
angular.module('history', []);
|
103
guacamole/src/main/webapp/app/history/services/guacHistory.js
Normal file
103
guacamole/src/main/webapp/app/history/services/guacHistory.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 reading and manipulating the Guacamole connection history.
|
||||||
|
*/
|
||||||
|
angular.module('history').factory('guacHistory', ['HistoryEntry', function guacHistory(HistoryEntry) {
|
||||||
|
|
||||||
|
var service = {};
|
||||||
|
|
||||||
|
// The parameter name for getting the history from local storage
|
||||||
|
var GUAC_HISTORY_STORAGE_KEY = "GUAC_HISTORY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of entries to allow before removing old entries based on the
|
||||||
|
* cutoff.
|
||||||
|
*/
|
||||||
|
var IDEAL_LENGTH = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The top few recent connections, sorted in order of most recent access.
|
||||||
|
*
|
||||||
|
* @type HistoryEntry[]
|
||||||
|
*/
|
||||||
|
service.recentConnections = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the thumbnail and access time of the history entry for the
|
||||||
|
* connection with the given ID.
|
||||||
|
*
|
||||||
|
* @param {String} id
|
||||||
|
* The ID of the connection whose history entry should be updated.
|
||||||
|
*
|
||||||
|
* @param {String} thumbnail
|
||||||
|
* The URL of the thumbnail image to associate with the history entry.
|
||||||
|
*/
|
||||||
|
service.updateThumbnail = function(id, thumbnail) {
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
// Remove any existing entry for this connection
|
||||||
|
for (i=0; i < service.recentConnections.length; i++) {
|
||||||
|
if (service.recentConnections[i].id === id) {
|
||||||
|
service.recentConnections.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store new entry in history
|
||||||
|
service.recentConnections.unshift(new HistoryEntry(
|
||||||
|
id,
|
||||||
|
thumbnail,
|
||||||
|
new Date().getTime()
|
||||||
|
));
|
||||||
|
|
||||||
|
// Truncate history to ideal length
|
||||||
|
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) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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) {}
|
||||||
|
|
||||||
|
return service;
|
||||||
|
|
||||||
|
}]);
|
54
guacamole/src/main/webapp/app/history/types/HistoryEntry.js
Normal file
54
guacamole/src/main/webapp/app/history/types/HistoryEntry.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the HistoryEntry class used by the guacHistory service.
|
||||||
|
*/
|
||||||
|
angular.module('history').factory('HistoryEntry', [function defineHistoryEntry() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single entry in the connection history.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {String} id The ID of the connection.
|
||||||
|
*
|
||||||
|
* @param {String} thumbnail
|
||||||
|
* The URL of the thumbnail to use to represent the connection.
|
||||||
|
*/
|
||||||
|
var HistoryEntry = function HistoryEntry(id, thumbnail) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the connection associated with this history entry.
|
||||||
|
*/
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The thumbnail associated with the connection associated with this
|
||||||
|
* history entry.
|
||||||
|
*/
|
||||||
|
this.thumbnail = thumbnail;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return HistoryEntry;
|
||||||
|
|
||||||
|
}]);
|
@@ -31,7 +31,7 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
|
|||||||
|
|
||||||
// Get the dependencies commonJS style
|
// Get the dependencies commonJS style
|
||||||
var connectionGroupService = $injector.get("connectionGroupService");
|
var connectionGroupService = $injector.get("connectionGroupService");
|
||||||
var localStorageUtility = $injector.get("localStorageUtility");
|
var guacHistory = $injector.get("guacHistory");
|
||||||
|
|
||||||
// All the connections and connection groups in root
|
// All the connections and connection groups in root
|
||||||
$scope.connectionsAndGroups = [];
|
$scope.connectionsAndGroups = [];
|
||||||
@@ -48,14 +48,14 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
|
|||||||
connectionGroupService.getAllGroupsAndConnections($scope.connectionsAndGroups)
|
connectionGroupService.getAllGroupsAndConnections($scope.connectionsAndGroups)
|
||||||
.then(function findRecentConnections() {
|
.then(function findRecentConnections() {
|
||||||
|
|
||||||
// Try to parse out the recent connections from local storage
|
// TODONT: Munch the guacHistory recentConnections list into a legacy-style object
|
||||||
var recentConnections;
|
var recentConnections = {};
|
||||||
try {
|
for (var i=0; i < guacHistory.recentConnections.length; i++) {
|
||||||
recentConnections = JSON.parse(localStorageUtility.get(GUAC_HISTORY_STORAGE_KEY));
|
var entry = guacHistory.recentConnections[i];
|
||||||
} catch(e) {
|
recentConnections[encodeURIComponent(entry.id)] = {
|
||||||
|
id : entry.id,
|
||||||
// The recent history is corrupted - clear it
|
thumbnail : entry.thumbnail
|
||||||
localStorageUtility.clear(GUAC_HISTORY_STORAGE_KEY);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out which recent connection entries are valid
|
// Figure out which recent connection entries are valid
|
||||||
|
@@ -20,4 +20,4 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
angular.module('home', ['connection', 'connectionGroup', 'user', 'permission']);
|
angular.module('home', ['connection', 'connectionGroup', 'history', 'user', 'permission']);
|
||||||
|
@@ -61,7 +61,7 @@
|
|||||||
<div ng-repeat="recentConnection in recentConnections" class="connection">
|
<div ng-repeat="recentConnection in recentConnections" class="connection">
|
||||||
<a href="#/client/{{recentConnection.type}}/{{recentConnection.id}}/{{recentConnection.name}}">
|
<a href="#/client/{{recentConnection.type}}/{{recentConnection.id}}/{{recentConnection.name}}">
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<img alt="{{recentConnection.name}}" src="{{recentConnection.thumbnail}}"/>
|
<img alt="{{recentConnection.name}}" ng-src="{{recentConnection.thumbnail}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<span class="name">{{recentConnection.name}}</span>
|
<span class="name">{{recentConnection.name}}</span>
|
||||||
|
@@ -50,6 +50,21 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current status notification, or false if no status is currently
|
||||||
|
* shown.
|
||||||
|
*
|
||||||
|
* @type Notification|Boolean
|
||||||
|
*/
|
||||||
|
$scope.status = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All currently-visible notifications.
|
||||||
|
*
|
||||||
|
* @type Notification[]
|
||||||
|
*/
|
||||||
|
$scope.notifications = [];
|
||||||
|
|
||||||
// Put some useful variables in the top level scope
|
// Put some useful variables in the top level scope
|
||||||
$scope.page = {
|
$scope.page = {
|
||||||
title: '',
|
title: '',
|
||||||
@@ -59,7 +74,6 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
$scope.currentUserIsAdmin = false;
|
$scope.currentUserIsAdmin = false;
|
||||||
$scope.currentUserHasUpdate = false;
|
$scope.currentUserHasUpdate = false;
|
||||||
$scope.currentUserPermissions = null;
|
$scope.currentUserPermissions = null;
|
||||||
$scope.notifications = [];
|
|
||||||
var notificationUniqueID = 0;
|
var notificationUniqueID = 0;
|
||||||
|
|
||||||
// A promise to be fulfilled when all basic user permissions are loaded.
|
// A promise to be fulfilled when all basic user permissions are loaded.
|
||||||
@@ -77,41 +91,8 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
* notification is currently shown, no further statuses will be shown
|
* notification is currently shown, no further statuses will be shown
|
||||||
* until the current status is hidden.
|
* until the current status is hidden.
|
||||||
*
|
*
|
||||||
* @param {Object} status The status notification to show.
|
* @param {Notification|Boolean|Object} status
|
||||||
* @param {String} [status.title] The title of the notification.
|
* The status notification to show.
|
||||||
* @param {String} [status.text] The body text of the notification.
|
|
||||||
* @param {String} [status.className] The CSS class name to apply.
|
|
||||||
*
|
|
||||||
* @param {String} [status.countdown.text]
|
|
||||||
* In the case that a countdown applies to the notification, the text to
|
|
||||||
* display while the countdown is active.
|
|
||||||
*
|
|
||||||
* @param {Function} [status.countdown.callback]
|
|
||||||
* The callback to call when the countdown expires.
|
|
||||||
*
|
|
||||||
* @param {String} [status.countdown.remaining]
|
|
||||||
* The number of seconds remaining before the countdown callback is
|
|
||||||
* called.
|
|
||||||
*
|
|
||||||
* @param {String} [status.progress.text]
|
|
||||||
* If this notification has associated progress, the text to display
|
|
||||||
* while the operation is occurring.
|
|
||||||
*
|
|
||||||
* @param {String} [status.progress.value]
|
|
||||||
* The current state of operation progress, as an arbitrary number
|
|
||||||
* which increases as the operation continues.
|
|
||||||
*
|
|
||||||
* @param {String} [status.progress.unit]
|
|
||||||
* The unit of the arbitrary status.progress.value, if that value has
|
|
||||||
* an associated unit.
|
|
||||||
*
|
|
||||||
* @param {String} [status.progress.ratio]
|
|
||||||
* If known, the current status of the operation as a value between
|
|
||||||
* 0 and 1 inclusive, where 0 is not yet started, and 1 is complete.
|
|
||||||
*
|
|
||||||
* @param {Object[]} [status.actions]
|
|
||||||
* Array of action objects which contain an action name and callback to
|
|
||||||
* be executed when that action is invoked.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -138,43 +119,10 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
/**
|
/**
|
||||||
* Adds a notification to the the list of notifications shown.
|
* Adds a notification to the the list of notifications shown.
|
||||||
*
|
*
|
||||||
* @param {Object} notification The notification to add.
|
* @param {Notification|Object} notification The notification to add.
|
||||||
* @param {String} [notification.title] The title of the notification.
|
|
||||||
* @param {String} [notification.text] The body text of the notification.
|
|
||||||
* @param {String} [notification.className] The CSS class name to apply.
|
|
||||||
*
|
*
|
||||||
* @param {String} [notification.countdown.text]
|
* @returns {Number}
|
||||||
* In the case that a countdown applies to the notification, the text to
|
* A unique ID for the notification that's just been added.
|
||||||
* display while the countdown is active.
|
|
||||||
*
|
|
||||||
* @param {Function} [notification.countdown.callback]
|
|
||||||
* The callback to call when the countdown expires.
|
|
||||||
*
|
|
||||||
* @param {String} [notification.countdown.remaining]
|
|
||||||
* The number of seconds remaining before the countdown callback is
|
|
||||||
* called.
|
|
||||||
*
|
|
||||||
* @param {String} [notification.progress.text]
|
|
||||||
* If this notification has associated progress, the text to display
|
|
||||||
* while the operation is occurring.
|
|
||||||
*
|
|
||||||
* @param {String} [notification.progress.value]
|
|
||||||
* The current state of operation progress, as an arbitrary number
|
|
||||||
* which increases as the operation continues.
|
|
||||||
*
|
|
||||||
* @param {String} [notification.progress.unit]
|
|
||||||
* The unit of the arbitrary notification.progress.value, if that value
|
|
||||||
* has an associated unit.
|
|
||||||
*
|
|
||||||
* @param {String} [notification.progress.ratio]
|
|
||||||
* If known, the current status of the operation as a value between
|
|
||||||
* 0 and 1 inclusive, where 0 is not yet started, and 1 is complete.
|
|
||||||
*
|
|
||||||
* @param {Object[]} [notification.actions]
|
|
||||||
* Array of action objects which contain an action name and callback to
|
|
||||||
* be executed when that action is invoked.
|
|
||||||
*
|
|
||||||
* @returns {Number} A unique ID for the notification that's just been added.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
|
@@ -31,110 +31,11 @@ angular.module('notification').directive('guacNotification', [function guacNotif
|
|||||||
scope: {
|
scope: {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CSS class to apply to the notification.
|
* The notification to display.
|
||||||
*
|
*
|
||||||
* @type String
|
* @type Notification|Object
|
||||||
*/
|
*/
|
||||||
className : '=',
|
notification : '='
|
||||||
|
|
||||||
/**
|
|
||||||
* The title of the notification.
|
|
||||||
*
|
|
||||||
* @type String
|
|
||||||
*/
|
|
||||||
title : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The body text of the notification.
|
|
||||||
*
|
|
||||||
* @type String
|
|
||||||
*/
|
|
||||||
text : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text to use for displaying the countdown. For the sake of
|
|
||||||
* i18n, the variable REMAINING will be applied within the
|
|
||||||
* translation string for formatting plurals, etc.
|
|
||||||
*
|
|
||||||
* @type String
|
|
||||||
* @example
|
|
||||||
* "Only {REMAINING} {REMAINING, plural, one{second} other{seconds}} remain."
|
|
||||||
*/
|
|
||||||
countdownText : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of seconds to wait before automatically calling the
|
|
||||||
* default callback.
|
|
||||||
*
|
|
||||||
* @type Number
|
|
||||||
*/
|
|
||||||
countdown : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The function to call when timeRemaining expires. If timeRemaining
|
|
||||||
* is not set, this does not apply.
|
|
||||||
*
|
|
||||||
* @type Function
|
|
||||||
*/
|
|
||||||
defaultCallback : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text to use for displaying the progress. For the sake of
|
|
||||||
* i18n, the variable PROGRESS will be applied within the
|
|
||||||
* translation string for formatting plurals, etc., while the
|
|
||||||
* variable UNIT will be applied, if needed, for whatever units
|
|
||||||
* are applicable to the progress display.
|
|
||||||
*
|
|
||||||
* @type String
|
|
||||||
* @example
|
|
||||||
* "{PROGRESS} {UNIT, select, b{B} kb{KB}} uploaded."
|
|
||||||
*/
|
|
||||||
progressText : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unit which applies to the progress indicator, if any. This
|
|
||||||
* will be substituted in the progressText string with the UNIT
|
|
||||||
* variable.
|
|
||||||
*
|
|
||||||
* @type String
|
|
||||||
*/
|
|
||||||
progressUnit : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arbitrary value denoting how much progress has been made
|
|
||||||
* in some ongoing task that this notification represents.
|
|
||||||
*
|
|
||||||
* @type Number
|
|
||||||
*/
|
|
||||||
progress : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value between 0 and 1 denoting what proportion of the operation
|
|
||||||
* has completed. If known, this value should be 0 if the operation
|
|
||||||
* has not started, and 1 if the operation is complete.
|
|
||||||
*
|
|
||||||
* @type Number
|
|
||||||
*/
|
|
||||||
progressRatio : '=',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of name/callback pairs for each action the user is allowed
|
|
||||||
* to take once the notification is shown.
|
|
||||||
*
|
|
||||||
* @type Array
|
|
||||||
* @example
|
|
||||||
* [
|
|
||||||
* {
|
|
||||||
* name : "Action 1 name",
|
|
||||||
* callback : actionCallback1
|
|
||||||
* },
|
|
||||||
* {
|
|
||||||
* name : "Action 2 text",
|
|
||||||
* callback : actionCallback2
|
|
||||||
* }
|
|
||||||
* ]
|
|
||||||
*/
|
|
||||||
actions : '='
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -142,21 +43,22 @@ angular.module('notification').directive('guacNotification', [function guacNotif
|
|||||||
controller: ['$scope', '$interval', function guacNotificationController($scope, $interval) {
|
controller: ['$scope', '$interval', function guacNotificationController($scope, $interval) {
|
||||||
|
|
||||||
// Update progress bar if end known
|
// Update progress bar if end known
|
||||||
$scope.$watch("progressRatio", function updateProgress(ratio) {
|
$scope.$watch("notification.progress.ratio", function updateProgress(ratio) {
|
||||||
$scope.progressPercent = ratio * 100;
|
$scope.progressPercent = ratio * 100;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set countdown interval when associated property is set
|
$scope.$watch("notification", function resetTimeRemaining(notification) {
|
||||||
$scope.$watch("countdown", function resetTimeRemaining(countdown) {
|
|
||||||
|
|
||||||
$scope.timeRemaining = countdown;
|
var countdown = notification.countdown;
|
||||||
|
|
||||||
// Clean up any existing interval
|
// Clean up any existing interval
|
||||||
if ($scope.interval)
|
if ($scope.interval)
|
||||||
$interval.cancel($scope.interval);
|
$interval.cancel($scope.interval);
|
||||||
|
|
||||||
// Update and handle countdown, if provided
|
// Update and handle countdown, if provided
|
||||||
if ($scope.timeRemaining) {
|
if (countdown) {
|
||||||
|
|
||||||
|
$scope.timeRemaining = countdown.remaining;
|
||||||
|
|
||||||
$scope.interval = $interval(function updateTimeRemaining() {
|
$scope.interval = $interval(function updateTimeRemaining() {
|
||||||
|
|
||||||
@@ -164,8 +66,8 @@ angular.module('notification').directive('guacNotification', [function guacNotif
|
|||||||
$scope.timeRemaining--;
|
$scope.timeRemaining--;
|
||||||
|
|
||||||
// Call countdown callback when time remaining expires
|
// Call countdown callback when time remaining expires
|
||||||
if ($scope.timeRemaining === 0 && $scope.defaultCallback)
|
if ($scope.timeRemaining === 0)
|
||||||
$scope.defaultCallback();
|
countdown.performAction();
|
||||||
|
|
||||||
}, 1000, $scope.timeRemaining);
|
}, 1000, $scope.timeRemaining);
|
||||||
|
|
||||||
@@ -184,4 +86,4 @@ angular.module('notification').directive('guacNotification', [function guacNotif
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="notification" ng-class="className">
|
<div class="notification" ng-class="notification.className">
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2014 Glyptodon LLC
|
Copyright (C) 2014 Glyptodon LLC
|
||||||
|
|
||||||
@@ -22,26 +22,26 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- Notification title -->
|
<!-- Notification title -->
|
||||||
<div ng-show="title" class="title-bar">
|
<div ng-show="notification.title" class="title-bar">
|
||||||
<div class="title">{{title | translate}}</div>
|
<div class="title">{{notification.title | translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
|
|
||||||
<!-- Notification text -->
|
<!-- Notification text -->
|
||||||
<p ng-show="text" class="text">{{text | translate}}</p>
|
<p ng-show="notification.text" class="text">{{notification.text | translate}}</p>
|
||||||
|
|
||||||
<!-- Current progress -->
|
<!-- Current progress -->
|
||||||
<div ng-show="progressText" class="progress"><div ng-show="progressPercent" ng-style="{'width': progressPercent + '%'}" class="bar"></div><div class="text">{{progressText | translate:"{ PROGRESS: progress, UNIT: progressUnit }"}}</div></div>
|
<div ng-show="notification.progress" class="progress"><div ng-show="progressPercent" ng-style="{'width': progressPercent + '%'}" class="bar"></div><div ng-show="notification.progress.text" class="text">{{notification.progress.text | translate:"{ PROGRESS: notification.progress.value, UNIT: notification.progress.unit}"}}</div></div>
|
||||||
|
|
||||||
<!-- Default action countdown text -->
|
<!-- Default action countdown text -->
|
||||||
<p ng-show="countdownText" class="countdown-text">{{countdownText | translate:"{ REMAINING: timeRemaining }"}}</p>
|
<p ng-show="notification.countdown.text" class="countdown-text">{{notification.countdown.text | translate:"{ REMAINING: timeRemaining}"}}</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div ng-show="actions && actions.length" class="buttons">
|
<div ng-show="notification.actions.length" class="buttons">
|
||||||
<button ng-repeat="action in actions" ng-click="action.callback()">{{action.name | translate}}</button>
|
<button ng-repeat="action in notification.actions" ng-click="action.callback()">{{action.name | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Notification class definition.
|
||||||
|
*/
|
||||||
|
angular.module('notification').factory('Notification', [function defineNotification() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Notification, initializing the properties of that
|
||||||
|
* Notification with the corresponding properties of the given template.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {Notification|Object} [template={}]
|
||||||
|
* The object whose properties should be copied within the new
|
||||||
|
* Notification.
|
||||||
|
*/
|
||||||
|
var Notification = function Notification(template) {
|
||||||
|
|
||||||
|
// Use empty object by default
|
||||||
|
template = template || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CSS class to associate with the notification, if any.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.className = template.className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of the notification.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.title = template.title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The body text of the notification.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.text = template.text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of all actions available to the user in response to this
|
||||||
|
* notification.
|
||||||
|
*
|
||||||
|
* @type NotificationAction[]
|
||||||
|
*/
|
||||||
|
this.actions = template.actions || [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current progress state of the ongoing action associated with this
|
||||||
|
* notification.
|
||||||
|
*
|
||||||
|
* @type NotificationProgress
|
||||||
|
*/
|
||||||
|
this.progress = template.progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The countdown and corresponding default action which applies to
|
||||||
|
* this notification, if any.
|
||||||
|
*
|
||||||
|
* @type NotificationCountdown
|
||||||
|
*/
|
||||||
|
this.countdown = template.countdown;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return Notification;
|
||||||
|
|
||||||
|
}]);
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the NotificationAction class definition.
|
||||||
|
*/
|
||||||
|
angular.module('notification').factory('NotificationAction', [function defineNotificationAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationAction, which pairs an arbitrary callback with
|
||||||
|
* an action name. The name of this action will ultimately be presented to
|
||||||
|
* the user when the user is prompted to choose among available actions.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {String} name The name of this action.
|
||||||
|
*
|
||||||
|
* @param {Function} callback
|
||||||
|
* The callback to call when the user elects to perform this action.
|
||||||
|
*/
|
||||||
|
var NotificationAction = function NotificationAction(name, callback) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to this NotificationAction.
|
||||||
|
*
|
||||||
|
* @type NotificationAction
|
||||||
|
*/
|
||||||
|
var action = this;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of this action.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback to call when this action is performed.
|
||||||
|
*
|
||||||
|
* @type Function
|
||||||
|
*/
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the callback associated with this NotificationAction, if any.
|
||||||
|
* If no callback is associated with this NotificationAction, this
|
||||||
|
* function has no effect.
|
||||||
|
*/
|
||||||
|
this.performAction = function performAction() {
|
||||||
|
if (action.callback)
|
||||||
|
action.callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return NotificationAction;
|
||||||
|
|
||||||
|
}]);
|
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the NotificationCountdown class definition.
|
||||||
|
*/
|
||||||
|
angular.module('notification').factory('NotificationCountdown', [function defineNotificationCountdown() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationCountdown which describes an action that
|
||||||
|
* should be performed after a specific number of seconds has elapsed.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {String} text The body text of the notification countdown.
|
||||||
|
*
|
||||||
|
* @param {Number} remaining
|
||||||
|
* The number of seconds remaining in the countdown.
|
||||||
|
*
|
||||||
|
* @param {Function} [callback]
|
||||||
|
* The callback to call when the countdown elapses.
|
||||||
|
*/
|
||||||
|
var NotificationCountdown = function NotificationCountdown(text, remaining, callback) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to this NotificationCountdown.
|
||||||
|
*
|
||||||
|
* @type NotificationCountdown
|
||||||
|
*/
|
||||||
|
var countdown = this;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The body text of the notification countdown. For the sake of i18n,
|
||||||
|
* the variable REMAINING should be applied within the translation
|
||||||
|
* string for formatting plurals, etc.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of seconds remaining in the countdown. After this number
|
||||||
|
* of seconds elapses, the callback associated with this
|
||||||
|
* NotificationCountdown will be called.
|
||||||
|
*
|
||||||
|
* @type Number
|
||||||
|
*/
|
||||||
|
this.remaining = remaining;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback to call when this countdown expires.
|
||||||
|
*
|
||||||
|
* @type Function
|
||||||
|
*/
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the callback associated with this NotificationCountdown, if any.
|
||||||
|
* If no callback is associated with this NotificationCountdown, this
|
||||||
|
* function has no effect.
|
||||||
|
*/
|
||||||
|
this.performAction = function performAction() {
|
||||||
|
if (countdown.callback)
|
||||||
|
countdown.callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return NotificationCountdown;
|
||||||
|
|
||||||
|
}]);
|
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the NotificationProgress class definition.
|
||||||
|
*/
|
||||||
|
angular.module('notification').factory('NotificationProgress', [function defineNotificationProgress() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationProgress which describes the current status
|
||||||
|
* of an operation, and how much of that operation remains to be performed.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {String} text The text describing the operation progress.
|
||||||
|
*
|
||||||
|
* @param {Number} value
|
||||||
|
* The current state of operation progress, as an arbitrary number
|
||||||
|
* which increases as the operation continues.
|
||||||
|
*
|
||||||
|
* @param {String} [unit]
|
||||||
|
* The unit of the arbitrary value, if that value has an associated
|
||||||
|
* unit.
|
||||||
|
*
|
||||||
|
* @param {Number} [ratio]
|
||||||
|
* If known, the current status of the operation as a value between 0
|
||||||
|
* and 1 inclusive, where 0 is not yet started, and 1 is complete.
|
||||||
|
*/
|
||||||
|
var NotificationProgress = function NotificationProgress(text, value, unit, ratio) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text describing the operation progress. For the sake of i18n,
|
||||||
|
* the variable PROGRESS should be applied within the translation
|
||||||
|
* string for formatting plurals, etc., while UNIT should be used
|
||||||
|
* for the progress unit, if any.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current state of operation progress, as an arbitrary number which
|
||||||
|
* increases as the operation continues.
|
||||||
|
*
|
||||||
|
* @type Number
|
||||||
|
*/
|
||||||
|
this.value = value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unit of the arbitrary value, if that value has an associated
|
||||||
|
* unit.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.unit = unit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If known, the current status of the operation as a value between 0
|
||||||
|
* and 1 inclusive, where 0 is not yet started, and 1 is complete.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.ratio = ratio;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return NotificationProgress;
|
||||||
|
|
||||||
|
}]);
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* A module for code relating to permissions.
|
* A module for code relating to permissions.
|
||||||
*/
|
*/
|
||||||
angular.module('permission', []);
|
angular.module('permission', ['auth']);
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
/**
|
/**
|
||||||
* The DAO for permission operations agains the REST API.
|
* The DAO for permission operations agains the REST API.
|
||||||
*/
|
*/
|
||||||
angular.module('permission').factory('permissionDAO', ['$http', 'localStorageUtility',
|
angular.module('permission').factory('permissionDAO', ['$http', 'authenticationService',
|
||||||
function permissionDAO($http, localStorageUtility) {
|
function permissionDAO($http, authenticationService) {
|
||||||
|
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ angular.module('permission').factory('permissionDAO', ['$http', 'localStorageUti
|
|||||||
* @returns {promise} A promise for the HTTP call.
|
* @returns {promise} A promise for the HTTP call.
|
||||||
*/
|
*/
|
||||||
service.getPermissions = function getPermissions(userID) {
|
service.getPermissions = function getPermissions(userID) {
|
||||||
return $http.get("api/permission/" + userID + "/?token=" + localStorageUtility.get('authToken'));
|
return $http.get("api/permission/" + userID + "/?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,7 +50,7 @@ angular.module('permission').factory('permissionDAO', ['$http', 'localStorageUti
|
|||||||
* @returns {promise} A promise for the HTTP call.
|
* @returns {promise} A promise for the HTTP call.
|
||||||
*/
|
*/
|
||||||
service.addPermission = function addPermission(userID, permission) {
|
service.addPermission = function addPermission(userID, permission) {
|
||||||
return $http.post("api/permission/" + userID + "/?token=" + localStorageUtility.get('authToken'), permission);
|
return $http.post("api/permission/" + userID + "/?token=" + authenticationService.getCurrentToken(), permission);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ angular.module('permission').factory('permissionDAO', ['$http', 'localStorageUti
|
|||||||
* @returns {promise} A promise for the HTTP call.
|
* @returns {promise} A promise for the HTTP call.
|
||||||
*/
|
*/
|
||||||
service.removePermission = function removePermission(userID, permission) {
|
service.removePermission = function removePermission(userID, permission) {
|
||||||
return $http.post("api/permission/remove/" + userID + "/?token=" + localStorageUtility.get('authToken'), permission);
|
return $http.post("api/permission/remove/" + userID + "/?token=" + authenticationService.getCurrentToken(), permission);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ angular.module('permission').factory('permissionDAO', ['$http', 'localStorageUti
|
|||||||
// Make the HTTP call
|
// Make the HTTP call
|
||||||
return $http({
|
return $http({
|
||||||
method : 'PATCH',
|
method : 'PATCH',
|
||||||
url : "api/permission/?token=" + localStorageUtility.get('authToken'),
|
url : "api/permission/?token=" + authenticationService.getCurrentToken(),
|
||||||
data : permissionPatch
|
data : permissionPatch
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
/**
|
/**
|
||||||
* The DAO for connection operations agains the REST API.
|
* The DAO for connection operations agains the REST API.
|
||||||
*/
|
*/
|
||||||
angular.module('user').factory('userDAO', ['$http', 'localStorageUtility',
|
angular.module('user').factory('userDAO', ['$http', 'authenticationService',
|
||||||
function userDAO($http, localStorageUtility) {
|
function userDAO($http, authenticationService) {
|
||||||
|
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ angular.module('user').factory('userDAO', ['$http', 'localStorageUtility',
|
|||||||
* @returns {promise} A promise for the HTTP call.
|
* @returns {promise} A promise for the HTTP call.
|
||||||
*/
|
*/
|
||||||
service.getUsers = function getUsers() {
|
service.getUsers = function getUsers() {
|
||||||
return $http.get("api/user?token=" + localStorageUtility.get('authToken'));
|
return $http.get("api/user?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +47,7 @@ angular.module('user').factory('userDAO', ['$http', 'localStorageUtility',
|
|||||||
* @returns {promise} A promise for the HTTP call.
|
* @returns {promise} A promise for the HTTP call.
|
||||||
*/
|
*/
|
||||||
service.getUser = function getUser(userID) {
|
service.getUser = function getUser(userID) {
|
||||||
return $http.get("api/user/" + userID + "/?token=" + localStorageUtility.get('authToken'));
|
return $http.get("api/user/" + userID + "/?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,7 +61,7 @@ angular.module('user').factory('userDAO', ['$http', 'localStorageUtility',
|
|||||||
service.deleteUser = function deleteUser(user) {
|
service.deleteUser = function deleteUser(user) {
|
||||||
return $http['delete'](
|
return $http['delete'](
|
||||||
"api/user/" + user.username +
|
"api/user/" + user.username +
|
||||||
"?token=" + localStorageUtility.get('authToken'));
|
"?token=" + authenticationService.getCurrentToken());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ angular.module('user').factory('userDAO', ['$http', 'localStorageUtility',
|
|||||||
service.createUser = function createUser(user) {
|
service.createUser = function createUser(user) {
|
||||||
return $http.post(
|
return $http.post(
|
||||||
"api/user/"
|
"api/user/"
|
||||||
+ "?token=" + localStorageUtility.get('authToken'),
|
+ "?token=" + authenticationService.getCurrentToken(),
|
||||||
user
|
user
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ angular.module('user').factory('userDAO', ['$http', 'localStorageUtility',
|
|||||||
service.saveUser = function saveUser(user) {
|
service.saveUser = function saveUser(user) {
|
||||||
return $http.post(
|
return $http.post(
|
||||||
"api/user/" + user.username +
|
"api/user/" + user.username +
|
||||||
"?token=" + localStorageUtility.get('authToken'),
|
"?token=" + authenticationService.getCurrentToken(),
|
||||||
user);
|
user);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* A module for code relating to users.
|
* A module for code relating to users.
|
||||||
*/
|
*/
|
||||||
angular.module('user', []);
|
angular.module('user', ['auth']);
|
||||||
|
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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 handling storage and retrieval of values on localStorage.
|
|
||||||
* If local storage is not available, cookies will be used as a fallback.
|
|
||||||
*/
|
|
||||||
angular.module('util').factory('localStorageUtility', ['$cookieStore',
|
|
||||||
function localStorageUtility($cookieStore) {
|
|
||||||
|
|
||||||
var service = {};
|
|
||||||
|
|
||||||
// The prefix to use when storing cookies
|
|
||||||
var COOKIE_PREFIX = "guacamole.ui.localstorage.";
|
|
||||||
|
|
||||||
// Check if we can actually use localStorage
|
|
||||||
var localStorageEnabled;
|
|
||||||
try {
|
|
||||||
window.localStorage.setItem("test", "test");
|
|
||||||
window.localStorage.removeItem("test");
|
|
||||||
localStorageEnabled = true;
|
|
||||||
} catch(e) {
|
|
||||||
localStorageEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var getFunc, setFunc;
|
|
||||||
|
|
||||||
if(localStorageEnabled) {
|
|
||||||
|
|
||||||
// Just a passthrough to localStorage
|
|
||||||
getFunc = function getFromLocalStorage(key) {
|
|
||||||
return window.localStorage.getItem(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
setFunc = function setOnLocalStorage(key, value) {
|
|
||||||
return window.localStorage.setItem(key, value);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// Store the values as cookies
|
|
||||||
getFunc = function getValueFromCookie(key) {
|
|
||||||
return $cookieStore.get(COOKIE_PREFIX + key);
|
|
||||||
};
|
|
||||||
|
|
||||||
setFunc = function setValueOnCookie(key, value) {
|
|
||||||
return $cookieStore.put(COOKIE_PREFIX + key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a value from the persistent local store.
|
|
||||||
*
|
|
||||||
* @param {string} key The key to use as an index into the map.
|
|
||||||
*
|
|
||||||
* @returns {string} The value, if found.
|
|
||||||
*/
|
|
||||||
service.get = getFunc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a value on the persistent local store.
|
|
||||||
*
|
|
||||||
* @param {string} key The key to use as an index into the map.
|
|
||||||
* @param {string} value The value to store in the map.
|
|
||||||
*/
|
|
||||||
service.set = setFunc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear a value from the persistent local store.
|
|
||||||
*
|
|
||||||
* @param {string} key The key to clear from the map.
|
|
||||||
*/
|
|
||||||
service.clear = function clear(key) {
|
|
||||||
return service.set(key, undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
return service;
|
|
||||||
}]);
|
|
@@ -23,4 +23,4 @@
|
|||||||
/**
|
/**
|
||||||
* A module for miscellaneous services and utilities that don't belong elsewhere.
|
* A module for miscellaneous services and utilities that don't belong elsewhere.
|
||||||
*/
|
*/
|
||||||
angular.module('util', ['ngCookies']);
|
angular.module('util', []);
|
||||||
|
@@ -37,20 +37,7 @@ THE SOFTWARE.
|
|||||||
<!-- Global status/error dialog -->
|
<!-- Global status/error dialog -->
|
||||||
<div ng-class="{shown: status}" class="status-outer">
|
<div ng-class="{shown: status}" class="status-outer">
|
||||||
<div class="status-middle">
|
<div class="status-middle">
|
||||||
|
<guac-notification notification="status"/>
|
||||||
<guac-notification
|
|
||||||
class-name="status.className"
|
|
||||||
title="status.title"
|
|
||||||
text="status.text"
|
|
||||||
progress-text="status.progress.text"
|
|
||||||
progress-unit="status.progress.unit"
|
|
||||||
progress-ratio="status.progress.ratio"
|
|
||||||
progress="status.progress.value"
|
|
||||||
actions="status.actions"
|
|
||||||
countdown-text="status.countdown.text"
|
|
||||||
countdown="status.countdown.remaining"
|
|
||||||
default-callback="status.countdown.callback"/>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -60,20 +47,7 @@ THE SOFTWARE.
|
|||||||
<!-- Notification area -->
|
<!-- Notification area -->
|
||||||
<div id="notificationArea">
|
<div id="notificationArea">
|
||||||
<div ng-repeat="wrapper in notifications">
|
<div ng-repeat="wrapper in notifications">
|
||||||
|
<guac-notification notification="wrapper.notification"/>
|
||||||
<guac-notification
|
|
||||||
class-name="wrapper.notification.className"
|
|
||||||
title="wrapper.notification.title"
|
|
||||||
text="wrapper.notification.text"
|
|
||||||
progress-text="wrapper.notification.progress.text"
|
|
||||||
progress-unit="wrapper.notification.progress.unit"
|
|
||||||
progress-ratio="wrapper.notification.progress.ratio"
|
|
||||||
progress="wrapper.notification.progress.value"
|
|
||||||
actions="wrapper.notification.actions"
|
|
||||||
countdown-text="wrapper.notification.countdown.text"
|
|
||||||
countdown="wrapper.notification.countdown.remaining"
|
|
||||||
default-callback="wrapper.notification.countdown.callback"/>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -1,212 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set of thumbnails for each connection, indexed by ID.
|
|
||||||
*/
|
|
||||||
GuacamoleHistory = new (function() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to this GuacamoleHistory.
|
|
||||||
*/
|
|
||||||
var guac_history = this;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of entries to allow before removing old entries based on the
|
|
||||||
* cutoff.
|
|
||||||
*/
|
|
||||||
var IDEAL_LENGTH = 6;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum age of a history entry before it is removed, in
|
|
||||||
* milliseconds.
|
|
||||||
*/
|
|
||||||
var CUTOFF_AGE = 900000;
|
|
||||||
|
|
||||||
var history = {};
|
|
||||||
|
|
||||||
function truncate() {
|
|
||||||
|
|
||||||
// Build list of entries
|
|
||||||
var entries = [];
|
|
||||||
for (var old_id in history)
|
|
||||||
entries.push(history[old_id]);
|
|
||||||
|
|
||||||
// Avoid history growth beyond defined number of entries
|
|
||||||
if (entries.length > IDEAL_LENGTH) {
|
|
||||||
|
|
||||||
// Sort list
|
|
||||||
entries.sort(GuacamoleHistory.Entry.compare);
|
|
||||||
|
|
||||||
// Remove entries until length is ideal or all are recent
|
|
||||||
var now = new Date().getTime();
|
|
||||||
while (entries.length > IDEAL_LENGTH
|
|
||||||
&& now - entries[0].accessed > CUTOFF_AGE) {
|
|
||||||
|
|
||||||
// Remove entry
|
|
||||||
var removed = entries.shift();
|
|
||||||
delete history[removed.id];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the URL for the thumbnail of the connection with the given ID,
|
|
||||||
* or undefined if no thumbnail is associated with that connection.
|
|
||||||
*/
|
|
||||||
this.get = function(id) {
|
|
||||||
return history[id] || new GuacamoleHistory.Entry();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the thumbnail and access time of the history entry for the
|
|
||||||
* connection with the given ID.
|
|
||||||
*/
|
|
||||||
this.update = function(id, thumbnail) {
|
|
||||||
|
|
||||||
/* Do nothing if localStorage not present */
|
|
||||||
if (!localStorage)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Create updated entry
|
|
||||||
var entry = new GuacamoleHistory.Entry(
|
|
||||||
id,
|
|
||||||
thumbnail,
|
|
||||||
new Date().getTime()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Store entry in history
|
|
||||||
history[id] = entry;
|
|
||||||
truncate();
|
|
||||||
|
|
||||||
// Save updated history, ignore inability to use localStorage
|
|
||||||
try {
|
|
||||||
localStorage.setItem("GUAC_HISTORY", JSON.stringify(history));
|
|
||||||
}
|
|
||||||
catch (ignore) {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads all history data.
|
|
||||||
*/
|
|
||||||
this.reload = function() {
|
|
||||||
|
|
||||||
/* Do nothing if localStorage not present */
|
|
||||||
if (!localStorage)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get old and new for comparison, ignore inability to use localStorage
|
|
||||||
var old_history = history;
|
|
||||||
try {
|
|
||||||
var new_history = JSON.parse(localStorage.getItem("GUAC_HISTORY") || "{}");
|
|
||||||
}
|
|
||||||
catch (ignore) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update history
|
|
||||||
history = new_history;
|
|
||||||
|
|
||||||
// Call onchange handler as necessary
|
|
||||||
if (guac_history.onchange) {
|
|
||||||
|
|
||||||
// Produce union of all known IDs
|
|
||||||
var known_ids = {};
|
|
||||||
for (var new_id in new_history) known_ids[new_id] = true;
|
|
||||||
for (var old_id in old_history) known_ids[old_id] = true;
|
|
||||||
|
|
||||||
// For each known ID
|
|
||||||
for (var id in known_ids) {
|
|
||||||
|
|
||||||
// Get entries
|
|
||||||
var old_entry = old_history[id];
|
|
||||||
var new_entry = new_history[id];
|
|
||||||
|
|
||||||
// Call handler for all changed
|
|
||||||
if (!old_entry || !new_entry
|
|
||||||
|| old_entry.accessed != new_entry.accessed)
|
|
||||||
guac_history.onchange(id, old_entry, new_entry);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end onchange
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handler called whenever a history entry is changed.
|
|
||||||
*
|
|
||||||
* @event
|
|
||||||
* @param {String} id The ID of the connection whose history entry is
|
|
||||||
* changing.
|
|
||||||
* @param {GuacamoleHistory.Entry} old_entry The old value of the entry, if
|
|
||||||
* any.
|
|
||||||
* @param {GuacamoleHistory.Entry} new_entry The new value of the entry, if
|
|
||||||
* any.
|
|
||||||
*/
|
|
||||||
this.onchange = null;
|
|
||||||
|
|
||||||
// Reload when modified
|
|
||||||
window.addEventListener("storage", guac_history.reload, false);
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
guac_history.reload();
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A single entry in the indexed connection usage history.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @param {String} id The ID of this connection.
|
|
||||||
* @param {String} thumbnail The URL of the thumbnail to use to represent this
|
|
||||||
* connection.
|
|
||||||
* @param {Number} last_access The time this connection was last accessed, in
|
|
||||||
* seconds.
|
|
||||||
*/
|
|
||||||
GuacamoleHistory.Entry = function(id, thumbnail, last_access) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID of the connection associated with this history entry.
|
|
||||||
*/
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The thumbnail associated with the connection associated with this history
|
|
||||||
* entry.
|
|
||||||
*/
|
|
||||||
this.thumbnail = thumbnail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time the connection associated with this entry was last accessed.
|
|
||||||
*/
|
|
||||||
this.accessed = last_access;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
GuacamoleHistory.Entry.compare = function(a, b) {
|
|
||||||
return a.accessed - b.accessed;
|
|
||||||
};
|
|
@@ -1,171 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Global storage for Guacamole pages.
|
|
||||||
*/
|
|
||||||
GuacamoleSessionStorage = (function() {
|
|
||||||
|
|
||||||
// Retrieve storage from owner of window, if possible
|
|
||||||
var opener_storage = null;
|
|
||||||
try {
|
|
||||||
opener_storage = opener && opener.GuacamoleSessionStorage;
|
|
||||||
}
|
|
||||||
catch (e) {}
|
|
||||||
|
|
||||||
return opener_storage;
|
|
||||||
|
|
||||||
})() || new (function() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The contents of storage, as a JSON string containing name/value pairs as
|
|
||||||
* properties.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @type String
|
|
||||||
*/
|
|
||||||
var stored_json = "{}";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called whenever an item value changes.
|
|
||||||
*
|
|
||||||
* @callback onchange
|
|
||||||
* @param {String} name The name of the item changed.
|
|
||||||
* @param value The new item value.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All attached listeners.
|
|
||||||
*
|
|
||||||
* @type onchange[]
|
|
||||||
*/
|
|
||||||
var listeners = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies all listeners that an item has changed.
|
|
||||||
*
|
|
||||||
* @param {String} name The name of the item that changed.
|
|
||||||
* @param value The new item value.
|
|
||||||
*/
|
|
||||||
function __notify_changed(name, value) {
|
|
||||||
for (var i=0; i<listeners.length; i++)
|
|
||||||
listeners[i](name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value stored within the item having the given name.
|
|
||||||
*
|
|
||||||
* @param {String} name The name of the item to read.
|
|
||||||
* @param [value] The default value, if any.
|
|
||||||
* @return The value of the given item.
|
|
||||||
*/
|
|
||||||
this.getItem = function(name, value) {
|
|
||||||
|
|
||||||
// Attempt to read JSON from localStorage, default to local variable
|
|
||||||
var json = stored_json;
|
|
||||||
if (localStorage) {
|
|
||||||
try {
|
|
||||||
json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
|
||||||
}
|
|
||||||
catch (ignore) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = JSON.parse(json);
|
|
||||||
if (obj[name] !== undefined)
|
|
||||||
return obj[name];
|
|
||||||
|
|
||||||
return value;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the item having the given name to the given value.
|
|
||||||
*
|
|
||||||
* @param {String} name The name of the item to change.
|
|
||||||
* @param [value] An arbitrary value.
|
|
||||||
*/
|
|
||||||
this.setItem = function(name, value) {
|
|
||||||
|
|
||||||
// Attempt to read JSON from localStorage, default to local variable
|
|
||||||
var json = stored_json;
|
|
||||||
if (localStorage) {
|
|
||||||
try {
|
|
||||||
json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
|
||||||
}
|
|
||||||
catch (ignore) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify object property
|
|
||||||
var obj = JSON.parse(json);
|
|
||||||
var old = obj[name];
|
|
||||||
obj[name] = value;
|
|
||||||
|
|
||||||
// Notify of change
|
|
||||||
if (old !== value)
|
|
||||||
__notify_changed(name, value);
|
|
||||||
|
|
||||||
// Attempt to set JSON within localStorage, default to local variable
|
|
||||||
stored_json = JSON.stringify(obj);
|
|
||||||
if (localStorage) {
|
|
||||||
try {
|
|
||||||
localStorage.setItem("GUACAMOLE_STATE", stored_json);
|
|
||||||
}
|
|
||||||
catch (ignore) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reload when modified
|
|
||||||
window.addEventListener("storage", function reload() {
|
|
||||||
|
|
||||||
// Pull current state
|
|
||||||
var new_json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
|
||||||
|
|
||||||
var new_state = JSON.parse(new_json);
|
|
||||||
var old_state = JSON.parse(stored_json);
|
|
||||||
|
|
||||||
// Check if any values are different
|
|
||||||
for (var name in new_state) {
|
|
||||||
|
|
||||||
// If value changed, notify
|
|
||||||
var old = old_state[name];
|
|
||||||
if (old !== new_state[name])
|
|
||||||
__notify_changed(name, new_state[name]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stored_json = new_json;
|
|
||||||
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that the given function will be called for each change in
|
|
||||||
* item value. The function must accept a single argument which will be
|
|
||||||
* the name of the item changed.
|
|
||||||
*
|
|
||||||
* @param {onchange} onchange The function to call when an item changes.
|
|
||||||
*/
|
|
||||||
this.addChangeListener = function(onchange) {
|
|
||||||
listeners.push(onchange);
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
Reference in New Issue
Block a user