| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview | |
| 7 * Class representing the host-list portion of the home screen UI. | |
| 8 */ | |
| 9 | |
| 10 'use strict'; | |
| 11 | |
| 12 /** @suppress {duplicate} */ | |
| 13 var remoting = remoting || {}; | |
| 14 | |
| 15 /** | |
| 16 * Create a host list consisting of the specified HTML elements, which should | |
| 17 * have a common parent that contains only host-list UI as it will be hidden | |
| 18 * if the host-list is empty. | |
| 19 * | |
| 20 * @constructor | |
| 21 * @param {Element} table The HTML <table> to contain host-list. | |
| 22 * @param {Element} errorDiv The HTML <div> to display error messages. | |
| 23 */ | |
| 24 remoting.HostList = function(table, errorDiv) { | |
| 25 /** | |
| 26 * @type {Element} | |
| 27 * @private | |
| 28 */ | |
| 29 this.table_ = table; | |
| 30 /** | |
| 31 * @type {Element} | |
| 32 * @private | |
| 33 */ | |
| 34 this.errorDiv_ = errorDiv; | |
| 35 /** | |
| 36 * @type {Array.<remoting.HostTableEntry>} | |
| 37 * @private | |
| 38 */ | |
| 39 this.hostTableEntries_ = []; | |
| 40 /** | |
| 41 * @type {Array.<remoting.Host>} | |
| 42 * @private | |
| 43 */ | |
| 44 this.hosts_ = []; | |
| 45 /** | |
| 46 * @type {string} | |
| 47 * @private | |
| 48 */ | |
| 49 this.lastError_ = ''; | |
| 50 | |
| 51 // Load the cache of the last host-list, if present. | |
| 52 var cached = /** @type {string} */ | |
| 53 (window.localStorage.getItem(remoting.HostList.HOSTS_KEY)); | |
| 54 if (cached) { | |
| 55 try { | |
| 56 this.hosts_ = /** @type {Array} */ JSON.parse(cached); | |
| 57 } catch (err) { | |
| 58 console.error('Invalid host list cache:', /** @type {*} */(err)); | |
| 59 } | |
| 60 } | |
| 61 }; | |
| 62 | |
| 63 /** | |
| 64 * Search the host list for a host with the specified id. | |
| 65 * | |
| 66 * @param {string} hostId The unique id of the host. | |
| 67 * @return {remoting.Host?} The host, if any. | |
| 68 */ | |
| 69 remoting.HostList.prototype.getHostForId = function(hostId) { | |
| 70 for (var i = 0; i < this.hosts_.length; ++i) { | |
| 71 if (this.hosts_[i].hostId == hostId) { | |
| 72 return this.hosts_[i]; | |
| 73 } | |
| 74 } | |
| 75 return null; | |
| 76 }; | |
| 77 | |
| 78 /** | |
| 79 * Query the Remoting Directory for the user's list of hosts. | |
| 80 * | |
| 81 * @param {function(boolean):void} onDone Callback invoked with true on success | |
| 82 * or false on failure. | |
| 83 * @return {void} Nothing. | |
| 84 */ | |
| 85 remoting.HostList.prototype.refresh = function(onDone) { | |
| 86 /** @type {remoting.HostList} */ | |
| 87 var that = this; | |
| 88 /** @param {XMLHttpRequest} xhr The response from the server. */ | |
| 89 var parseHostListResponse = function(xhr) { | |
| 90 that.parseHostListResponse_(xhr, onDone); | |
| 91 } | |
| 92 /** @param {string} token The OAuth2 token. */ | |
| 93 var getHosts = function(token) { | |
| 94 var headers = { 'Authorization': 'OAuth ' + token }; | |
| 95 remoting.xhr.get( | |
| 96 'https://www.googleapis.com/chromoting/v1/@me/hosts', | |
| 97 parseHostListResponse, '', headers); | |
| 98 }; | |
| 99 remoting.oauth2.callWithToken(getHosts); | |
| 100 }; | |
| 101 | |
| 102 /** | |
| 103 * Handle the results of the host list request. A success response will | |
| 104 * include a JSON-encoded list of host descriptions, which we display if we're | |
| 105 * able to successfully parse it. | |
| 106 * | |
| 107 * @param {XMLHttpRequest} xhr The XHR object for the host list request. | |
| 108 * @param {function(boolean):void} onDone The callback passed to |refresh|. | |
| 109 * @return {void} Nothing. | |
| 110 * @private | |
| 111 */ | |
| 112 remoting.HostList.prototype.parseHostListResponse_ = function(xhr, onDone) { | |
| 113 this.hosts_ = []; | |
| 114 this.lastError_ = ''; | |
| 115 try { | |
| 116 if (xhr.status == 200) { | |
| 117 var parsed_response = | |
| 118 /** @type {{data: {items: Array}}} */ JSON.parse(xhr.responseText); | |
| 119 if (parsed_response.data && parsed_response.data.items) { | |
| 120 this.hosts_ = parsed_response.data.items; | |
| 121 } | |
| 122 } else { | |
| 123 // Some other error. | |
| 124 console.error('Bad status on host list query: ', xhr); | |
| 125 if (xhr.status == 403) { | |
| 126 // The user's account is not enabled for Me2Me, so fail silently. | |
| 127 } else if (xhr.status >= 400 && xhr.status < 500) { | |
| 128 // For other errors, tell the user to re-authorize us. | |
| 129 this.lastError_ = remoting.Error.GENERIC; | |
| 130 } else if (xhr.status == 503) { | |
| 131 this.lastError_ = remoting.Error.SERVICE_UNAVAILABLE; | |
| 132 } else { | |
| 133 this.lastError_ = remoting.Error.UNEXPECTED; | |
| 134 } | |
| 135 } | |
| 136 } catch (er) { | |
| 137 var typed_er = /** @type {Object} */ (er); | |
| 138 console.error('Error processing response: ', xhr, typed_er); | |
| 139 this.lastError_ = remoting.Error.UNEXPECTED; | |
| 140 } | |
| 141 window.localStorage.setItem(remoting.HostList.HOSTS_KEY, | |
| 142 JSON.stringify(this.hosts_)); | |
| 143 onDone(this.lastError_ == ''); | |
| 144 }; | |
| 145 | |
| 146 /** | |
| 147 * Display the list of hosts or error condition. | |
| 148 * | |
| 149 * @return {void} Nothing. | |
| 150 */ | |
| 151 remoting.HostList.prototype.display = function() { | |
| 152 this.table_.innerHTML = ''; | |
| 153 this.errorDiv_.innerText = ''; | |
| 154 this.hostTableEntries_ = []; | |
| 155 | |
| 156 /** | |
| 157 * @type {remoting.HostList} | |
| 158 */ | |
| 159 var that = this; | |
| 160 /** | |
| 161 * @param {remoting.HostTableEntry} hostTableEntry The entry being renamed. | |
| 162 */ | |
| 163 var onRename = function(hostTableEntry) { that.renameHost_(hostTableEntry); } | |
| 164 /** | |
| 165 * @param {remoting.HostTableEntry} hostTableEntry The entry beign deleted. | |
| 166 */ | |
| 167 var onDelete = function(hostTableEntry) { that.deleteHost_(hostTableEntry); } | |
| 168 | |
| 169 for (var i = 0; i < this.hosts_.length; ++i) { | |
| 170 /** @type {remoting.Host} */ | |
| 171 var host = this.hosts_[i]; | |
| 172 // Validate the entry to make sure it has all the fields we expect. | |
| 173 if (host.hostName && host.hostId && host.status && host.jabberId && | |
| 174 host.publicKey) { | |
| 175 var hostTableEntry = new remoting.HostTableEntry(); | |
| 176 hostTableEntry.init(host, onRename, onDelete); | |
| 177 this.hostTableEntries_[i] = hostTableEntry; | |
| 178 this.table_.appendChild(hostTableEntry.tableRow); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 if (this.lastError_ != '') { | |
| 183 l10n.localizeElementFromTag(this.errorDiv_, this.lastError_); | |
| 184 } | |
| 185 | |
| 186 this.showOrHide_(this.hosts_.length != 0 || this.lastError_ != ''); | |
| 187 }; | |
| 188 | |
| 189 /** | |
| 190 * Show or hide the host-list UI. | |
| 191 * | |
| 192 * @param {boolean} show True to show the UI, or false to hide it. | |
| 193 * @return {void} Nothing. | |
| 194 * @private | |
| 195 */ | |
| 196 remoting.HostList.prototype.showOrHide_ = function(show) { | |
| 197 var parent = /** @type {Element} */ (this.table_.parentNode); | |
| 198 parent.hidden = !show; | |
| 199 if (show) { | |
| 200 parent.style.height = parent.scrollHeight + 'px'; | |
| 201 removeClass(parent, remoting.HostList.COLLAPSED_); | |
| 202 } else { | |
| 203 addClass(parent, remoting.HostList.COLLAPSED_); | |
| 204 } | |
| 205 }; | |
| 206 | |
| 207 /** | |
| 208 * Remove a host from the list, and deregister it. | |
| 209 * @param {remoting.HostTableEntry} hostTableEntry The host to be removed. | |
| 210 * @return {void} Nothing. | |
| 211 * @private | |
| 212 */ | |
| 213 remoting.HostList.prototype.deleteHost_ = function(hostTableEntry) { | |
| 214 this.table_.removeChild(hostTableEntry.tableRow); | |
| 215 var index = this.hostTableEntries_.indexOf(hostTableEntry); | |
| 216 if (index != -1) { | |
| 217 this.hostTableEntries_.splice(index, 1); | |
| 218 } | |
| 219 | |
| 220 /** @param {string} token The OAuth2 token. */ | |
| 221 var deleteHost = function(token) { | |
| 222 var headers = { 'Authorization': 'OAuth ' + token }; | |
| 223 remoting.xhr.remove( | |
| 224 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + | |
| 225 hostTableEntry.host.hostId, | |
| 226 function() {}, '', headers); | |
| 227 } | |
| 228 remoting.oauth2.callWithToken(deleteHost); | |
| 229 | |
| 230 this.showOrHide_(this.hostTableEntries_.length != 0); | |
| 231 }; | |
| 232 | |
| 233 /** | |
| 234 * Prepare a host for renaming by replacing its name with an edit box. | |
| 235 * @param {remoting.HostTableEntry} hostTableEntry The host to be renamed. | |
| 236 * @return {void} Nothing. | |
| 237 * @private | |
| 238 */ | |
| 239 remoting.HostList.prototype.renameHost_ = function(hostTableEntry) { | |
| 240 /** @param {string} token */ | |
| 241 var renameHost = function(token) { | |
| 242 var headers = { | |
| 243 'Authorization': 'OAuth ' + token, | |
| 244 'Content-type' : 'application/json; charset=UTF-8' | |
| 245 }; | |
| 246 var newHostDetails = { data: { | |
| 247 hostId: hostTableEntry.host.hostId, | |
| 248 hostName: hostTableEntry.host.hostName, | |
| 249 publicKey: hostTableEntry.host.publicKey | |
| 250 } }; | |
| 251 remoting.xhr.put( | |
| 252 'https://www.googleapis.com/chromoting/v1/@me/hosts/' + | |
| 253 hostTableEntry.host.hostId, | |
| 254 function(xhr) {}, | |
| 255 JSON.stringify(newHostDetails), | |
| 256 headers); | |
| 257 } | |
| 258 remoting.oauth2.callWithToken(renameHost); | |
| 259 }; | |
| 260 | |
| 261 /** | |
| 262 * Class name for the host list when it is collapsed. | |
| 263 * @private | |
| 264 */ | |
| 265 remoting.HostList.COLLAPSED_ = 'collapsed'; | |
| 266 | |
| 267 /** | |
| 268 * Key name under which Me2Me hosts are cached. | |
| 269 */ | |
| 270 remoting.HostList.HOSTS_KEY = 'me2me-cached-hosts'; | |
| 271 | |
| 272 /** @type {remoting.HostList} */ | |
| 273 remoting.hostList = null; | |
| OLD | NEW |