Chromium Code Reviews| Index: remoting/webapp/me2mom/host_list.js |
| diff --git a/remoting/webapp/me2mom/host_list.js b/remoting/webapp/me2mom/host_list.js |
| index d84d73edf7d8119dc0348e45707d700abea0e109..715471a5dabfe889317a6166cfe9d9c9fc2d89b3 100644 |
| --- a/remoting/webapp/me2mom/host_list.js |
| +++ b/remoting/webapp/me2mom/host_list.js |
| @@ -13,6 +13,9 @@ |
| var remoting = remoting || {}; |
| /** |
| + * The deserialized form of the chromoting host as returned by Apiary. |
| + * Note that the object has more fields than are detailed below--these |
| + * are just the ones that we refer to directly. |
| * @constructor |
| */ |
| remoting.Host = function() { |
| @@ -26,8 +29,20 @@ remoting.Host = function() { |
| this.jabberId = ''; |
| /** @type {string} */ |
| this.publicKey = ''; |
| -} |
| +}; |
| +/** |
| + * An entry in the host table. |
| + * @constructor |
| + */ |
| +remoting.HostTableEntry = function() { |
| + /** @type {remoting.Host} */ |
| + this.host = null; |
| + /** @type {Element} */ |
| + this.tableRow = null; |
| + /** @type {Element} */ |
| + this.hostNameCell = null; |
| +}; |
|
Jamie
2011/11/17 21:42:09
I separated out the raw object (Host) we get back
simonmorris
2011/11/17 23:59:48
Maybe HostModel and HostView would be more familia
Jamie
2011/11/18 22:54:57
It's not really MVC pattern though. I'd prefer to
|
| /** |
| * @constructor |
| @@ -37,9 +52,26 @@ remoting.Host = function() { |
| remoting.HostList = function(table, errorDiv) { |
| this.table = table; |
| this.errorDiv = errorDiv; |
| - /** @type {Array.<remoting.Host>} */ |
| - this.hosts = null; |
| -} |
| + /** |
| + * @type {Array.<remoting.HostTableEntry>} |
| + * @private |
| + */ |
| + this.hostTableEntries = null; |
| +}; |
| + |
| +/** |
| + * Search the host list for a host with the specified id. |
| + * @param {string} hostId The unique id of the host. |
| + * @return {remoting.HostTableEntry?} The host table entry, if any. |
| + */ |
| +remoting.HostList.prototype.getHostForId = function(hostId) { |
| + for (var i = 0; i < this.hostTableEntries.length; ++i) { |
| + if (this.hostTableEntries[i].host.hostId == hostId) { |
| + return this.hostTableEntries[i]; |
| + } |
| + } |
| + return null; |
| +}; |
| /** |
| * Refresh the host list with up-to-date details. |
| @@ -49,28 +81,38 @@ remoting.HostList = function(table, errorDiv) { |
| remoting.HostList.prototype.update = function(hosts) { |
| this.table.innerHTML = ''; |
| this.showError(null); |
| - this.hosts = hosts; |
| + this.hostTableEntries = []; |
| for (var i = 0; i < hosts.length; ++i) { |
| var host = hosts[i]; |
| + // Validate the entry to make sure it has all the fields we expect. |
| if (!host.hostName || !host.hostId || !host.status || !host.jabberId || |
| !host.publicKey) |
| continue; |
| - var hostEntry = document.createElement('tr'); |
| - addClass(hostEntry, 'host-list-row'); |
| + var hostTableEntry = new remoting.HostTableEntry(); |
| + hostTableEntry.host = host; |
| + |
| + hostTableEntry.tableRow = document.createElement('tr'); |
| + addClass(hostTableEntry.tableRow, 'host-list-row'); |
| var hostIcon = document.createElement('td'); |
| + addClass(hostIcon, 'host-list-row-start'); |
| var hostIconImage = document.createElement('img'); |
| hostIconImage.src = 'icon_host.png'; |
| - hostIcon.className = 'host-list-row-start'; |
| + addClass(hostIconImage, 'host-list-main-icon'); |
| hostIcon.appendChild(hostIconImage); |
| - hostEntry.appendChild(hostIcon); |
| + hostTableEntry.tableRow.appendChild(hostIcon); |
| - var hostName = document.createElement('td'); |
| - hostName.setAttribute('class', 'mode-select-label'); |
| - hostName.appendChild(document.createTextNode(host.hostName)); |
| - hostEntry.appendChild(hostName); |
| + hostTableEntry.hostNameCell = document.createElement('td'); |
| + hostTableEntry.hostNameCell.setAttribute('class', 'mode-select-label'); |
| + hostTableEntry.hostNameCell.appendChild( |
| + document.createTextNode(host.hostName)); |
| + hostTableEntry.hostNameCell.setAttribute( |
| + 'ondblclick', |
| + 'remoting.hostList.beginRenameHost("' + |
| + host.hostId + '"); return false;'); |
| + hostTableEntry.tableRow.appendChild(hostTableEntry.hostNameCell); |
| var hostStatus = document.createElement('td'); |
| if (host.status == 'ONLINE') { |
| @@ -78,22 +120,45 @@ remoting.HostList.prototype.update = function(hosts) { |
| connectButton.setAttribute('class', 'mode-select-button'); |
| connectButton.setAttribute('type', 'button'); |
| connectButton.setAttribute('onclick', |
| - 'remoting.connectHost("'+host.hostId+'")'); |
| + 'remoting.connectHost("' + host.hostId + '")'); |
| connectButton.innerHTML = |
| chrome.i18n.getMessage(/*i18n-content*/'CONNECT_BUTTON'); |
| hostStatus.appendChild(connectButton); |
| } else { |
| - addClass(hostEntry, 'host-offline'); |
| + addClass(hostTableEntry.tableRow, 'host-offline'); |
| hostStatus.innerHTML = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE'); |
| } |
| hostStatus.className = 'host-list-row-end'; |
| - hostEntry.appendChild(hostStatus); |
| + hostTableEntry.tableRow.appendChild(hostStatus); |
| - this.table.appendChild(hostEntry); |
| + var editButton = document.createElement('td'); |
| + editButton.setAttribute('onclick', 'remoting.hostList.beginRenameHost("' + |
| + host.hostId + '")'); |
| + addClass(editButton, 'clickable'); |
| + addClass(editButton, 'host-list-edit'); |
| + var penImage = document.createElement('img'); |
| + penImage.src = 'icon_pencil.png'; |
| + addClass(penImage, 'host-list-rename-icon'); |
| + editButton.appendChild(penImage); |
| + hostTableEntry.tableRow.appendChild(editButton); |
| + |
| + var removeButton = document.createElement('td'); |
| + removeButton.setAttribute('onclick', 'remoting.hostList.removeHost("' + |
| + host.hostId + '")'); |
| + addClass(removeButton, 'clickable'); |
| + addClass(removeButton, 'host-list-edit'); |
| + var crossImage = document.createElement('img'); |
| + crossImage.src = 'icon_cross.png'; |
| + addClass(crossImage, 'host-list-remove-icon'); |
| + removeButton.appendChild(crossImage); |
| + hostTableEntry.tableRow.appendChild(removeButton); |
|
simonmorris
2011/11/17 23:59:48
Clearer to make this new code a method of HostTabl
Jamie
2011/11/18 22:54:57
Good point, thanks for spotting this. I've refacto
|
| + |
| + this.hostTableEntries[i] = hostTableEntry; |
| + this.table.appendChild(hostTableEntry.tableRow); |
| } |
| - this.showOrHide_(this.hosts.length != 0); |
| -} |
| + this.showOrHide_(this.hostTableEntries.length != 0); |
| +}; |
| /** |
| * Display a localized error message. |
| @@ -110,7 +175,7 @@ remoting.HostList.prototype.showError = function(errorTag) { |
| } else { |
| this.errorDiv.innerText = ''; |
| } |
| -} |
| +}; |
| /** |
| * Show or hide the host-list UI. |
| @@ -127,7 +192,142 @@ remoting.HostList.prototype.showOrHide_ = function(show) { |
| } else { |
| addClass(parent, remoting.HostList.COLLAPSED_); |
| } |
| -} |
| +}; |
| + |
| +/** |
| + * Remove a host from the list, and deregister it. |
|
simonmorris
2011/11/17 23:59:48
Won't the user expect this to have some effect on
Jamie
2011/11/18 22:54:57
I think the long-term plan is to have hosts shut d
|
| + * @param {string} hostId The id of the host to be removed. |
| + * @return {void} Nothing. |
| + */ |
| +remoting.HostList.prototype.removeHost = function(hostId) { |
| + /** @type {remoting.HostTableEntry} */ |
| + var hostTableEntry = this.getHostForId(hostId); |
| + if (!hostTableEntry) { |
| + console.error('No host registered for id ' + hostId); |
| + return; |
| + } |
| + hostTableEntry.tableRow.parentElement.removeChild(hostTableEntry.tableRow); |
| + var index = this.hostTableEntries.indexOf(hostTableEntry); |
| + if (index != -1) { // Since we've just found it, index must be >= 0 |
| + this.hostTableEntries.splice(index, 1); |
| + } |
| + |
| + /** @param {string} token */ |
| + var deleteHost = function(token) { |
| + var headers = { 'Authorization': 'OAuth ' + token }; |
| + remoting.xhr.remove( |
| + 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId, |
| + function() {}, '', headers); |
|
Wez
2011/11/17 22:58:58
Why not refresh the host list when the XHR complet
Jamie
2011/11/18 22:54:57
I'm not sure about that. It's only going to make a
|
| + } |
| + remoting.oauth2.callWithToken(deleteHost); |
| + |
| + this.showOrHide_(this.hostTableEntries.length != 0); |
| +}; |
| + |
| +/** |
| + * Prepare a host for renaming by replacing its name with an edit box. |
| + * @param {string} hostId The id of the host to be renamed. |
| + * @return {void} Nothing. |
| + */ |
| +remoting.HostList.prototype.beginRenameHost = function(hostId) { |
| + /** @type {remoting.HostTableEntry} */ |
| + var hostTableEntry = this.getHostForId(hostId); |
| + if (!hostTableEntry) { |
| + console.error('No host registered for id ' + hostId); |
| + return; |
| + } |
| + var editBox = /** @type {HTMLInputElement} */ document.createElement('input'); |
| + editBox.type = 'text'; |
| + editBox.value = hostTableEntry.host.hostName; |
| + editBox.setAttribute('onblur', |
| + 'remoting.hostList.commitRenameHost("' + hostId + '");'); |
| + /** @type {remoting.HostList} */ |
| + var that = this; |
| + /** @param {Event} event */ |
| + var onKeydown = function(event) { |
| + that.onKeydown(event, hostId); |
| + } |
| + editBox.onkeydown = onKeydown; |
| + hostTableEntry.hostNameCell.innerHTML = ''; |
| + hostTableEntry.hostNameCell.appendChild(editBox); |
| + editBox.select(); |
|
simonmorris
2011/11/17 23:59:48
This could be a method of HostView.
Jamie
2011/11/18 22:54:57
My refactoring addresses this.
|
| +}; |
| + |
| +/** |
| + * Remove the edit box corresponding to the specified host, and reset its name. |
| + * @param {remoting.HostTableEntry} hostTableEntry The host being renamed. |
| + * @return {void} Nothing. |
| + */ |
| +remoting.HostList.prototype.removeEditBox = function(hostTableEntry) { |
| + var editBox = hostTableEntry.hostNameCell.querySelector('input'); |
| + if (editBox) { |
| + // onblur will fire when the edit box is removed, so remove the hook. |
| + editBox.onblur = null; |
| + } |
| + hostTableEntry.hostNameCell.innerHTML = ''; |
| + hostTableEntry.hostNameCell.appendChild( |
| + document.createTextNode(hostTableEntry.host.hostName)); |
| +}; |
| + |
| +/** |
| + * Accept the host name entered by the user. |
| + * @param {string} hostId The id of the host to be renamed. |
| + * @return {void} Nothing. |
| + */ |
| +remoting.HostList.prototype.commitRenameHost = function(hostId) { |
| + /** @type {remoting.HostTableEntry} */ |
| + var hostTableEntry = this.getHostForId(hostId); |
| + if (hostTableEntry) { |
| + var editBox = hostTableEntry.hostNameCell.querySelector('input'); |
| + if (editBox) { |
| + if (hostTableEntry.host.hostName != editBox.value) { |
| + hostTableEntry.host.hostName = editBox.value; |
| + hostTableEntry.host.status = 'OFFLINE'; |
|
simonmorris
2011/11/17 23:59:48
Why is the status set here?
Jamie
2011/11/18 22:54:57
Left-over debugging code. Removed.
|
| + /** @param {string} token */ |
| + var renameHost = function(token) { |
| + var headers = { |
| + 'Authorization': 'OAuth ' + token, |
| + 'content-type' : 'application/json; charset=UTF-8' |
|
Wez
2011/11/17 22:58:58
nit: content-type capitalization.
Jamie
2011/11/18 22:54:57
Done.
|
| + }; |
| + var newHostDetails = { data: hostTableEntry.host }; |
| + remoting.xhr.put( |
| + 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId, |
| + function(xhr) {}, |
|
Wez
2011/11/17 22:58:58
Why not refresh when the XHR returns?
Jamie
2011/11/18 22:54:57
Same argument as above.
|
| + JSON.stringify(newHostDetails), |
| + headers); |
| + } |
| + remoting.oauth2.callWithToken(renameHost); |
| + } |
| + } |
| + } |
| + this.removeEditBox(hostTableEntry); |
| +}; |
| + |
| +/** |
| + * Revert the host name, ignoring changes made by the user. |
| + * @param {string} hostId The id of the host to be renamed. |
| + * @return {void} Nothing. |
| + */ |
| +remoting.HostList.prototype.cancelRenameHost = function(hostId) { |
| + var host = this.getHostForId(hostId); |
|
Wez
2011/11/17 22:58:58
How can the user have cancelled editing if the hos
Jamie
2011/11/18 22:54:57
It's just a sanity check. I'd make it a DCHECK if
|
| + if (host) { |
| + this.removeEditBox(host); |
| + } |
| +}; |
| + |
| +/** |
| + * Handle a key event while the user is typing a host name |
| + * @param {Event} event The keyboard event. |
| + * @param {string} hostId The id of the host being renamed. |
| + * @return {void} Nothing. |
| + */ |
| +remoting.HostList.prototype.onKeydown = function(event, hostId) { |
| + if (event.which == 27) { // Escape |
| + this.cancelRenameHost(hostId); |
| + } else if (event.which == 13) { // Enter |
| + this.commitRenameHost(hostId); |
| + } |
| +}; |
| /** |
| * Class name for the host list when it is collapsed. |
| @@ -136,4 +336,4 @@ remoting.HostList.prototype.showOrHide_ = function(show) { |
| remoting.HostList.COLLAPSED_ = 'collapsed'; |
| /** @type {remoting.HostList} */ |
| -remoting.hostList = null; |
| +remoting.hostList = null; |
|
Wez
2011/11/17 22:58:58
Newline change?
Jamie
2011/11/18 22:54:57
Yep. The original was missing a newline.
|