summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 18:29:30 +0000
committerjamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 18:29:30 +0000
commit088c4d0895fcc1ae30fe988057ab7866be303d74 (patch)
tree4e45ec7a368d94e4288878ad058a599a69c64c92
parent84ad47e975cb77a12881dc0c72053a22993950ca (diff)
downloadchromium_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
-rw-r--r--remoting/remoting.gyp8
-rw-r--r--remoting/resources/icon_cross.pngbin0 -> 2928 bytes
-rw-r--r--remoting/resources/icon_pencil.pngbin0 -> 3200 bytes
-rw-r--r--remoting/webapp/me2mom/choice.css30
-rw-r--r--remoting/webapp/me2mom/choice.html2
-rw-r--r--remoting/webapp/me2mom/client_screen.js41
-rw-r--r--remoting/webapp/me2mom/home_screen.js101
-rw-r--r--remoting/webapp/me2mom/host_list.js242
-rw-r--r--remoting/webapp/me2mom/host_table_entry.js189
-rw-r--r--remoting/webapp/me2mom/ui_mode.js2
-rw-r--r--remoting/webapp/me2mom/xhr.js134
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
new file mode 100644
index 0000000..87ac408
--- /dev/null
+++ b/remoting/resources/icon_cross.png
Binary files differ
diff --git a/remoting/resources/icon_pencil.png b/remoting/resources/icon_pencil.png
new file mode 100644
index 0000000..ba10005
--- /dev/null
+++ b/remoting/resources/icon_pencil.png
Binary files differ
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;
};