| OLD | NEW |
| 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 * @constructor | 16 * Create a host list consisting of the specified HTML elements, which should |
| 17 */ | 17 * have a common parent that contains only host-list UI as it will be hidden |
| 18 remoting.Host = function() { | 18 * if the host-list is empty. |
| 19 /** @type {string} */ | |
| 20 this.hostName = ''; | |
| 21 /** @type {string} */ | |
| 22 this.hostId = ''; | |
| 23 /** @type {string} */ | |
| 24 this.status = ''; | |
| 25 /** @type {string} */ | |
| 26 this.jabberId = ''; | |
| 27 /** @type {string} */ | |
| 28 this.publicKey = ''; | |
| 29 } | |
| 30 | |
| 31 | |
| 32 /** | |
| 33 * @constructor | 19 * @constructor |
| 34 * @param {Element} table The HTML <table> to contain host-list. | 20 * @param {Element} table The HTML <table> to contain host-list. |
| 35 * @param {Element} errorDiv The HTML <div> to display error messages. | 21 * @param {Element} errorDiv The HTML <div> to display error messages. |
| 36 */ | 22 */ |
| 37 remoting.HostList = function(table, errorDiv) { | 23 remoting.HostList = function(table, errorDiv) { |
| 38 this.table = table; | 24 /** |
| 39 this.errorDiv = errorDiv; | 25 * @type {Element} |
| 40 /** @type {Array.<remoting.Host>} */ | 26 * @private |
| 41 this.hosts = null; | 27 */ |
| 28 this.table_ = table; |
| 29 /** |
| 30 * @type {Element} |
| 31 * @private |
| 32 */ |
| 33 this.errorDiv_ = errorDiv; |
| 34 /** |
| 35 * @type {Array.<remoting.HostTableEntry>} |
| 36 * @private |
| 37 */ |
| 38 this.hostTableEntries_ = null; |
| 39 }; |
| 40 |
| 41 /** |
| 42 * Search the host list for a host with the specified id. |
| 43 * @param {string} hostId The unique id of the host. |
| 44 * @return {remoting.HostTableEntry?} The host table entry, if any. |
| 45 */ |
| 46 remoting.HostList.prototype.getHostForId = function(hostId) { |
| 47 for (var i = 0; i < this.hostTableEntries_.length; ++i) { |
| 48 if (this.hostTableEntries_[i].host.hostId == hostId) { |
| 49 return this.hostTableEntries_[i]; |
| 50 } |
| 51 } |
| 52 return null; |
| 53 }; |
| 54 |
| 55 /** |
| 56 * Query the Remoting Directory for the user's list of hosts. |
| 57 * |
| 58 * @return {void} Nothing. |
| 59 */ |
| 60 remoting.HostList.prototype.refresh = function() { |
| 61 /** @type {remoting.HostList} */ |
| 62 var that = this; |
| 63 /** @param {XMLHttpRequest} xhr */ |
| 64 var parseHostListResponse = function(xhr) { |
| 65 that.parseHostListResponse_(xhr); |
| 66 } |
| 67 /** @param {string} token */ |
| 68 var getHosts = function(token) { |
| 69 var headers = { 'Authorization': 'OAuth ' + token }; |
| 70 remoting.xhr.get( |
| 71 'https://www.googleapis.com/chromoting/v1/@me/hosts', |
| 72 parseHostListResponse, '', headers); |
| 73 }; |
| 74 remoting.oauth2.callWithToken(getHosts); |
| 75 } |
| 76 |
| 77 /** |
| 78 * Handle the results of the host list request. A success response will |
| 79 * include a JSON-encoded list of host descriptions, which we display if we're |
| 80 * able to successfully parse it. |
| 81 * |
| 82 * @param {XMLHttpRequest} xhr The XHR object for the host list request. |
| 83 * @return {void} Nothing. |
| 84 */ |
| 85 remoting.HostList.prototype.parseHostListResponse_ = function(xhr) { |
| 86 try { |
| 87 if (xhr.status == 200) { |
| 88 var parsed_response = |
| 89 /** @type {{data: {items: Array}}} */ JSON.parse(xhr.responseText); |
| 90 if (parsed_response.data && parsed_response.data.items) { |
| 91 this.setHosts_(parsed_response.data.items); |
| 92 } |
| 93 } else { |
| 94 // Some other error. |
| 95 console.error('Bad status on host list query: ', xhr); |
| 96 // For most errors in the 4xx range, tell the user to re-authorize us. |
| 97 if (xhr.status == 403) { |
| 98 // The user's account is not enabled for Me2Me, so fail silently. |
| 99 } else if (xhr.status >= 400 && xhr.status <= 499) { |
| 100 this.showError_(remoting.Error.GENERIC); |
| 101 } |
| 102 } |
| 103 } catch (er) { |
| 104 console.error('Error processing response: ', xhr, er); |
| 105 } |
| 42 } | 106 } |
| 43 | 107 |
| 44 /** | 108 /** |
| 45 * Refresh the host list with up-to-date details. | 109 * Refresh the host list with up-to-date details. |
| 46 * @param {Array.<remoting.Host>} hosts The new host list. | 110 * @param {Array.<remoting.Host>} hosts The new host list. |
| 47 * @return {void} Nothing. | 111 * @return {void} Nothing. |
| 112 * @private |
| 48 */ | 113 */ |
| 49 remoting.HostList.prototype.update = function(hosts) { | 114 remoting.HostList.prototype.setHosts_ = function(hosts) { |
| 50 this.table.innerHTML = ''; | 115 this.table_.innerHTML = ''; |
| 51 this.showError(null); | 116 this.showError_(null); |
| 52 this.hosts = hosts; | 117 this.hostTableEntries_ = []; |
| 53 | 118 |
| 119 /** @type {remoting.HostList} */ |
| 120 var that = this; |
| 54 for (var i = 0; i < hosts.length; ++i) { | 121 for (var i = 0; i < hosts.length; ++i) { |
| 122 /** @type {remoting.Host} */ |
| 55 var host = hosts[i]; | 123 var host = hosts[i]; |
| 56 if (!host.hostName || !host.hostId || !host.status || !host.jabberId || | 124 // Validate the entry to make sure it has all the fields we expect. |
| 57 !host.publicKey) | 125 if (host.hostName && host.hostId && host.status && host.jabberId && |
| 58 continue; | 126 host.publicKey) { |
| 59 | 127 var onRename = function() { that.renameHost_(host.hostId); } |
| 60 var hostEntry = document.createElement('tr'); | 128 var onDelete = function() { that.deleteHost_(host.hostId); } |
| 61 addClass(hostEntry, 'host-list-row'); | 129 var hostTableEntry = new remoting.HostTableEntry(); |
| 62 | 130 hostTableEntry.init(host, onRename, onDelete); |
| 63 var hostIcon = document.createElement('td'); | 131 this.hostTableEntries_[i] = hostTableEntry; |
| 64 var hostIconImage = document.createElement('img'); | 132 this.table_.appendChild(hostTableEntry.tableRow); |
| 65 hostIconImage.src = 'icon_host.png'; | |
| 66 hostIcon.className = 'host-list-row-start'; | |
| 67 hostIcon.appendChild(hostIconImage); | |
| 68 hostEntry.appendChild(hostIcon); | |
| 69 | |
| 70 var hostName = document.createElement('td'); | |
| 71 hostName.setAttribute('class', 'mode-select-label'); | |
| 72 hostName.appendChild(document.createTextNode(host.hostName)); | |
| 73 hostEntry.appendChild(hostName); | |
| 74 | |
| 75 var hostStatus = document.createElement('td'); | |
| 76 if (host.status == 'ONLINE') { | |
| 77 var connectButton = document.createElement('button'); | |
| 78 connectButton.setAttribute('class', 'mode-select-button'); | |
| 79 connectButton.setAttribute('type', 'button'); | |
| 80 connectButton.setAttribute('onclick', | |
| 81 'remoting.connectHost("'+host.hostId+'")'); | |
| 82 connectButton.innerHTML = | |
| 83 chrome.i18n.getMessage(/*i18n-content*/'CONNECT_BUTTON'); | |
| 84 hostStatus.appendChild(connectButton); | |
| 85 } else { | |
| 86 addClass(hostEntry, 'host-offline'); | |
| 87 hostStatus.innerHTML = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE'); | |
| 88 } | 133 } |
| 89 hostStatus.className = 'host-list-row-end'; | |
| 90 hostEntry.appendChild(hostStatus); | |
| 91 | |
| 92 this.table.appendChild(hostEntry); | |
| 93 } | 134 } |
| 94 | 135 |
| 95 this.showOrHide_(this.hosts.length != 0); | 136 this.showOrHide_(this.hostTableEntries_.length != 0); |
| 96 } | 137 }; |
| 97 | 138 |
| 98 /** | 139 /** |
| 99 * Display a localized error message. | 140 * Display a localized error message. |
| 100 * @param {remoting.Error?} errorTag The error to display, or NULL to clear any | 141 * @param {remoting.Error?} errorTag The error to display, or NULL to clear any |
| 101 * previous error. | 142 * previous error. |
| 102 * @return {void} Nothing. | 143 * @return {void} Nothing. |
| 103 */ | 144 */ |
| 104 remoting.HostList.prototype.showError = function(errorTag) { | 145 remoting.HostList.prototype.showError_ = function(errorTag) { |
| 105 this.table.innerHTML = ''; | 146 this.table_.innerHTML = ''; |
| 106 if (errorTag) { | 147 if (errorTag) { |
| 107 l10n.localizeElementFromTag(this.errorDiv, | 148 l10n.localizeElementFromTag(this.errorDiv_, |
| 108 /** @type {string} */ (errorTag)); | 149 /** @type {string} */ (errorTag)); |
| 109 this.showOrHide_(true); | 150 this.showOrHide_(true); |
| 110 } else { | 151 } else { |
| 111 this.errorDiv.innerText = ''; | 152 this.errorDiv_.innerText = ''; |
| 112 } | 153 } |
| 113 } | 154 }; |
| 114 | 155 |
| 115 /** | 156 /** |
| 116 * Show or hide the host-list UI. | 157 * Show or hide the host-list UI. |
| 117 * @param {boolean} show True to show the UI, or false to hide it. | 158 * @param {boolean} show True to show the UI, or false to hide it. |
| 118 * @return {void} Nothing. | 159 * @return {void} Nothing. |
| 119 * @private | 160 * @private |
| 120 */ | 161 */ |
| 121 remoting.HostList.prototype.showOrHide_ = function(show) { | 162 remoting.HostList.prototype.showOrHide_ = function(show) { |
| 122 var parent = /** @type {Element} */ (this.table.parentNode); | 163 var parent = /** @type {Element} */ (this.table_.parentNode); |
| 123 parent.hidden = !show; | 164 parent.hidden = !show; |
| 124 if (show) { | 165 if (show) { |
| 125 parent.style.height = parent.scrollHeight + 'px'; | 166 parent.style.height = parent.scrollHeight + 'px'; |
| 126 removeClass(parent, remoting.HostList.COLLAPSED_); | 167 removeClass(parent, remoting.HostList.COLLAPSED_); |
| 127 } else { | 168 } else { |
| 128 addClass(parent, remoting.HostList.COLLAPSED_); | 169 addClass(parent, remoting.HostList.COLLAPSED_); |
| 129 } | 170 } |
| 130 } | 171 }; |
| 172 |
| 173 /** |
| 174 * Remove a host from the list, and deregister it. |
| 175 * @param {string} hostId The id of the host to be removed. |
| 176 * @return {void} Nothing. |
| 177 * @private |
| 178 */ |
| 179 remoting.HostList.prototype.deleteHost_ = function(hostId) { |
| 180 /** @type {remoting.HostTableEntry} */ |
| 181 var hostTableEntry = this.getHostForId(hostId); |
| 182 if (!hostTableEntry) { |
| 183 console.error('No host registered for id ' + hostId); |
| 184 return; |
| 185 } |
| 186 |
| 187 this.table_.removeChild(hostTableEntry.tableRow); |
| 188 var index = this.hostTableEntries_.indexOf(hostTableEntry); |
| 189 if (index != -1) { // Since we've just found it, index must be >= 0 |
| 190 this.hostTableEntries_.splice(index, 1); |
| 191 } |
| 192 |
| 193 /** @param {string} token */ |
| 194 var deleteHost = function(token) { |
| 195 var headers = { 'Authorization': 'OAuth ' + token }; |
| 196 remoting.xhr.remove( |
| 197 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId, |
| 198 function() {}, '', headers); |
| 199 } |
| 200 remoting.oauth2.callWithToken(deleteHost); |
| 201 |
| 202 this.showOrHide_(this.hostTableEntries_.length != 0); |
| 203 }; |
| 204 |
| 205 /** |
| 206 * Prepare a host for renaming by replacing its name with an edit box. |
| 207 * @param {string} hostId The id of the host to be renamed. |
| 208 * @return {void} Nothing. |
| 209 * @private |
| 210 */ |
| 211 remoting.HostList.prototype.renameHost_ = function(hostId) { |
| 212 /** @type {remoting.HostTableEntry} */ |
| 213 var hostTableEntry = this.getHostForId(hostId); |
| 214 if (!hostTableEntry) { |
| 215 console.error('No host registered for id ' + hostId); |
| 216 return; |
| 217 } |
| 218 /** @param {string} token */ |
| 219 var renameHost = function(token) { |
| 220 var headers = { |
| 221 'Authorization': 'OAuth ' + token, |
| 222 'Content-type' : 'application/json; charset=UTF-8' |
| 223 }; |
| 224 var newHostDetails = { data: hostTableEntry.host }; |
| 225 remoting.xhr.put( |
| 226 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + hostId, |
| 227 function(xhr) {}, |
| 228 JSON.stringify(newHostDetails), |
| 229 headers); |
| 230 } |
| 231 remoting.oauth2.callWithToken(renameHost); |
| 232 }; |
| 131 | 233 |
| 132 /** | 234 /** |
| 133 * Class name for the host list when it is collapsed. | 235 * Class name for the host list when it is collapsed. |
| 134 * @private | 236 * @private |
| 135 */ | 237 */ |
| 136 remoting.HostList.COLLAPSED_ = 'collapsed'; | 238 remoting.HostList.COLLAPSED_ = 'collapsed'; |
| 137 | 239 |
| 138 /** @type {remoting.HostList} */ | 240 /** @type {remoting.HostList} */ |
| 139 remoting.hostList = null; | 241 remoting.hostList = null; |
| OLD | NEW |