Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: remoting/webapp/me2mom/host_list.js

Issue 8587050: Implement rename and delete. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleaned up CSS. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 6 * @fileoverview
7 * Class representing the host-list portion of the home screen UI. 7 * Class representing the host-list portion of the home screen UI.
8 */ 8 */
9 9
10 'use strict'; 10 'use strict';
11 11
12 /** @suppress {duplicate} */ 12 /** @suppress {duplicate} */
13 var remoting = remoting || {}; 13 var remoting = remoting || {};
14 14
15 /** 15 /**
16 * The deserialized form of the chromoting host as returned by Apiary.
17 * Note that the object has more fields than are detailed below--these
18 * are just the ones that we refer to directly.
16 * @constructor 19 * @constructor
17 */ 20 */
18 remoting.Host = function() { 21 remoting.Host = function() {
19 /** @type {string} */ 22 /** @type {string} */
20 this.hostName = ''; 23 this.hostName = '';
21 /** @type {string} */ 24 /** @type {string} */
22 this.hostId = ''; 25 this.hostId = '';
23 /** @type {string} */ 26 /** @type {string} */
24 this.status = ''; 27 this.status = '';
25 /** @type {string} */ 28 /** @type {string} */
26 this.jabberId = ''; 29 this.jabberId = '';
27 /** @type {string} */ 30 /** @type {string} */
28 this.publicKey = ''; 31 this.publicKey = '';
29 } 32 };
30 33
34 /**
35 * An entry in the host table.
36 * @constructor
37 */
38 remoting.HostTableEntry = function() {
39 /** @type {remoting.Host} */
40 this.host = null;
41 /** @type {Element} */
42 this.tableRow = null;
43 /** @type {Element} */
44 this.hostNameCell = null;
45 };
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
31 46
32 /** 47 /**
33 * @constructor 48 * @constructor
34 * @param {Element} table The HTML <table> to contain host-list. 49 * @param {Element} table The HTML <table> to contain host-list.
35 * @param {Element} errorDiv The HTML <div> to display error messages. 50 * @param {Element} errorDiv The HTML <div> to display error messages.
36 */ 51 */
37 remoting.HostList = function(table, errorDiv) { 52 remoting.HostList = function(table, errorDiv) {
38 this.table = table; 53 this.table = table;
39 this.errorDiv = errorDiv; 54 this.errorDiv = errorDiv;
40 /** @type {Array.<remoting.Host>} */ 55 /**
41 this.hosts = null; 56 * @type {Array.<remoting.HostTableEntry>}
42 } 57 * @private
58 */
59 this.hostTableEntries = null;
60 };
61
62 /**
63 * Search the host list for a host with the specified id.
64 * @param {string} hostId The unique id of the host.
65 * @return {remoting.HostTableEntry?} The host table entry, if any.
66 */
67 remoting.HostList.prototype.getHostForId = function(hostId) {
68 for (var i = 0; i < this.hostTableEntries.length; ++i) {
69 if (this.hostTableEntries[i].host.hostId == hostId) {
70 return this.hostTableEntries[i];
71 }
72 }
73 return null;
74 };
43 75
44 /** 76 /**
45 * Refresh the host list with up-to-date details. 77 * Refresh the host list with up-to-date details.
46 * @param {Array.<remoting.Host>} hosts The new host list. 78 * @param {Array.<remoting.Host>} hosts The new host list.
47 * @return {void} Nothing. 79 * @return {void} Nothing.
48 */ 80 */
49 remoting.HostList.prototype.update = function(hosts) { 81 remoting.HostList.prototype.update = function(hosts) {
50 this.table.innerHTML = ''; 82 this.table.innerHTML = '';
51 this.showError(null); 83 this.showError(null);
52 this.hosts = hosts; 84 this.hostTableEntries = [];
53 85
54 for (var i = 0; i < hosts.length; ++i) { 86 for (var i = 0; i < hosts.length; ++i) {
55 var host = hosts[i]; 87 var host = hosts[i];
88 // Validate the entry to make sure it has all the fields we expect.
56 if (!host.hostName || !host.hostId || !host.status || !host.jabberId || 89 if (!host.hostName || !host.hostId || !host.status || !host.jabberId ||
57 !host.publicKey) 90 !host.publicKey)
58 continue; 91 continue;
59 92
60 var hostEntry = document.createElement('tr'); 93 var hostTableEntry = new remoting.HostTableEntry();
61 addClass(hostEntry, 'host-list-row'); 94 hostTableEntry.host = host;
95
96 hostTableEntry.tableRow = document.createElement('tr');
97 addClass(hostTableEntry.tableRow, 'host-list-row');
62 98
63 var hostIcon = document.createElement('td'); 99 var hostIcon = document.createElement('td');
100 addClass(hostIcon, 'host-list-row-start');
64 var hostIconImage = document.createElement('img'); 101 var hostIconImage = document.createElement('img');
65 hostIconImage.src = 'icon_host.png'; 102 hostIconImage.src = 'icon_host.png';
66 hostIcon.className = 'host-list-row-start'; 103 addClass(hostIconImage, 'host-list-main-icon');
67 hostIcon.appendChild(hostIconImage); 104 hostIcon.appendChild(hostIconImage);
68 hostEntry.appendChild(hostIcon); 105 hostTableEntry.tableRow.appendChild(hostIcon);
69 106
70 var hostName = document.createElement('td'); 107 hostTableEntry.hostNameCell = document.createElement('td');
71 hostName.setAttribute('class', 'mode-select-label'); 108 hostTableEntry.hostNameCell.setAttribute('class', 'mode-select-label');
72 hostName.appendChild(document.createTextNode(host.hostName)); 109 hostTableEntry.hostNameCell.appendChild(
73 hostEntry.appendChild(hostName); 110 document.createTextNode(host.hostName));
111 hostTableEntry.hostNameCell.setAttribute(
112 'ondblclick',
113 'remoting.hostList.beginRenameHost("' +
114 host.hostId + '"); return false;');
115 hostTableEntry.tableRow.appendChild(hostTableEntry.hostNameCell);
74 116
75 var hostStatus = document.createElement('td'); 117 var hostStatus = document.createElement('td');
76 if (host.status == 'ONLINE') { 118 if (host.status == 'ONLINE') {
77 var connectButton = document.createElement('button'); 119 var connectButton = document.createElement('button');
78 connectButton.setAttribute('class', 'mode-select-button'); 120 connectButton.setAttribute('class', 'mode-select-button');
79 connectButton.setAttribute('type', 'button'); 121 connectButton.setAttribute('type', 'button');
80 connectButton.setAttribute('onclick', 122 connectButton.setAttribute('onclick',
81 'remoting.connectHost("'+host.hostId+'")'); 123 'remoting.connectHost("' + host.hostId + '")');
82 connectButton.innerHTML = 124 connectButton.innerHTML =
83 chrome.i18n.getMessage(/*i18n-content*/'CONNECT_BUTTON'); 125 chrome.i18n.getMessage(/*i18n-content*/'CONNECT_BUTTON');
84 hostStatus.appendChild(connectButton); 126 hostStatus.appendChild(connectButton);
85 } else { 127 } else {
86 addClass(hostEntry, 'host-offline'); 128 addClass(hostTableEntry.tableRow, 'host-offline');
87 hostStatus.innerHTML = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE'); 129 hostStatus.innerHTML = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE');
88 } 130 }
89 hostStatus.className = 'host-list-row-end'; 131 hostStatus.className = 'host-list-row-end';
90 hostEntry.appendChild(hostStatus); 132 hostTableEntry.tableRow.appendChild(hostStatus);
91 133
92 this.table.appendChild(hostEntry); 134 var editButton = document.createElement('td');
135 editButton.setAttribute('onclick', 'remoting.hostList.beginRenameHost("' +
136 host.hostId + '")');
137 addClass(editButton, 'clickable');
138 addClass(editButton, 'host-list-edit');
139 var penImage = document.createElement('img');
140 penImage.src = 'icon_pencil.png';
141 addClass(penImage, 'host-list-rename-icon');
142 editButton.appendChild(penImage);
143 hostTableEntry.tableRow.appendChild(editButton);
144
145 var removeButton = document.createElement('td');
146 removeButton.setAttribute('onclick', 'remoting.hostList.removeHost("' +
147 host.hostId + '")');
148 addClass(removeButton, 'clickable');
149 addClass(removeButton, 'host-list-edit');
150 var crossImage = document.createElement('img');
151 crossImage.src = 'icon_cross.png';
152 addClass(crossImage, 'host-list-remove-icon');
153 removeButton.appendChild(crossImage);
154 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
155
156 this.hostTableEntries[i] = hostTableEntry;
157 this.table.appendChild(hostTableEntry.tableRow);
93 } 158 }
94 159
95 this.showOrHide_(this.hosts.length != 0); 160 this.showOrHide_(this.hostTableEntries.length != 0);
96 } 161 };
97 162
98 /** 163 /**
99 * Display a localized error message. 164 * Display a localized error message.
100 * @param {remoting.Error?} errorTag The error to display, or NULL to clear any 165 * @param {remoting.Error?} errorTag The error to display, or NULL to clear any
101 * previous error. 166 * previous error.
102 * @return {void} Nothing. 167 * @return {void} Nothing.
103 */ 168 */
104 remoting.HostList.prototype.showError = function(errorTag) { 169 remoting.HostList.prototype.showError = function(errorTag) {
105 this.table.innerHTML = ''; 170 this.table.innerHTML = '';
106 if (errorTag) { 171 if (errorTag) {
107 l10n.localizeElementFromTag(this.errorDiv, 172 l10n.localizeElementFromTag(this.errorDiv,
108 /** @type {string} */ (errorTag)); 173 /** @type {string} */ (errorTag));
109 this.showOrHide_(true); 174 this.showOrHide_(true);
110 } else { 175 } else {
111 this.errorDiv.innerText = ''; 176 this.errorDiv.innerText = '';
112 } 177 }
113 } 178 };
114 179
115 /** 180 /**
116 * Show or hide the host-list UI. 181 * Show or hide the host-list UI.
117 * @param {boolean} show True to show the UI, or false to hide it. 182 * @param {boolean} show True to show the UI, or false to hide it.
118 * @return {void} Nothing. 183 * @return {void} Nothing.
119 * @private 184 * @private
120 */ 185 */
121 remoting.HostList.prototype.showOrHide_ = function(show) { 186 remoting.HostList.prototype.showOrHide_ = function(show) {
122 var parent = /** @type {Element} */ (this.table.parentNode); 187 var parent = /** @type {Element} */ (this.table.parentNode);
123 parent.hidden = !show; 188 parent.hidden = !show;
124 if (show) { 189 if (show) {
125 parent.style.height = parent.scrollHeight + 'px'; 190 parent.style.height = parent.scrollHeight + 'px';
126 removeClass(parent, remoting.HostList.COLLAPSED_); 191 removeClass(parent, remoting.HostList.COLLAPSED_);
127 } else { 192 } else {
128 addClass(parent, remoting.HostList.COLLAPSED_); 193 addClass(parent, remoting.HostList.COLLAPSED_);
129 } 194 }
130 } 195 };
196
197 /**
198 * 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
199 * @param {string} hostId The id of the host to be removed.
200 * @return {void} Nothing.
201 */
202 remoting.HostList.prototype.removeHost = function(hostId) {
203 /** @type {remoting.HostTableEntry} */
204 var hostTableEntry = this.getHostForId(hostId);
205 if (!hostTableEntry) {
206 console.error('No host registered for id ' + hostId);
207 return;
208 }
209 hostTableEntry.tableRow.parentElement.removeChild(hostTableEntry.tableRow);
210 var index = this.hostTableEntries.indexOf(hostTableEntry);
211 if (index != -1) { // Since we've just found it, index must be >= 0
212 this.hostTableEntries.splice(index, 1);
213 }
214
215 /** @param {string} token */
216 var deleteHost = function(token) {
217 var headers = { 'Authorization': 'OAuth ' + token };
218 remoting.xhr.remove(
219 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId,
220 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
221 }
222 remoting.oauth2.callWithToken(deleteHost);
223
224 this.showOrHide_(this.hostTableEntries.length != 0);
225 };
226
227 /**
228 * Prepare a host for renaming by replacing its name with an edit box.
229 * @param {string} hostId The id of the host to be renamed.
230 * @return {void} Nothing.
231 */
232 remoting.HostList.prototype.beginRenameHost = function(hostId) {
233 /** @type {remoting.HostTableEntry} */
234 var hostTableEntry = this.getHostForId(hostId);
235 if (!hostTableEntry) {
236 console.error('No host registered for id ' + hostId);
237 return;
238 }
239 var editBox = /** @type {HTMLInputElement} */ document.createElement('input');
240 editBox.type = 'text';
241 editBox.value = hostTableEntry.host.hostName;
242 editBox.setAttribute('onblur',
243 'remoting.hostList.commitRenameHost("' + hostId + '");');
244 /** @type {remoting.HostList} */
245 var that = this;
246 /** @param {Event} event */
247 var onKeydown = function(event) {
248 that.onKeydown(event, hostId);
249 }
250 editBox.onkeydown = onKeydown;
251 hostTableEntry.hostNameCell.innerHTML = '';
252 hostTableEntry.hostNameCell.appendChild(editBox);
253 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.
254 };
255
256 /**
257 * Remove the edit box corresponding to the specified host, and reset its name.
258 * @param {remoting.HostTableEntry} hostTableEntry The host being renamed.
259 * @return {void} Nothing.
260 */
261 remoting.HostList.prototype.removeEditBox = function(hostTableEntry) {
262 var editBox = hostTableEntry.hostNameCell.querySelector('input');
263 if (editBox) {
264 // onblur will fire when the edit box is removed, so remove the hook.
265 editBox.onblur = null;
266 }
267 hostTableEntry.hostNameCell.innerHTML = '';
268 hostTableEntry.hostNameCell.appendChild(
269 document.createTextNode(hostTableEntry.host.hostName));
270 };
271
272 /**
273 * Accept the host name entered by the user.
274 * @param {string} hostId The id of the host to be renamed.
275 * @return {void} Nothing.
276 */
277 remoting.HostList.prototype.commitRenameHost = function(hostId) {
278 /** @type {remoting.HostTableEntry} */
279 var hostTableEntry = this.getHostForId(hostId);
280 if (hostTableEntry) {
281 var editBox = hostTableEntry.hostNameCell.querySelector('input');
282 if (editBox) {
283 if (hostTableEntry.host.hostName != editBox.value) {
284 hostTableEntry.host.hostName = editBox.value;
285 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.
286 /** @param {string} token */
287 var renameHost = function(token) {
288 var headers = {
289 'Authorization': 'OAuth ' + token,
290 '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.
291 };
292 var newHostDetails = { data: hostTableEntry.host };
293 remoting.xhr.put(
294 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId,
295 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.
296 JSON.stringify(newHostDetails),
297 headers);
298 }
299 remoting.oauth2.callWithToken(renameHost);
300 }
301 }
302 }
303 this.removeEditBox(hostTableEntry);
304 };
305
306 /**
307 * Revert the host name, ignoring changes made by the user.
308 * @param {string} hostId The id of the host to be renamed.
309 * @return {void} Nothing.
310 */
311 remoting.HostList.prototype.cancelRenameHost = function(hostId) {
312 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
313 if (host) {
314 this.removeEditBox(host);
315 }
316 };
317
318 /**
319 * Handle a key event while the user is typing a host name
320 * @param {Event} event The keyboard event.
321 * @param {string} hostId The id of the host being renamed.
322 * @return {void} Nothing.
323 */
324 remoting.HostList.prototype.onKeydown = function(event, hostId) {
325 if (event.which == 27) { // Escape
326 this.cancelRenameHost(hostId);
327 } else if (event.which == 13) { // Enter
328 this.commitRenameHost(hostId);
329 }
330 };
131 331
132 /** 332 /**
133 * Class name for the host list when it is collapsed. 333 * Class name for the host list when it is collapsed.
134 * @private 334 * @private
135 */ 335 */
136 remoting.HostList.COLLAPSED_ = 'collapsed'; 336 remoting.HostList.COLLAPSED_ = 'collapsed';
137 337
138 /** @type {remoting.HostList} */ 338 /** @type {remoting.HostList} */
139 remoting.hostList = null; 339 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.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698