diff --git a/guacamole/src/main/webapp/index.xhtml b/guacamole/src/main/webapp/index.xhtml index 5fdd2bfa9..69f68a8c6 100644 --- a/guacamole/src/main/webapp/index.xhtml +++ b/guacamole/src/main/webapp/index.xhtml @@ -77,8 +77,8 @@

No recent connections.

-

Other Connections

-
+

All Connections

+

Clipboard

diff --git a/guacamole/src/main/webapp/scripts/history.js b/guacamole/src/main/webapp/scripts/history.js index ba6eb0d5f..d605bc984 100644 --- a/guacamole/src/main/webapp/scripts/history.js +++ b/guacamole/src/main/webapp/scripts/history.js @@ -3,8 +3,53 @@ */ GuacamoleHistory = new (function() { - var history = - JSON.parse(localStorage.getItem("GUAC_HISTORY") || "{}"); + /** + * 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() { + + // Avoid history growth beyond defined number of entries + if (history.length > IDEAL_LENGTH) { + + // Build list of entries + var entries = []; + for (var old_entry in history) + entries.push(old_entry); + + // Sort list + entries.sort(GuacamoleHistory.Entries.compare); + + // Remove entries until length is ideal or all are recent + var now = new Date().getTime(); + while (entries.length > IDEAL_LENGTH + && entries[0].accessed - now > 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, @@ -29,12 +74,67 @@ GuacamoleHistory = new (function() { // Store entry in history history[id] = entry; + truncate(); // Save updated history localStorage.setItem("GUAC_HISTORY", JSON.stringify(history)); }; + /** + * Reloads all history data. + */ + this.reload = function() { + + // Get old and new for comparison + var old_history = history; + var new_history = JSON.parse(localStorage.getItem("GUAC_HISTORY") || "{}"); + + // 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; + + // Initial load + guac_history.reload(); + })(); /** @@ -66,4 +166,7 @@ GuacamoleHistory.Entry = function(id, thumbnail, last_access) { this.accessed = last_access; }; - \ No newline at end of file + +GuacamoleHistory.Entry.compare = function(a, b) { + return a.accessed - b.accessed; +}; diff --git a/guacamole/src/main/webapp/scripts/root-ui.js b/guacamole/src/main/webapp/scripts/root-ui.js index 20945767e..60e4c2fdc 100644 --- a/guacamole/src/main/webapp/scripts/root-ui.js +++ b/guacamole/src/main/webapp/scripts/root-ui.js @@ -26,7 +26,7 @@ var GuacamoleRootUI = { "sections": { "login_form" : document.getElementById("login-form"), "recent_connections" : document.getElementById("recent-connections"), - "other_connections" : document.getElementById("other-connections") + "all_connections" : document.getElementById("all-connections") }, "messages": { @@ -148,6 +148,11 @@ GuacamoleRootUI.getConfigurations = function(parameters) { */ GuacamoleRootUI.Connection = function(config) { + /** + * The configuration associated with this connection. + */ + this.configuration = config; + function element(tagname, classname) { var new_element = document.createElement(tagname); new_element.className = classname; @@ -161,6 +166,7 @@ GuacamoleRootUI.Connection = function(config) { var name = element("span", "name"); var protocol_icon = element("div", "icon " + config.protocol); var thumbnail = element("div", "thumbnail"); + var thumb_img; // Get URL var url = "client.xhtml?id=" + encodeURIComponent(config.id); @@ -197,9 +203,9 @@ GuacamoleRootUI.Connection = function(config) { if (thumbnail_url) { // Create thumbnail element - var img = document.createElement("img"); - img.src = thumbnail_url; - thumbnail.appendChild(img); + thumb_img = document.createElement("img"); + thumb_img.src = thumbnail_url; + thumbnail.appendChild(thumb_img); } @@ -214,11 +220,59 @@ GuacamoleRootUI.Connection = function(config) { * Returns whether this connection has an associated thumbnail. */ this.hasThumbnail = function() { - return thumbnail_url && true; + return thumb_img && true; + }; + + /** + * Sets the thumbnail URL of this existing connection. Note that this will + * only work if the connection already had a thumbnail associated with it. + */ + this.setThumbnail = function(url) { + + // If no image element, create it + if (!thumb_img) { + thumb_img = document.createElement("img"); + thumb_img.src = url; + thumbnail.appendChild(thumb_img); + } + + // Otherwise, set source of existing + else + thumb_img.src = url; + }; }; +/** + * Set of all thumbnailed connections, indexed by ID. + */ +GuacamoleRootUI.thumbnailConnections = {}; + +/** + * Set of all configurations, indexed by ID. + */ +GuacamoleRootUI.configurations = {}; + +/** + * Adds the given connection to the recent connections list. + */ +GuacamoleRootUI.addRecentConnection = function(connection) { + + // Add connection object to list of thumbnailed connections + GuacamoleRootUI.thumbnailConnections[connection.configuration.id] = + connection; + + // Add connection to recent list + GuacamoleRootUI.sections.recent_connections.appendChild( + connection.getElement()); + + // Hide "No recent connections" message + GuacamoleRootUI.messages.no_recent_connections.style.display = "none"; + +}; + + /** * Resets the interface such that the login UI is displayed if * the user is not authenticated (or authentication fails) and @@ -249,26 +303,19 @@ GuacamoleRootUI.reset = function() { // Add connection icons for (var i=0; i