Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 an entry in the host-list portion of the home screen. | 7 * Class representing an entry in the host-list portion of the home screen. |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 'use strict'; | |
| 11 | |
| 12 /** @suppress {duplicate} */ | 10 /** @suppress {duplicate} */ |
| 13 var remoting = remoting || {}; | 11 var remoting = remoting || {}; |
| 14 | 12 |
| 13 (function() { | |
| 14 | |
| 15 'use strict'; | |
| 16 | |
| 15 /** | 17 /** |
| 16 * An entry in the host table. | 18 * An entry in the host table. |
| 17 * @param {remoting.Host} host The host, as obtained from Apiary. | |
| 18 * @param {number} webappMajorVersion The major version nmber of the web-app, | 19 * @param {number} webappMajorVersion The major version nmber of the web-app, |
| 19 * used to identify out-of-date hosts. | 20 * used to identify out-of-date hosts. |
| 20 * @param {function(remoting.HostTableEntry):void} onRename Callback for | 21 * @param {function(remoting.HostTableEntry):void} onRename Callback for |
| 21 * rename operations. | 22 * rename operations. |
| 22 * @param {function(remoting.HostTableEntry):void=} opt_onDelete Callback for | 23 * @param {function(remoting.HostTableEntry):void=} opt_onDelete Callback for |
| 23 * delete operations. | 24 * delete operations. |
| 24 * @constructor | 25 * @constructor |
| 25 */ | 26 */ |
| 26 remoting.HostTableEntry = function( | 27 remoting.HostTableEntry = function(webappMajorVersion, onRename, opt_onDelete) { |
| 27 host, webappMajorVersion, onRename, opt_onDelete) { | |
| 28 /** @type {remoting.Host} */ | 28 /** @type {remoting.Host} */ |
| 29 this.host = host; | 29 this.host = null; |
| 30 /** @type {number} */ | 30 /** @type {number} */ |
| 31 this.webappMajorVersion_ = webappMajorVersion; | 31 this.webappMajorVersion_ = webappMajorVersion; |
| 32 /** @type {function(remoting.HostTableEntry):void} @private */ | 32 /** @type {function(remoting.HostTableEntry):void} @private */ |
| 33 this.onRename_ = onRename; | 33 this.onRename_ = onRename; |
| 34 /** @type {undefined|function(remoting.HostTableEntry):void} @private */ | 34 /** @type {undefined|function(remoting.HostTableEntry):void} @private */ |
| 35 this.onDelete_ = opt_onDelete; | 35 this.onDelete_ = opt_onDelete; |
| 36 | 36 |
| 37 /** @type {HTMLElement} */ | 37 /** @type {HTMLElement} */ |
| 38 this.tableRow = null; | 38 this.rootElement_ = null; |
| 39 /** @type {HTMLElement} @private */ | 39 /** @private {HTMLElement} */ |
| 40 this.hostNameCell_ = null; | 40 this.hostNameLabel_ = null; |
| 41 /** @type {HTMLElement} @private */ | 41 /** @private {HTMLInputElement} */ |
| 42 this.renameInputField_ = null; | |
| 43 /** @private {HTMLElement} */ | |
| 42 this.warningOverlay_ = null; | 44 this.warningOverlay_ = null; |
| 45 /** @private {base.Disposables} */ | |
| 46 this.renameInputEventHooks_ = null; | |
| 47 | |
| 43 // References to event handlers so that they can be removed. | 48 // References to event handlers so that they can be removed. |
| 44 /** @type {function():void} @private */ | 49 /** @type {function():void} @private */ |
| 45 this.onBlurReference_ = function() {}; | |
| 46 /** @type {function():void} @private */ | |
| 47 this.onConfirmDeleteReference_ = function() {}; | 50 this.onConfirmDeleteReference_ = function() {}; |
| 48 /** @type {function():void} @private */ | 51 /** @type {function():void} @private */ |
| 49 this.onCancelDeleteReference_ = function() {}; | 52 this.onCancelDeleteReference_ = function() {}; |
| 50 /** @type {function():void?} @private */ | 53 |
| 51 this.onConnectReference_ = null; | 54 this.createDom_(); |
| 55 }; | |
| 56 | |
| 57 /** @return {string} */ | |
| 58 remoting.HostTableEntry.prototype.getHTML_ = function() { | |
| 59 var editLabel = chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_RENAME'); | |
| 60 var html = | |
| 61 '<div class="host-list-main-icon">' + | |
| 62 '<span class="warning-overlay"></span>' + | |
| 63 '<img src="icon_host.webp">' + | |
| 64 '</div>' + | |
| 65 '<div class="box-spacer"">' + | |
| 66 '<a class="host-list-label" href="#""></a>' + | |
| 67 '<input type="text" hidden/>' + | |
| 68 '</div>' + | |
| 69 '<span tabindex="0" class="clickable host-list-edit rename-button">' + | |
| 70 '<img title="' + editLabel + '"' + | |
| 71 'src="icon_pencil.webp" class="host-list-rename-icon">' + | |
| 72 '</span>'; | |
| 73 if (this.onDelete_) { | |
| 74 var deleteLabel = chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_DELETE'); | |
| 75 html += | |
| 76 '<span tabindex="0" class="clickable host-list-edit delete-button">' + | |
| 77 '<img title="' + deleteLabel + '"' + | |
| 78 'src="icon_cross.webp" class="host-list-remove-icon">' + | |
| 79 '</span>'; | |
| 80 } | |
| 81 return html; | |
| 52 }; | 82 }; |
| 53 | 83 |
| 54 /** | 84 /** |
| 55 * Create the HTML elements for this entry and set up event handlers. | 85 * Create the HTML elements for this entry and set up event handlers. |
| 56 * @return {void} Nothing. | 86 * @return {void} Nothing. |
| 57 */ | 87 */ |
| 58 remoting.HostTableEntry.prototype.createDom = function() { | 88 remoting.HostTableEntry.prototype.createDom_ = function() { |
| 59 // Create the top-level <div> | 89 var editLabel = chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_RENAME'); |
| 60 var tableRow = /** @type {HTMLElement} */ (document.createElement('div')); | 90 var container = /** @type {HTMLElement} */ (document.createElement('div')); |
| 61 tableRow.classList.add('section-row'); | 91 container.innerHTML = '<div class="section-row host-offline" title="">' + |
| 62 // Create the host icon cell. | 92 this.getHTML_() + '</div>'; |
|
Jamie
2015/02/23 22:28:11
I would prefer that we not use innerHTML with anyt
kelvinp
2015/02/24 22:04:44
Done.
| |
| 63 var hostIconDiv = /** @type {HTMLElement} */ (document.createElement('div')); | |
| 64 hostIconDiv.classList.add('host-list-main-icon'); | |
| 65 var warningOverlay = | |
| 66 /** @type {HTMLElement} */ (document.createElement('span')); | |
| 67 hostIconDiv.appendChild(warningOverlay); | |
| 68 var hostIcon = /** @type {HTMLElement} */ (document.createElement('img')); | |
| 69 hostIcon.src = 'icon_host.webp'; | |
| 70 hostIconDiv.appendChild(hostIcon); | |
| 71 tableRow.appendChild(hostIconDiv); | |
| 72 // Create the host name cell. | |
| 73 var hostNameCell = /** @type {HTMLElement} */ (document.createElement('div')); | |
| 74 hostNameCell.classList.add('box-spacer'); | |
| 75 hostNameCell.id = 'host_' + this.host.hostId; | |
| 76 tableRow.appendChild(hostNameCell); | |
| 77 // Create the host rename cell. | |
| 78 var editButton = /** @type {HTMLElement} */ (document.createElement('span')); | |
| 79 var editButtonImg = | |
| 80 /** @type {HTMLElement} */ (document.createElement('img')); | |
| 81 editButtonImg.title = chrome.i18n.getMessage( | |
| 82 /*i18n-content*/'TOOLTIP_RENAME'); | |
| 83 editButtonImg.src = 'icon_pencil.webp'; | |
| 84 editButton.tabIndex = 0; | |
| 85 editButton.classList.add('clickable'); | |
| 86 editButton.classList.add('host-list-edit'); | |
| 87 editButtonImg.classList.add('host-list-rename-icon'); | |
| 88 editButton.appendChild(editButtonImg); | |
| 89 tableRow.appendChild(editButton); | |
| 90 // Create the host delete cell. | |
| 91 var deleteButton = | |
| 92 /** @type {HTMLElement} */ (document.createElement('span')); | |
| 93 var deleteButtonImg = | |
| 94 /** @type {HTMLElement} */ (document.createElement('img')); | |
| 95 deleteButtonImg.title = | |
| 96 chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_DELETE'); | |
| 97 deleteButtonImg.src = 'icon_cross.webp'; | |
| 98 deleteButton.tabIndex = 0; | |
| 99 deleteButton.classList.add('clickable'); | |
| 100 deleteButton.classList.add('host-list-edit'); | |
| 101 deleteButtonImg.classList.add('host-list-remove-icon'); | |
| 102 deleteButton.appendChild(deleteButtonImg); | |
| 103 tableRow.appendChild(deleteButton); | |
| 104 | 93 |
| 105 this.init(tableRow, warningOverlay, hostNameCell, editButton, deleteButton); | 94 // Setup DOM references. |
| 95 this.rootElement_ = /** @type {HTMLElement} */ (container.firstElementChild); | |
| 96 this.warningOverlay_ = container.querySelector('.warning-overlay'); | |
| 97 this.hostNameLabel_ = container.querySelector('.box-spacer a'); | |
| 98 this.renameInputField_ = /** @type {HTMLInputElement} */ ( | |
| 99 container.querySelector('.box-spacer input')); | |
| 100 | |
| 101 // Register event handlers. | |
| 102 var editButton = container.querySelector('.rename-button'); | |
| 103 var deleteButton = container.querySelector('.delete-button'); | |
| 104 this.registerButton_(editButton, this.beginRename_.bind(this)); | |
|
Jamie
2015/02/23 22:28:11
Don't you need to assign the result somewhere so t
kelvinp
2015/02/24 22:04:44
Currently, the host list is not designed in a way
| |
| 105 this.registerButton_(this.rootElement_, this.onConnectButton_.bind(this)); | |
| 106 if (deleteButton) { | |
| 107 this.registerButton_(deleteButton, this.showDeleteConfirmation_.bind(this)); | |
| 108 } | |
| 109 }; | |
| 110 | |
| 111 /** @return {HTMLElement} */ | |
| 112 remoting.HostTableEntry.prototype.element = function() { | |
| 113 return this.rootElement_; | |
| 114 }; | |
| 115 | |
| 116 /** @return {base.Disposable} */ | |
| 117 remoting.HostTableEntry.prototype.registerButton_ = function( | |
| 118 /** HTMLElement */ button, | |
|
Jamie
2015/02/23 22:28:11
Indentation.
kelvinp
2015/02/24 22:04:44
Done.
| |
| 119 /** function(...?):? */ callback) { | |
|
Jamie
2015/02/23 22:28:11
Just use Function as the type?
kelvinp
2015/02/24 22:04:44
Done.
| |
| 120 var ENTER = 13; | |
| 121 var SPACEBAR = 32; | |
| 122 var onKeyDown = function(/** Event */ e) { | |
| 123 if (e.which === ENTER || e.which === SPACEBAR) { | |
| 124 callback(); | |
| 125 e.stopPropagation(); | |
| 126 } | |
| 127 }; | |
| 128 var onClick = function(/** Event */ e) { | |
| 129 callback(); | |
| 130 e.stopPropagation(); | |
| 131 }; | |
| 132 var onFocusChanged = this.onFocusChange_.bind(this); | |
| 133 return new base.Disposables( | |
| 134 new base.DomEventHook(button, 'click', onClick, false), | |
| 135 new base.DomEventHook(button, 'keydown', onKeyDown, false), | |
| 136 // Register focus and blur handlers to cause the parent node to be | |
| 137 // highlighted whenever a child link has keyboard focus. Note that this is | |
| 138 // only necessary because Chrome does not yet support the draft CSS | |
| 139 // Selectors 4 specification (http://www.w3.org/TR/selectors4/#subject), | |
| 140 // which provides a more elegant solution to this problem. | |
| 141 new base.DomEventHook(button, 'focus', onFocusChanged, false), | |
| 142 new base.DomEventHook(button, 'blur', onFocusChanged, false)); | |
| 143 }; | |
| 144 | |
| 145 /** @param {remoting.Host} host */ | |
| 146 remoting.HostTableEntry.prototype.setHost = function(host) { | |
| 147 this.host = host; | |
| 148 this.updateUI_(); | |
| 149 }; | |
| 150 | |
| 151 /** @return {void} */ | |
| 152 remoting.HostTableEntry.prototype.onConnectButton_ = function() { | |
| 153 if (!this.isRenaming_() && this.host.status === 'ONLINE') { | |
| 154 var encodedHostId = encodeURIComponent(this.host.hostId); | |
| 155 remoting.connectMe2Me(encodedHostId); | |
| 156 } | |
| 157 }; | |
| 158 | |
| 159 /** @return {string} */ | |
| 160 remoting.HostTableEntry.prototype.getHostDisplayName_ = function() { | |
| 161 if (this.host.status == 'ONLINE') { | |
| 162 if (remoting.Host.needsUpdate(this.host, this.webappMajorVersion_)) { | |
| 163 return chrome.i18n.getMessage( | |
| 164 /*i18n-content*/'UPDATE_REQUIRED', this.host.hostName); | |
| 165 } | |
| 166 return this.host.hostName; | |
| 167 } else { | |
| 168 if (this.host.updatedTime) { | |
| 169 var formattedTime = formatUpdateTime(this.host.updatedTime); | |
| 170 return chrome.i18n.getMessage(/*i18n-content*/ 'LAST_ONLINE', | |
| 171 [this.host.hostName, formattedTime]); | |
| 172 } | |
| 173 return chrome.i18n.getMessage(/*i18n-content*/ 'OFFLINE', | |
| 174 this.host.hostName); | |
| 175 } | |
| 106 }; | 176 }; |
| 107 | 177 |
| 108 /** | 178 /** |
| 109 * Associate the table row with the specified elements and callbacks, and set | 179 * Update the UI to reflect the current status of the host |
| 110 * up event handlers. | |
| 111 * | 180 * |
| 112 * @param {HTMLElement} tableRow The top-level <div> for the table entry. | |
| 113 * @param {HTMLElement} warningOverlay The <span> element to render a warning | |
| 114 * icon on top of the host icon. | |
| 115 * @param {HTMLElement} hostNameCell The element containing the host name. | |
| 116 * @param {HTMLElement} editButton The <img> containing the pencil icon for | |
| 117 * editing the host name. | |
| 118 * @param {HTMLElement=} opt_deleteButton The <img> containing the cross icon | |
| 119 * for deleting the host, if present. | |
| 120 * @return {void} Nothing. | 181 * @return {void} Nothing. |
| 121 */ | 182 */ |
| 122 remoting.HostTableEntry.prototype.init = function( | 183 remoting.HostTableEntry.prototype.updateUI_ = function() { |
| 123 tableRow, warningOverlay, hostNameCell, editButton, opt_deleteButton) { | 184 var clickToConnect = this.host.status == 'ONLINE' && !this.isRenaming_(); |
| 124 this.tableRow = tableRow; | 185 var showOffline = this.host.status != 'ONLINE'; |
| 125 this.warningOverlay_ = warningOverlay; | 186 var connectLabel = chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_CONNECT', |
| 126 this.hostNameCell_ = hostNameCell; | 187 this.host.hostName); |
| 127 this.setHostName_(); | 188 this.rootElement_.classList.toggle('clickable', clickToConnect); |
| 189 this.rootElement_.title = (clickToConnect) ? connectLabel : ''; | |
| 190 this.rootElement_.classList.toggle('host-online', !showOffline); | |
| 191 this.rootElement_.classList.toggle('host-offline', showOffline); | |
| 128 | 192 |
| 129 /** @type {remoting.HostTableEntry} */ | 193 var hostReportedError = this.host.hostOfflineReason !== ''; |
| 130 var that = this; | 194 var hostNeedsUpdate = remoting.Host.needsUpdate( |
| 131 | 195 this.host, this.webappMajorVersion_); |
| 132 /** @param {Event} event The click event. */ | 196 this.warningOverlay_.hidden = !hostNeedsUpdate && !hostReportedError; |
| 133 var beginRename = function(event) { | 197 this.hostNameLabel_.innerText = this.getHostDisplayName_(); |
| 134 that.beginRename_(); | 198 this.hostNameLabel_.title = |
| 135 event.stopPropagation(); | 199 formatHostOfflineReason(this.host.hostOfflineReason); |
| 136 }; | 200 this.renameInputField_.hidden = !this.isRenaming_(); |
| 137 /** @param {Event} event The keyup event. */ | 201 this.hostNameLabel_.hidden = this.isRenaming_(); |
| 138 var beginRenameKeyboard = function(event) { | |
| 139 if (event.which == 13 || event.which == 32) { | |
| 140 that.beginRename_(); | |
| 141 event.stopPropagation(); | |
| 142 } | |
| 143 }; | |
| 144 editButton.addEventListener('click', beginRename, true); | |
| 145 editButton.addEventListener('keyup', beginRenameKeyboard, true); | |
| 146 this.registerFocusHandlers_(editButton); | |
| 147 | |
| 148 if (opt_deleteButton) { | |
| 149 /** @param {Event} event The click event. */ | |
| 150 var confirmDelete = function(event) { | |
| 151 that.showDeleteConfirmation_(); | |
| 152 event.stopPropagation(); | |
| 153 }; | |
| 154 /** @param {Event} event The keyup event. */ | |
| 155 var confirmDeleteKeyboard = function(event) { | |
| 156 if (event.which == 13 || event.which == 32) { | |
| 157 that.showDeleteConfirmation_(); | |
| 158 } | |
| 159 }; | |
| 160 opt_deleteButton.addEventListener('click', confirmDelete, false); | |
| 161 opt_deleteButton.addEventListener('keyup', confirmDeleteKeyboard, false); | |
| 162 this.registerFocusHandlers_(opt_deleteButton); | |
| 163 } | |
| 164 this.updateStatus(); | |
| 165 }; | 202 }; |
| 166 | 203 |
| 167 /** | 204 /** |
| 168 * Update the row to reflect the current status of the host (online/offline and | |
| 169 * clickable/unclickable). | |
| 170 * | |
| 171 * @param {boolean=} opt_forEdit True if the status is being updated in order | |
| 172 * to allow the host name to be edited. | |
| 173 * @return {void} Nothing. | |
| 174 */ | |
| 175 remoting.HostTableEntry.prototype.updateStatus = function(opt_forEdit) { | |
| 176 var clickToConnect = this.host.status == 'ONLINE' && !opt_forEdit; | |
| 177 if (clickToConnect) { | |
| 178 if (!this.onConnectReference_) { | |
| 179 /** @type {string} */ | |
| 180 var encodedHostId = encodeURIComponent(this.host.hostId); | |
| 181 this.onConnectReference_ = function() { | |
| 182 remoting.connectMe2Me(encodedHostId); | |
| 183 }; | |
| 184 this.tableRow.addEventListener('click', this.onConnectReference_, false); | |
| 185 } | |
| 186 this.tableRow.classList.add('clickable'); | |
| 187 this.tableRow.title = chrome.i18n.getMessage( | |
| 188 /*i18n-content*/'TOOLTIP_CONNECT', this.host.hostName); | |
| 189 } else { | |
| 190 if (this.onConnectReference_) { | |
| 191 this.tableRow.removeEventListener('click', this.onConnectReference_, | |
| 192 false); | |
| 193 this.onConnectReference_ = null; | |
| 194 } | |
| 195 this.tableRow.classList.remove('clickable'); | |
| 196 this.tableRow.title = ''; | |
| 197 } | |
| 198 var showOffline = this.host.status != 'ONLINE'; | |
| 199 if (showOffline) { | |
| 200 this.tableRow.classList.remove('host-online'); | |
| 201 this.tableRow.classList.add('host-offline'); | |
| 202 } else { | |
| 203 this.tableRow.classList.add('host-online'); | |
| 204 this.tableRow.classList.remove('host-offline'); | |
| 205 } | |
| 206 var hostReportedError = this.host.hostOfflineReason != ''; | |
| 207 var hostNeedsUpdate = remoting.Host.needsUpdate( | |
| 208 this.host, this.webappMajorVersion_); | |
| 209 this.warningOverlay_.hidden = !hostNeedsUpdate && !hostReportedError; | |
| 210 }; | |
| 211 | |
| 212 /** | |
| 213 * Prepare the host for renaming by replacing its name with an edit box. | 205 * Prepare the host for renaming by replacing its name with an edit box. |
| 214 * @return {void} Nothing. | 206 * @return {void} Nothing. |
| 215 * @private | 207 * @private |
| 216 */ | 208 */ |
| 217 remoting.HostTableEntry.prototype.beginRename_ = function() { | 209 remoting.HostTableEntry.prototype.beginRename_ = function() { |
| 218 var editBox = | 210 this.renameInputField_.value = this.host.hostName; |
| 219 /** @type {HTMLInputElement} */ (document.createElement('input')); | 211 base.dispose(this.renameInputEventHooks_); |
| 220 editBox.type = 'text'; | 212 this.renameInputEventHooks_ = new base.Disposables( |
| 221 editBox.value = this.host.hostName; | 213 new base.DomEventHook(this.renameInputField_, 'blur', |
| 222 this.hostNameCell_.innerText = ''; | 214 this.commitRename_.bind(this), false), |
| 223 this.hostNameCell_.appendChild(editBox); | 215 new base.DomEventHook(this.renameInputField_, 'keydown', |
| 224 editBox.select(); | 216 this.onKeydown_.bind(this), false)); |
| 217 this.updateUI_(); | |
| 218 this.renameInputField_.focus(); | |
| 219 }; | |
| 225 | 220 |
| 226 this.onBlurReference_ = this.commitRename_.bind(this); | 221 /** @return {boolean} */ |
| 227 editBox.addEventListener('blur', this.onBlurReference_, false); | 222 remoting.HostTableEntry.prototype.isRenaming_ = function() { |
| 228 | 223 return Boolean(this.renameInputEventHooks_); |
| 229 editBox.addEventListener('keydown', this.onKeydown_.bind(this), false); | |
| 230 this.updateStatus(true); | |
| 231 }; | 224 }; |
| 232 | 225 |
| 233 /** | 226 /** |
| 234 * Accept the hostname entered by the user. | 227 * Accept the hostname entered by the user. |
| 235 * @return {void} Nothing. | 228 * @return {void} Nothing. |
| 236 * @private | 229 * @private |
| 237 */ | 230 */ |
| 238 remoting.HostTableEntry.prototype.commitRename_ = function() { | 231 remoting.HostTableEntry.prototype.commitRename_ = function() { |
| 239 var editBox = this.hostNameCell_.querySelector('input'); | 232 if (this.host.hostName != this.renameInputField_.value) { |
| 240 if (editBox) { | 233 this.host.hostName = this.renameInputField_.value; |
| 241 if (this.host.hostName != editBox.value) { | 234 this.onRename_(this); |
| 242 this.host.hostName = editBox.value; | 235 } |
| 243 this.onRename_(this); | 236 this.hideEditBox_(); |
| 244 } | 237 }; |
| 245 this.removeEditBox_(); | 238 |
| 239 /** | |
| 240 * Remove the edit box corresponding to the specified host, and reset its name. | |
| 241 * @return {void} Nothing. | |
| 242 * @private | |
| 243 */ | |
| 244 remoting.HostTableEntry.prototype.hideEditBox_ = function() { | |
| 245 // onblur will fire when the edit box is removed, so remove the hook. | |
| 246 base.dispose(this.renameInputEventHooks_); | |
| 247 this.renameInputEventHooks_ = null; | |
| 248 // Update the tool-top and event handler. | |
| 249 this.updateUI_(); | |
| 250 }; | |
| 251 | |
| 252 /** | |
| 253 * Handle a key event while the user is typing a host name | |
| 254 * @param {Event} event The keyboard event. | |
| 255 * @return {void} Nothing. | |
| 256 * @private | |
| 257 */ | |
| 258 remoting.HostTableEntry.prototype.onKeydown_ = function(event) { | |
| 259 if (event.which == 27) { // Escape | |
| 260 this.hideEditBox_(); | |
| 261 } else if (event.which == 13) { // Enter | |
| 262 this.commitRename_(); | |
| 246 } | 263 } |
| 247 }; | 264 }; |
| 248 | 265 |
| 249 /** | 266 /** |
| 250 * Prompt the user to confirm or cancel deletion of a host. | 267 * Prompt the user to confirm or cancel deletion of a host. |
| 251 * @return {void} Nothing. | 268 * @return {void} Nothing. |
| 252 * @private | 269 * @private |
| 253 */ | 270 */ |
| 254 remoting.HostTableEntry.prototype.showDeleteConfirmation_ = function() { | 271 remoting.HostTableEntry.prototype.showDeleteConfirmation_ = function() { |
| 255 var message = document.getElementById('confirm-host-delete-message'); | 272 var message = document.getElementById('confirm-host-delete-message'); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 293 function() { | 310 function() { |
| 294 var confirm = document.getElementById('confirm-host-delete'); | 311 var confirm = document.getElementById('confirm-host-delete'); |
| 295 var cancel = document.getElementById('cancel-host-delete'); | 312 var cancel = document.getElementById('cancel-host-delete'); |
| 296 confirm.removeEventListener('click', this.onConfirmDeleteReference_, false); | 313 confirm.removeEventListener('click', this.onConfirmDeleteReference_, false); |
| 297 cancel.removeEventListener('click', this.onCancelDeleteReference_, false); | 314 cancel.removeEventListener('click', this.onCancelDeleteReference_, false); |
| 298 this.onCancelDeleteReference_ = function() {}; | 315 this.onCancelDeleteReference_ = function() {}; |
| 299 this.onConfirmDeleteReference_ = function() {}; | 316 this.onConfirmDeleteReference_ = function() {}; |
| 300 }; | 317 }; |
| 301 | 318 |
| 302 /** | 319 /** |
| 303 * Remove the edit box corresponding to the specified host, and reset its name. | 320 * Handle a focus change event within this table row. |
| 304 * @return {void} Nothing. | 321 * @return {void} Nothing. |
| 305 * @private | 322 * @private |
| 306 */ | 323 */ |
| 307 remoting.HostTableEntry.prototype.removeEditBox_ = function() { | 324 remoting.HostTableEntry.prototype.onFocusChange_ = function() { |
| 308 var editBox = this.hostNameCell_.querySelector('input'); | 325 var element = document.activeElement; |
| 309 if (editBox) { | 326 while (element) { |
| 310 // onblur will fire when the edit box is removed, so remove the hook. | 327 if (element == this.rootElement_) { |
| 311 editBox.removeEventListener('blur', this.onBlurReference_, false); | 328 this.rootElement_.classList.add('child-focused'); |
| 329 return; | |
| 330 } | |
| 331 element = element.parentNode; | |
| 312 } | 332 } |
| 313 // Update the tool-top and event handler. | 333 this.rootElement_.classList.remove('child-focused'); |
| 314 this.updateStatus(); | |
| 315 this.setHostName_(); | |
| 316 }; | 334 }; |
| 317 | 335 |
| 318 /** | 336 /** |
| 319 * Formats host's updateTime value relative to current time (i.e. only | 337 * Formats host's updateTime value relative to current time (i.e. only |
| 320 * displaying hours and minutes if updateTime is less than a day in the past). | 338 * displaying hours and minutes if updateTime is less than a day in the past). |
| 321 * @param {string} updateTime RFC 3339 formatted date-time value. | 339 * @param {string} updateTime RFC 3339 formatted date-time value. |
| 322 * @return {string} Formatted value (i.e. 11/11/2014) | 340 * @return {string} Formatted value (i.e. 11/11/2014) |
| 323 */ | 341 */ |
| 324 function formatUpdateTime(updateTime) { | 342 function formatUpdateTime(updateTime) { |
| 325 var lastOnline = new Date(updateTime); | 343 var lastOnline = new Date(updateTime); |
| 326 var now = new Date(); | 344 var now = new Date(); |
| 327 var displayString = ''; | 345 var displayString = ''; |
| 328 if (now.getFullYear() == lastOnline.getFullYear() && | 346 if (now.getFullYear() == lastOnline.getFullYear() && |
| 329 now.getMonth() == lastOnline.getMonth() && | 347 now.getMonth() == lastOnline.getMonth() && |
| 330 now.getDate() == lastOnline.getDate()) { | 348 now.getDate() == lastOnline.getDate()) { |
| 331 return lastOnline.toLocaleTimeString(); | 349 return lastOnline.toLocaleTimeString(); |
| 332 } else { | 350 } else { |
| 333 return lastOnline.toLocaleDateString(); | 351 return lastOnline.toLocaleDateString(); |
| 334 } | 352 } |
| 335 } | 353 } |
| 336 | 354 |
| 337 /** | 355 /** |
| 338 * Formats host's host-offline-reason value (i.e. 'INVALID_HOST_CONFIGURATION') | 356 * Formats host's host-offline-reason value (i.e. 'INVALID_HOST_CONFIGURATION') |
| 339 * to a human-readable description of the error. | 357 * to a human-readable description of the error. |
| 340 * @param {string} hostOfflineReason | 358 * @param {string} hostOfflineReason |
| 341 * @return {string} | 359 * @return {string} |
| 342 */ | 360 */ |
| 343 function formatHostOfflineReason(hostOfflineReason) { | 361 function formatHostOfflineReason(hostOfflineReason) { |
| 362 if (!hostOfflineReason) { | |
| 363 return ''; | |
| 364 } | |
| 344 var knownReasonTags = [ | 365 var knownReasonTags = [ |
| 345 /*i18n-content*/ 'OFFLINE_REASON_INITIALIZATION_FAILED', | 366 /*i18n-content*/ 'OFFLINE_REASON_INITIALIZATION_FAILED', |
| 346 /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_CONFIGURATION', | 367 /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_CONFIGURATION', |
| 347 /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_ID', | 368 /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_ID', |
| 348 /*i18n-content*/ 'OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS', | 369 /*i18n-content*/ 'OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS', |
| 349 /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_DOMAIN', | 370 /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_DOMAIN', |
| 350 /*i18n-content*/ 'OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED', | 371 /*i18n-content*/ 'OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED', |
| 351 /*i18n-content*/ 'OFFLINE_REASON_USERNAME_MISMATCH' | 372 /*i18n-content*/ 'OFFLINE_REASON_USERNAME_MISMATCH' |
| 352 ]; | 373 ]; |
| 353 var offlineReasonTag = 'OFFLINE_REASON_' + hostOfflineReason; | 374 var offlineReasonTag = 'OFFLINE_REASON_' + hostOfflineReason; |
| 354 if (knownReasonTags.indexOf(offlineReasonTag) != (-1)) { | 375 if (knownReasonTags.indexOf(offlineReasonTag) != (-1)) { |
| 355 return chrome.i18n.getMessage(offlineReasonTag); | 376 return chrome.i18n.getMessage(offlineReasonTag); |
| 356 } else { | 377 } else { |
| 357 return chrome.i18n.getMessage( | 378 return chrome.i18n.getMessage( |
| 358 /*i18n-content*/ 'OFFLINE_REASON_UNKNOWN', | 379 /*i18n-content*/ 'OFFLINE_REASON_UNKNOWN', |
| 359 hostOfflineReason); | 380 hostOfflineReason); |
| 360 } | 381 } |
| 361 } | 382 } |
| 362 | 383 |
| 363 /** | 384 })(); |
| 364 * Create the DOM nodes and event handlers for the hostname cell. | |
| 365 * @return {void} Nothing. | |
| 366 * @private | |
| 367 */ | |
| 368 remoting.HostTableEntry.prototype.setHostName_ = function() { | |
| 369 var hostNameNode = /** @type {HTMLElement} */ (document.createElement('a')); | |
| 370 if (this.host.status == 'ONLINE') { | |
| 371 if (remoting.Host.needsUpdate(this.host, this.webappMajorVersion_)) { | |
| 372 hostNameNode.innerText = chrome.i18n.getMessage( | |
| 373 /*i18n-content*/'UPDATE_REQUIRED', this.host.hostName); | |
| 374 } else { | |
| 375 hostNameNode.innerText = this.host.hostName; | |
| 376 } | |
| 377 hostNameNode.href = '#'; | |
| 378 this.registerFocusHandlers_(hostNameNode); | |
| 379 /** @type {remoting.HostTableEntry} */ | |
| 380 var that = this; | |
| 381 /** @param {Event} event */ | |
| 382 var onKeyDown = function(event) { | |
| 383 if (that.onConnectReference_ && | |
| 384 (event.which == 13 || event.which == 32)) { | |
| 385 that.onConnectReference_(); | |
| 386 } | |
| 387 }; | |
| 388 hostNameNode.addEventListener('keydown', onKeyDown, false); | |
| 389 } else { | |
| 390 if (this.host.updatedTime) { | |
| 391 var formattedTime = formatUpdateTime(this.host.updatedTime); | |
| 392 hostNameNode.innerText = chrome.i18n.getMessage( | |
| 393 /*i18n-content*/'LAST_ONLINE', [this.host.hostName, formattedTime]); | |
| 394 if (this.host.hostOfflineReason) { | |
| 395 var detailsText = formatHostOfflineReason(this.host.hostOfflineReason); | |
| 396 // TODO(lukasza): Put detailsText into a hideable div (title/tooltip | |
| 397 // is not as discoverable + doesn't work well for touchscreens). | |
| 398 hostNameNode.title = detailsText; | |
| 399 } | |
| 400 } else { | |
| 401 hostNameNode.innerText = chrome.i18n.getMessage( | |
| 402 /*i18n-content*/'OFFLINE', this.host.hostName); | |
| 403 } | |
| 404 } | |
| 405 hostNameNode.classList.add('host-list-label'); | |
| 406 this.hostNameCell_.innerText = ''; // Remove previous contents (if any). | |
| 407 this.hostNameCell_.appendChild(hostNameNode); | |
| 408 }; | |
| 409 | |
| 410 /** | |
| 411 * Handle a key event while the user is typing a host name | |
| 412 * @param {Event} event The keyboard event. | |
| 413 * @return {void} Nothing. | |
| 414 * @private | |
| 415 */ | |
| 416 remoting.HostTableEntry.prototype.onKeydown_ = function(event) { | |
| 417 if (event.which == 27) { // Escape | |
| 418 this.removeEditBox_(); | |
| 419 } else if (event.which == 13) { // Enter | |
| 420 this.commitRename_(); | |
| 421 } | |
| 422 }; | |
| 423 | |
| 424 /** | |
| 425 * Register focus and blur handlers to cause the parent node to be highlighted | |
| 426 * whenever a child link has keyboard focus. Note that this is only necessary | |
| 427 * because Chrome does not yet support the draft CSS Selectors 4 specification | |
| 428 * (http://www.w3.org/TR/selectors4/#subject), which provides a more elegant | |
| 429 * solution to this problem. | |
| 430 * | |
| 431 * @param {HTMLElement} e The element on which to register the event handlers. | |
| 432 * @return {void} Nothing. | |
| 433 * @private | |
| 434 */ | |
| 435 remoting.HostTableEntry.prototype.registerFocusHandlers_ = function(e) { | |
| 436 e.addEventListener('focus', this.onFocusChange_.bind(this), false); | |
| 437 e.addEventListener('blur', this.onFocusChange_.bind(this), false); | |
| 438 }; | |
| 439 | |
| 440 /** | |
| 441 * Handle a focus change event within this table row. | |
| 442 * @return {void} Nothing. | |
| 443 * @private | |
| 444 */ | |
| 445 remoting.HostTableEntry.prototype.onFocusChange_ = function() { | |
| 446 var element = document.activeElement; | |
| 447 while (element) { | |
| 448 if (element == this.tableRow) { | |
| 449 this.tableRow.classList.add('child-focused'); | |
| 450 return; | |
| 451 } | |
| 452 element = element.parentNode; | |
| 453 } | |
| 454 this.tableRow.classList.remove('child-focused'); | |
| 455 }; | |
| OLD | NEW |