diff options
author | jamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-21 18:29:30 +0000 |
---|---|---|
committer | jamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-21 18:29:30 +0000 |
commit | 088c4d0895fcc1ae30fe988057ab7866be303d74 (patch) | |
tree | 4e45ec7a368d94e4288878ad058a599a69c64c92 /remoting | |
parent | 84ad47e975cb77a12881dc0c72053a22993950ca (diff) | |
download | chromium_src-088c4d0895fcc1ae30fe988057ab7866be303d74.zip chromium_src-088c4d0895fcc1ae30fe988057ab7866be303d74.tar.gz chromium_src-088c4d0895fcc1ae30fe988057ab7866be303d74.tar.bz2 |
Implement rename and delete.
BUG=None
TEST=Manual
Review URL: http://codereview.chromium.org/8587050
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110962 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/remoting.gyp | 8 | ||||
-rw-r--r-- | remoting/resources/icon_cross.png | bin | 0 -> 2928 bytes | |||
-rw-r--r-- | remoting/resources/icon_pencil.png | bin | 0 -> 3200 bytes | |||
-rw-r--r-- | remoting/webapp/me2mom/choice.css | 30 | ||||
-rw-r--r-- | remoting/webapp/me2mom/choice.html | 2 | ||||
-rw-r--r-- | remoting/webapp/me2mom/client_screen.js | 41 | ||||
-rw-r--r-- | remoting/webapp/me2mom/home_screen.js | 101 | ||||
-rw-r--r-- | remoting/webapp/me2mom/host_list.js | 242 | ||||
-rw-r--r-- | remoting/webapp/me2mom/host_table_entry.js | 189 | ||||
-rw-r--r-- | remoting/webapp/me2mom/ui_mode.js | 2 | ||||
-rw-r--r-- | remoting/webapp/me2mom/xhr.js | 134 |
11 files changed, 495 insertions, 254 deletions
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 7181001..03ba153 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -98,7 +98,9 @@ }], ], 'remoting_it2me_files': [ + 'resources/icon_cross.png', 'resources/icon_host.png', + 'resources/icon_pencil.png', 'resources/icon_warning.png', 'webapp/me2mom/choice.css', 'webapp/me2mom/choice.html', @@ -109,10 +111,10 @@ 'webapp/me2mom/debug_log.js', 'webapp/me2mom/dividerbottom.png', 'webapp/me2mom/dividertop.png', - 'webapp/me2mom/home_screen.js', 'webapp/me2mom/host_list.js', 'webapp/me2mom/host_screen.js', 'webapp/me2mom/host_session.js', + 'webapp/me2mom/host_table_entry.js', 'webapp/me2mom/l10n.js', 'webapp/me2mom/log_to_server.js', 'webapp/me2mom/main.css', @@ -362,7 +364,7 @@ 'webapp/verify-webapp.py', 'webapp/me2mom/_locales/en/messages.json', 'webapp/me2mom/choice.html', - 'webapp/me2mom/host_list.js', + 'webapp/me2mom/host_table_entry.js', 'webapp/me2mom/manifest.json', 'webapp/me2mom/remoting.js', 'host/plugin/host_script_object.cc', @@ -376,7 +378,7 @@ '<(PRODUCT_DIR)/remoting/it2me_verified.stamp', 'webapp/me2mom/_locales/en/messages.json', 'webapp/me2mom/choice.html', - 'webapp/me2mom/host_list.js', + 'webapp/me2mom/host_table_entry.js', 'webapp/me2mom/manifest.json', 'webapp/me2mom/remoting.js', 'host/plugin/host_script_object.cc', diff --git a/remoting/resources/icon_cross.png b/remoting/resources/icon_cross.png Binary files differnew file mode 100644 index 0000000..87ac408 --- /dev/null +++ b/remoting/resources/icon_cross.png diff --git a/remoting/resources/icon_pencil.png b/remoting/resources/icon_pencil.png Binary files differnew file mode 100644 index 0000000..ba10005 --- /dev/null +++ b/remoting/resources/icon_pencil.png diff --git a/remoting/webapp/me2mom/choice.css b/remoting/webapp/me2mom/choice.css index c74e07b..839524a 100644 --- a/remoting/webapp/me2mom/choice.css +++ b/remoting/webapp/me2mom/choice.css @@ -110,6 +110,10 @@ label { padding-bottom: 20px; } +.clickable { + cursor: pointer; +} + .description { margin-bottom: 25px; } @@ -201,10 +205,6 @@ label { color: black; } -.host-list-row, .host-list-row td { - -webkit-transition: all 0.5s; -} - .host-list-row td { border-bottom: 1px solid transparent; border-top: 1px solid transparent; @@ -214,6 +214,26 @@ label { background-color: #e7eef2; } +.host-list-rename-icon, .host-list-remove-icon { + opacity: 0; +} + +.host-list-row:hover .host-list-rename-icon { + opacity: 0.8; +} + +.host-list-row:hover .host-list-remove-icon { + opacity: 0.3; +} + +.host-list-edit:hover .host-list-rename-icon { + opacity: 1; +} + +.host-list-edit:hover .host-list-remove-icon { + opacity: 0.5; +} + .host-list-row:hover td { border-bottom: 1px solid #c6c6c6; border-top: 1px solid #c6c6c6; @@ -231,7 +251,7 @@ label { color: #a9a9a9; } -.host-offline img { +.host-offline .host-list-main-icon { opacity: 0.5; } diff --git a/remoting/webapp/me2mom/choice.html b/remoting/webapp/me2mom/choice.html index 9a8cc04..80ee8a7 100644 --- a/remoting/webapp/me2mom/choice.html +++ b/remoting/webapp/me2mom/choice.html @@ -18,10 +18,10 @@ found in the LICENSE file. <script src="client_screen.js"></script> <script src="client_session.js"></script> <script src="debug_log.js"></script> - <script src="home_screen.js"></script> <script src="host_list.js"></script> <script src="host_screen.js"></script> <script src="host_session.js"></script> + <script src="host_table_entry.js"></script> <script src="l10n.js"></script> <script src="log_to_server.js"></script> <script src="oauth2.js"></script> diff --git a/remoting/webapp/me2mom/client_screen.js b/remoting/webapp/me2mom/client_screen.js index 3342d95..ad69024 100644 --- a/remoting/webapp/me2mom/client_screen.js +++ b/remoting/webapp/me2mom/client_screen.js @@ -392,31 +392,30 @@ function recenterToolbar_() { * @return {void} Nothing. */ remoting.connectHost = function(hostId) { - for (var i = 0; i < remoting.hostList.hosts.length; ++i) { - /** @type {remoting.Host} */ - var host = remoting.hostList.hosts[i]; - if (host.hostId != hostId) - continue; + var hostTableEntry = remoting.hostList.getHostForId(hostId); + if (!hostTableEntry) { + console.error('connectHost: Unrecognised hostId: ' + hostId); + return; + } - remoting.hostJid = host.jabberId; - remoting.hostPublicKey = host.publicKey; - document.getElementById('connected-to').innerText = host.hostName; + remoting.hostJid = hostTableEntry.host.jabberId; + remoting.hostPublicKey = hostTableEntry.host.publicKey; + document.getElementById('connected-to').innerText = + hostTableEntry.host.hostName; - remoting.debug.log('Connecting to host...'); + remoting.debug.log('Connecting to host...'); - if (!remoting.wcsLoader) { - remoting.wcsLoader = new remoting.WcsLoader(); - } - /** @param {function(string):void} setToken The callback function. */ - var callWithToken = function(setToken) { - remoting.oauth2.callWithToken(setToken); - }; - remoting.wcsLoader.start( - remoting.oauth2.getAccessToken(), - callWithToken, - remoting.connectHostWithWcs); - break; + if (!remoting.wcsLoader) { + remoting.wcsLoader = new remoting.WcsLoader(); } + /** @param {function(string):void} setToken The callback function. */ + var callWithToken = function(setToken) { + remoting.oauth2.callWithToken(setToken); + }; + remoting.wcsLoader.start( + remoting.oauth2.getAccessToken(), + callWithToken, + remoting.connectHostWithWcs); } /** diff --git a/remoting/webapp/me2mom/home_screen.js b/remoting/webapp/me2mom/home_screen.js deleted file mode 100644 index 8660fce..0000000 --- a/remoting/webapp/me2mom/home_screen.js +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview - * Functions related to the 'home screen' for Chromoting. - */ - -'use strict'; - -/** @suppress {duplicate} */ -var remoting = remoting || {}; - -(function() { - -/** - * Query the Remoting Directory for the user's list of hosts. - * - * @return {void} Nothing. - */ -remoting.refreshHostList = function() { - // Fetch a new Access Token for the user, if necessary. - if (remoting.oauth2.needsNewAccessToken()) { - remoting.oauth2.refreshAccessToken(function(xhr) { - if (remoting.oauth2.needsNewAccessToken()) { - // Failed to get access token - console.error('refreshHostList: OAuth2 token fetch failed'); - remoting.hostList.showError(remoting.Error.AUTHENTICATION_FAILED); - return; - } - remoting.refreshHostList(); - }); - return; - } - - var headers = { - 'Authorization': 'OAuth ' + remoting.oauth2.getAccessToken() - }; - - var xhr = remoting.xhr.get( - 'https://www.googleapis.com/chromoting/v1/@me/hosts', - parseHostListResponse_, - '', - headers); -} - -/** - * Handle the results of the host list request. A success response will - * include a JSON-encoded list of host descriptions, which we display if we're - * able to successfully parse it. - * - * @param {XMLHttpRequest} xhr The XHR object for the host list request. - * @return {void} Nothing. - */ -function parseHostListResponse_(xhr) { - // Ignore host list responses if we're not on the Home screen. This mainly - // ensures that errors don't cause an unexpected mode switch. - if (remoting.currentMode != remoting.AppMode.HOME) { - return; - } - - if (xhr.readyState != 4) { - return; - } - - try { - if (xhr.status == 200) { - var parsed_response = - /** @type {{data: {items: Array}}} */ JSON.parse(xhr.responseText); - if (parsed_response.data && parsed_response.data.items) { - remoting.hostList.update(parsed_response.data.items); - } - } else { - // Some other error. Log for now, pretty-print in future. - console.error('Bad status on host list query: ', xhr); - var errorResponse = - /** @type {{error: {code: *, message: *}}} */ - JSON.parse(xhr.responseText); - if (errorResponse.error && - errorResponse.error.code && - errorResponse.error.message) { - remoting.debug.log('Error code ' + errorResponse.error.code); - remoting.debug.log('Error message ' + errorResponse.error.message); - } else { - remoting.debug.log('Error response: ' + xhr.responseText); - } - - // For most errors in the 4xx range, tell the user to re-authorize us. - if (xhr.status == 403) { - // The user's account is not enabled for Me2Me, so fail silently. - } else if (xhr.status >= 400 && xhr.status <= 499) { - remoting.hostList.showError(remoting.Error.GENERIC); - } - } - } catch (er) { - console.error('Error processing response: ', xhr); - } -} - -}());
\ No newline at end of file diff --git a/remoting/webapp/me2mom/host_list.js b/remoting/webapp/me2mom/host_list.js index d84d73e..8fd59c0 100644 --- a/remoting/webapp/me2mom/host_list.js +++ b/remoting/webapp/me2mom/host_list.js @@ -13,87 +13,128 @@ var remoting = remoting || {}; /** + * Create a host list consisting of the specified HTML elements, which should + * have a common parent that contains only host-list UI as it will be hidden + * if the host-list is empty. * @constructor + * @param {Element} table The HTML <table> to contain host-list. + * @param {Element} errorDiv The HTML <div> to display error messages. */ -remoting.Host = function() { - /** @type {string} */ - this.hostName = ''; - /** @type {string} */ - this.hostId = ''; - /** @type {string} */ - this.status = ''; - /** @type {string} */ - this.jabberId = ''; - /** @type {string} */ - this.publicKey = ''; -} +remoting.HostList = function(table, errorDiv) { + /** + * @type {Element} + * @private + */ + this.table_ = table; + /** + * @type {Element} + * @private + */ + this.errorDiv_ = errorDiv; + /** + * @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; +}; /** - * @constructor - * @param {Element} table The HTML <table> to contain host-list. - * @param {Element} errorDiv The HTML <div> to display error messages. + * Query the Remoting Directory for the user's list of hosts. + * + * @return {void} Nothing. */ -remoting.HostList = function(table, errorDiv) { - this.table = table; - this.errorDiv = errorDiv; - /** @type {Array.<remoting.Host>} */ - this.hosts = null; +remoting.HostList.prototype.refresh = function() { + /** @type {remoting.HostList} */ + var that = this; + /** @param {XMLHttpRequest} xhr */ + var parseHostListResponse = function(xhr) { + that.parseHostListResponse_(xhr); + } + /** @param {string} token */ + var getHosts = function(token) { + var headers = { 'Authorization': 'OAuth ' + token }; + remoting.xhr.get( + 'https://www.googleapis.com/chromoting/v1/@me/hosts', + parseHostListResponse, '', headers); + }; + remoting.oauth2.callWithToken(getHosts); +} + +/** + * Handle the results of the host list request. A success response will + * include a JSON-encoded list of host descriptions, which we display if we're + * able to successfully parse it. + * + * @param {XMLHttpRequest} xhr The XHR object for the host list request. + * @return {void} Nothing. + */ +remoting.HostList.prototype.parseHostListResponse_ = function(xhr) { + try { + if (xhr.status == 200) { + var parsed_response = + /** @type {{data: {items: Array}}} */ JSON.parse(xhr.responseText); + if (parsed_response.data && parsed_response.data.items) { + this.setHosts_(parsed_response.data.items); + } + } else { + // Some other error. + console.error('Bad status on host list query: ', xhr); + // For most errors in the 4xx range, tell the user to re-authorize us. + if (xhr.status == 403) { + // The user's account is not enabled for Me2Me, so fail silently. + } else if (xhr.status >= 400 && xhr.status <= 499) { + this.showError_(remoting.Error.GENERIC); + } + } + } catch (er) { + console.error('Error processing response: ', xhr, er); + } } /** * Refresh the host list with up-to-date details. * @param {Array.<remoting.Host>} hosts The new host list. * @return {void} Nothing. + * @private */ -remoting.HostList.prototype.update = function(hosts) { - this.table.innerHTML = ''; - this.showError(null); - this.hosts = hosts; +remoting.HostList.prototype.setHosts_ = function(hosts) { + this.table_.innerHTML = ''; + this.showError_(null); + this.hostTableEntries_ = []; + /** @type {remoting.HostList} */ + var that = this; for (var i = 0; i < hosts.length; ++i) { + /** @type {remoting.Host} */ var host = hosts[i]; - if (!host.hostName || !host.hostId || !host.status || !host.jabberId || - !host.publicKey) - continue; - - var hostEntry = document.createElement('tr'); - addClass(hostEntry, 'host-list-row'); - - var hostIcon = document.createElement('td'); - var hostIconImage = document.createElement('img'); - hostIconImage.src = 'icon_host.png'; - hostIcon.className = 'host-list-row-start'; - hostIcon.appendChild(hostIconImage); - hostEntry.appendChild(hostIcon); - - var hostName = document.createElement('td'); - hostName.setAttribute('class', 'mode-select-label'); - hostName.appendChild(document.createTextNode(host.hostName)); - hostEntry.appendChild(hostName); - - var hostStatus = document.createElement('td'); - if (host.status == 'ONLINE') { - var connectButton = document.createElement('button'); - connectButton.setAttribute('class', 'mode-select-button'); - connectButton.setAttribute('type', 'button'); - connectButton.setAttribute('onclick', - 'remoting.connectHost("'+host.hostId+'")'); - connectButton.innerHTML = - chrome.i18n.getMessage(/*i18n-content*/'CONNECT_BUTTON'); - hostStatus.appendChild(connectButton); - } else { - addClass(hostEntry, 'host-offline'); - hostStatus.innerHTML = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE'); + // Validate the entry to make sure it has all the fields we expect. + if (host.hostName && host.hostId && host.status && host.jabberId && + host.publicKey) { + var onRename = function() { that.renameHost_(host.hostId); } + var onDelete = function() { that.deleteHost_(host.hostId); } + var hostTableEntry = new remoting.HostTableEntry(); + hostTableEntry.init(host, onRename, onDelete); + this.hostTableEntries_[i] = hostTableEntry; + this.table_.appendChild(hostTableEntry.tableRow); } - hostStatus.className = 'host-list-row-end'; - hostEntry.appendChild(hostStatus); - - this.table.appendChild(hostEntry); } - this.showOrHide_(this.hosts.length != 0); -} + this.showOrHide_(this.hostTableEntries_.length != 0); +}; /** * Display a localized error message. @@ -101,16 +142,16 @@ remoting.HostList.prototype.update = function(hosts) { * previous error. * @return {void} Nothing. */ -remoting.HostList.prototype.showError = function(errorTag) { - this.table.innerHTML = ''; +remoting.HostList.prototype.showError_ = function(errorTag) { + this.table_.innerHTML = ''; if (errorTag) { - l10n.localizeElementFromTag(this.errorDiv, + l10n.localizeElementFromTag(this.errorDiv_, /** @type {string} */ (errorTag)); this.showOrHide_(true); } else { - this.errorDiv.innerText = ''; + this.errorDiv_.innerText = ''; } -} +}; /** * Show or hide the host-list UI. @@ -119,7 +160,7 @@ remoting.HostList.prototype.showError = function(errorTag) { * @private */ remoting.HostList.prototype.showOrHide_ = function(show) { - var parent = /** @type {Element} */ (this.table.parentNode); + var parent = /** @type {Element} */ (this.table_.parentNode); parent.hidden = !show; if (show) { parent.style.height = parent.scrollHeight + 'px'; @@ -127,7 +168,68 @@ remoting.HostList.prototype.showOrHide_ = function(show) { } else { addClass(parent, remoting.HostList.COLLAPSED_); } -} +}; + +/** + * Remove a host from the list, and deregister it. + * @param {string} hostId The id of the host to be removed. + * @return {void} Nothing. + * @private + */ +remoting.HostList.prototype.deleteHost_ = function(hostId) { + /** @type {remoting.HostTableEntry} */ + var hostTableEntry = this.getHostForId(hostId); + if (!hostTableEntry) { + console.error('No host registered for id ' + hostId); + return; + } + + this.table_.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); + } + 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. + * @private + */ +remoting.HostList.prototype.renameHost_ = function(hostId) { + /** @type {remoting.HostTableEntry} */ + var hostTableEntry = this.getHostForId(hostId); + if (!hostTableEntry) { + console.error('No host registered for id ' + hostId); + return; + } + /** @param {string} token */ + var renameHost = function(token) { + var headers = { + 'Authorization': 'OAuth ' + token, + 'Content-type' : 'application/json; charset=UTF-8' + }; + var newHostDetails = { data: hostTableEntry.host }; + remoting.xhr.put( + 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId, + function(xhr) {}, + JSON.stringify(newHostDetails), + headers); + } + remoting.oauth2.callWithToken(renameHost); +}; /** * Class name for the host list when it is collapsed. @@ -136,4 +238,4 @@ remoting.HostList.prototype.showOrHide_ = function(show) { remoting.HostList.COLLAPSED_ = 'collapsed'; /** @type {remoting.HostList} */ -remoting.hostList = null;
\ No newline at end of file +remoting.hostList = null; diff --git a/remoting/webapp/me2mom/host_table_entry.js b/remoting/webapp/me2mom/host_table_entry.js new file mode 100644 index 0000000..2415399 --- /dev/null +++ b/remoting/webapp/me2mom/host_table_entry.js @@ -0,0 +1,189 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * Class representing an entry in the host-list portion of the home screen. + */ + +'use strict'; + +/** @suppress {duplicate} */ +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() { + /** @type {string} */ + this.hostName = ''; + /** @type {string} */ + this.hostId = ''; + /** @type {string} */ + this.status = ''; + /** @type {string} */ + 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} @private */ + this.hostNameCell_ = null; + /** @type {function():void} @private */ + this.onRename_ = function() {}; +}; + +/** + * Create the HTML elements for this entry. + * @param {remoting.Host} host The host, as obtained from Apiary. + * @param {function():void} onRename Callback for rename operations. + * @param {function():void} onDelete Callback for delete operations. + */ +remoting.HostTableEntry.prototype.init = function(host, onRename, onDelete) { + this.host = host; + this.onRename_ = onRename; + + /** @type {remoting.HostTableEntry} */ + var that = this; + + this.tableRow = document.createElement('tr'); + addClass(this.tableRow, 'host-list-row'); + + // Create the host icon cell. + var hostIcon = document.createElement('td'); + addClass(hostIcon, 'host-list-row-start'); + var hostIconImage = document.createElement('img'); + hostIconImage.src = 'icon_host.png'; + addClass(hostIconImage, 'host-list-main-icon'); + hostIcon.appendChild(hostIconImage); + this.tableRow.appendChild(hostIcon); + + // Create the host name cell. + this.hostNameCell_ = document.createElement('td'); + addClass(this.hostNameCell_, 'mode-select-label'); + this.hostNameCell_.appendChild( + document.createTextNode(host.hostName)); + this.hostNameCell_.ondblclick = function() { that.beginRename_(); }; + this.tableRow.appendChild(this.hostNameCell_); + + // Create the host status cell. + var hostStatus = document.createElement('td'); + if (host.status == 'ONLINE') { + var connectButton = document.createElement('button'); + connectButton.setAttribute('class', 'mode-select-button'); + connectButton.setAttribute('type', 'button'); + connectButton.setAttribute('onclick', + 'remoting.connectHost("' + host.hostId + '")'); + connectButton.innerHTML = + chrome.i18n.getMessage(/*i18n-content*/'CONNECT_BUTTON'); + hostStatus.appendChild(connectButton); + } else { + addClass(this.tableRow, 'host-offline'); + hostStatus.innerHTML = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE'); + } + hostStatus.className = 'host-list-row-end'; + this.tableRow.appendChild(hostStatus); + + // Create the host rename cell. + var editButton = document.createElement('td'); + editButton.onclick = function() { that.beginRename_(); }; + 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); + this.tableRow.appendChild(editButton); + + // Create the host delete cell. + var removeButton = document.createElement('td'); + removeButton.onclick = onDelete; + 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); + this.tableRow.appendChild(removeButton); +}; + +/** + * Prepare the host for renaming by replacing its name with an edit box. + * @return {void} Nothing. + * @private + */ +remoting.HostTableEntry.prototype.beginRename_ = function() { + var editBox = /** @type {HTMLInputElement} */ document.createElement('input'); + editBox.type = 'text'; + editBox.value = this.host.hostName; + this.hostNameCell_.innerHTML = ''; + this.hostNameCell_.appendChild(editBox); + editBox.select(); + + /** @type {remoting.HostTableEntry} */ + var that = this; + editBox.onblur = function() { that.commitRename_(); }; + + /** @param {Event} event */ + var onKeydown = function(event) { that.onKeydown_(event); } + editBox.onkeydown = onKeydown; +}; + +/** + * Accept the hostname entered by the user. + * @return {void} Nothing. + * @private + */ +remoting.HostTableEntry.prototype.commitRename_ = function() { + var editBox = this.hostNameCell_.querySelector('input'); + if (editBox) { + if (this.host.hostName != editBox.value) { + this.host.hostName = editBox.value; + this.removeEditBox_(); + this.onRename_(); + return; + } + } +}; + +/** + * Remove the edit box corresponding to the specified host, and reset its name. + * @return {void} Nothing. + * @private + */ +remoting.HostTableEntry.prototype.removeEditBox_ = function() { + var editBox = this.hostNameCell_.querySelector('input'); + if (editBox) { + // onblur will fire when the edit box is removed, so remove the hook. + editBox.onblur = null; + } + this.hostNameCell_.innerHTML = ''; + this.hostNameCell_.appendChild(document.createTextNode(this.host.hostName)); +}; + +/** + * Handle a key event while the user is typing a host name + * @param {Event} event The keyboard event. + * @return {void} Nothing. + * @private + */ +remoting.HostTableEntry.prototype.onKeydown_ = function(event) { + if (event.which == 27) { // Escape + this.removeEditBox_(); + } else if (event.which == 13) { // Enter + this.commitRename_(); + } +}; diff --git a/remoting/webapp/me2mom/ui_mode.js b/remoting/webapp/me2mom/ui_mode.js index 7e00c56..db18317 100644 --- a/remoting/webapp/me2mom/ui_mode.js +++ b/remoting/webapp/me2mom/ui_mode.js @@ -68,7 +68,7 @@ remoting.setMode = function(mode) { document.addEventListener('keydown', remoting.DebugLog.onKeydown, false); } if (mode == remoting.AppMode.HOME) { - remoting.refreshHostList(); + remoting.hostList.refresh(); } }; diff --git a/remoting/webapp/me2mom/xhr.js b/remoting/webapp/me2mom/xhr.js index 46edcaa..2a48077 100644 --- a/remoting/webapp/me2mom/xhr.js +++ b/remoting/webapp/me2mom/xhr.js @@ -51,50 +51,8 @@ remoting.xhr.urlencodeParamHash = function(paramHash) { */ remoting.xhr.get = function(url, onDone, opt_parameters, opt_headers, opt_withCredentials) { - /** @type {XMLHttpRequest} */ - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (xhr.readyState != 4) { - return; - } - onDone(xhr); - }; - - // Add parameters into URL. - if (typeof(opt_parameters) === 'string') { - if (opt_parameters.length > 0) { - url = url + '?' + opt_parameters; - } - } else if (typeof(opt_parameters) === 'object') { - var paramString = remoting.xhr.urlencodeParamHash(opt_parameters); - if (paramString.length > 0) { - url = url + '?' + paramString; - } - } else if (opt_parameters === undefined) { - // No problem here. Do nothing. - } else { - throw 'opt_parameters must be string or associated array.'; - } - - xhr.open('GET', url, true); - - // Add in request headers. - if (typeof(opt_headers) === 'object') { - for (var key in opt_headers) { - xhr.setRequestHeader(key, opt_headers[key]); - } - } else if (opt_headers === undefined) { - // No problem here. Do nothing. - } else { - throw 'opt_headers must be associative array.'; - } - - if (opt_withCredentials) { - xhr.withCredentials = true; - } - - xhr.send(null); - return xhr; + return remoting.xhr.doMethod('GET', url, onDone, opt_parameters, + opt_headers, opt_withCredentials); }; /** @@ -110,10 +68,75 @@ remoting.xhr.get = function(url, onDone, opt_parameters, opt_headers, * request. * @param {boolean=} opt_withCredentials Set the withCredentials flags in the * XHR. - * @return {void} Nothing. + * @return {XMLHttpRequest} The request object. */ remoting.xhr.post = function(url, onDone, opt_parameters, opt_headers, opt_withCredentials) { + return remoting.xhr.doMethod('POST', url, onDone, opt_parameters, + opt_headers, opt_withCredentials); +}; + +/** + * Execute an XHR DELETE asynchronously. + * + * @param {string} url The base URL to DELETE, excluding parameters. + * @param {function(XMLHttpRequest):void} onDone The function to call on + * completion. + * @param {(string|Object.<string>)=} opt_parameters The request parameters, + * either as an associative array, or a string. If it is a string, be + * sure it is correctly URLEncoded. + * @param {Object.<string>=} opt_headers Additional headers to include on the + * request. + * @param {boolean=} opt_withCredentials Set the withCredentials flags in the + * XHR. + * @return {XMLHttpRequest} The request object. + */ +remoting.xhr.remove = function(url, onDone, opt_parameters, opt_headers, + opt_withCredentials) { + return remoting.xhr.doMethod('DELETE', url, onDone, opt_parameters, + opt_headers, opt_withCredentials); +}; + +/** + * Execute an XHR PUT asynchronously. + * + * @param {string} url The base URL to PUT, excluding parameters. + * @param {function(XMLHttpRequest):void} onDone The function to call on + * completion. + * @param {(string|Object.<string>)=} opt_parameters The request parameters, + * either as an associative array, or a string. If it is a string, be + * sure it is correctly URLEncoded. + * @param {Object.<string>=} opt_headers Additional headers to include on the + * request. + * @param {boolean=} opt_withCredentials Set the withCredentials flags in the + * XHR. + * @return {XMLHttpRequest} The request object. + */ +remoting.xhr.put = function(url, onDone, opt_parameters, opt_headers, + opt_withCredentials) { + return remoting.xhr.doMethod('PUT', url, onDone, opt_parameters, + opt_headers, opt_withCredentials); +}; + +/** + * Execute an arbitrary HTTP method asynchronously. + * + * @param {string} methodName The HTTP method name, e.g. "GET", "POST" etc. + * @param {string} url The base URL, excluding parameters. + * @param {function(XMLHttpRequest):void} onDone The function to call on + * completion. + * @param {(string|Object.<string>)=} opt_parameters The request parameters, + * either as an associative array, or a string. If it is a string, be + * sure it is correctly URLEncoded. + * @param {Object.<string>=} opt_headers Additional headers to include on the + * request. + * @param {boolean=} opt_withCredentials Set the withCredentials flags in the + * XHR. + * @return {XMLHttpRequest} The XMLHttpRequest object. + */ +remoting.xhr.doMethod = function(methodName, url, onDone, + opt_parameters, opt_headers, + opt_withCredentials) { /** @type {XMLHttpRequest} */ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { @@ -123,21 +146,27 @@ remoting.xhr.post = function(url, onDone, opt_parameters, opt_headers, onDone(xhr); }; - // Add parameters into URL. - var postData = ''; + var parameterString = ''; if (typeof(opt_parameters) === 'string') { - postData = opt_parameters; + parameterString = opt_parameters; } else if (typeof(opt_parameters) === 'object') { - postData = remoting.xhr.urlencodeParamHash(opt_parameters); + parameterString = remoting.xhr.urlencodeParamHash(opt_parameters); } else if (opt_parameters === undefined) { // No problem here. Do nothing. } else { throw 'opt_parameters must be string or associated array.'; } - xhr.open('POST', url, true); - xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + var useBody = (methodName == 'POST') || (methodName == 'PUT'); + + if (!useBody && parameterString != '') { + url = url + '?' + parameterString; + } + xhr.open(methodName, url, true); + if (methodName == 'POST') { + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + } // Add in request headers. if (typeof(opt_headers) === 'object') { for (var key in opt_headers) { @@ -153,5 +182,6 @@ remoting.xhr.post = function(url, onDone, opt_parameters, opt_headers, xhr.withCredentials = true; } - xhr.send(postData); + xhr.send(useBody ? parameterString : null); + return xhr; }; |