Chromium Code Reviews| Index: remoting/webapp/crd/js/host_table_entry.js |
| diff --git a/remoting/webapp/crd/js/host_table_entry.js b/remoting/webapp/crd/js/host_table_entry.js |
| index 9b6a4c47675a21c1f065ba274adf1eb61d6428e7..5735bc2f6fb9a46b8436800003d706389b9b0430 100644 |
| --- a/remoting/webapp/crd/js/host_table_entry.js |
| +++ b/remoting/webapp/crd/js/host_table_entry.js |
| @@ -7,242 +7,274 @@ |
| * Class representing an entry in the host-list portion of the home screen. |
| */ |
| -'use strict'; |
| - |
| /** @suppress {duplicate} */ |
| var remoting = remoting || {}; |
| +(function() { |
| + |
| +'use strict'; |
| + |
| /** |
| * An entry in the host table. |
| - * @param {remoting.Host} host The host, as obtained from Apiary. |
| + * |
| * @param {number} webappMajorVersion The major version nmber of the web-app, |
| * used to identify out-of-date hosts. |
| + * @param {function(string):void} onConnect Callback for |
| + * connect operations. |
| * @param {function(remoting.HostTableEntry):void} onRename Callback for |
| * rename operations. |
| * @param {function(remoting.HostTableEntry):void=} opt_onDelete Callback for |
| * delete operations. |
| + * |
| * @constructor |
| + * @implements {base.Disposable} |
| */ |
| remoting.HostTableEntry = function( |
| - host, webappMajorVersion, onRename, opt_onDelete) { |
| + webappMajorVersion, onConnect, onRename, opt_onDelete) { |
| /** @type {remoting.Host} */ |
| - this.host = host; |
| - /** @type {number} */ |
| + this.host = null; |
| + /** @private {number} */ |
| this.webappMajorVersion_ = webappMajorVersion; |
| - /** @type {function(remoting.HostTableEntry):void} @private */ |
| + /** @private {function(remoting.HostTableEntry):void} */ |
| this.onRename_ = onRename; |
| - /** @type {undefined|function(remoting.HostTableEntry):void} @private */ |
| + /** @private {undefined|function(remoting.HostTableEntry):void} */ |
| this.onDelete_ = opt_onDelete; |
| + /** @private {function(string):void} */ |
| + this.onConnect_ = onConnect; |
| - /** @type {HTMLElement} */ |
| - this.tableRow = null; |
| - /** @type {HTMLElement} @private */ |
| - this.hostNameCell_ = null; |
| - /** @type {HTMLElement} @private */ |
| + /** @private {HTMLElement} */ |
| + this.rootElement_ = null; |
| + /** @private {HTMLElement} */ |
| + this.hostNameLabel_ = null; |
| + /** @private {HTMLInputElement} */ |
| + this.renameInputField_ = null; |
| + /** @private {HTMLElement} */ |
| this.warningOverlay_ = null; |
| + |
| + /** @private {base.Disposables} */ |
| + this.renameInputEventHooks_ = null; |
| + /** @private {base.Disposables} */ |
| + this.disposables_ = new base.Disposables(); |
| + |
| // References to event handlers so that they can be removed. |
| - /** @type {function():void} @private */ |
| - this.onBlurReference_ = function() {}; |
| - /** @type {function():void} @private */ |
| + /** @private {function():void} */ |
| this.onConfirmDeleteReference_ = function() {}; |
| - /** @type {function():void} @private */ |
| + /** @private {function():void} */ |
| this.onCancelDeleteReference_ = function() {}; |
| - /** @type {function():void?} @private */ |
| - this.onConnectReference_ = null; |
| + |
| + this.createDom_(); |
| +}; |
| + |
| +/** @param {remoting.Host} host */ |
| +remoting.HostTableEntry.prototype.setHost = function(host) { |
| + this.host = host; |
| + this.updateUI_(); |
| +}; |
| + |
| +/** @return {HTMLElement} */ |
| +remoting.HostTableEntry.prototype.element = function() { |
| + return this.rootElement_; |
| +}; |
| + |
| +remoting.HostTableEntry.prototype.dispose = function() { |
| + base.dispose(this.disposables_); |
| + this.disposables_ = null; |
| + base.dispose(this.renameInputEventHooks_); |
| + this.renameInputEventHooks_ = null; |
| +}; |
| + |
| +/** @return {string} */ |
| +remoting.HostTableEntry.prototype.getHTML_ = function() { |
| + var html = |
| + '<div class="host-list-main-icon">' + |
| + '<span class="warning-overlay"></span>' + |
| + '<img src="icon_host.webp">' + |
| + '</div>' + |
| + '<div class="box-spacer"">' + |
| + '<a class="host-list-label" href="#""></a>' + |
| + '<input type="text" hidden/>' + |
| + '</div>' + |
| + '<span tabindex="0" class="clickable host-list-edit rename-button">' + |
| + '<img src="icon_pencil.webp" class="host-list-rename-icon">' + |
| + '</span>'; |
| + if (this.onDelete_) { |
| + html += |
| + '<span tabindex="0" class="clickable host-list-edit delete-button">' + |
| + '<img src="icon_cross.webp" class="host-list-remove-icon">' + |
| + '</span>'; |
| + } |
| + return '<div class="section-row host-offline">' + html + '</div>'; |
| }; |
| /** |
| * Create the HTML elements for this entry and set up event handlers. |
| * @return {void} Nothing. |
| */ |
| -remoting.HostTableEntry.prototype.createDom = function() { |
| - // Create the top-level <div> |
| - var tableRow = /** @type {HTMLElement} */ (document.createElement('div')); |
| - tableRow.classList.add('section-row'); |
| - // Create the host icon cell. |
| - var hostIconDiv = /** @type {HTMLElement} */ (document.createElement('div')); |
| - hostIconDiv.classList.add('host-list-main-icon'); |
| - var warningOverlay = |
| - /** @type {HTMLElement} */ (document.createElement('span')); |
| - hostIconDiv.appendChild(warningOverlay); |
| - var hostIcon = /** @type {HTMLElement} */ (document.createElement('img')); |
| - hostIcon.src = 'icon_host.webp'; |
| - hostIconDiv.appendChild(hostIcon); |
| - tableRow.appendChild(hostIconDiv); |
| - // Create the host name cell. |
| - var hostNameCell = /** @type {HTMLElement} */ (document.createElement('div')); |
| - hostNameCell.classList.add('box-spacer'); |
| - hostNameCell.id = 'host_' + this.host.hostId; |
| - tableRow.appendChild(hostNameCell); |
| - // Create the host rename cell. |
| - var editButton = /** @type {HTMLElement} */ (document.createElement('span')); |
| - var editButtonImg = |
| - /** @type {HTMLElement} */ (document.createElement('img')); |
| - editButtonImg.title = chrome.i18n.getMessage( |
| - /*i18n-content*/'TOOLTIP_RENAME'); |
| - editButtonImg.src = 'icon_pencil.webp'; |
| - editButton.tabIndex = 0; |
| - editButton.classList.add('clickable'); |
| - editButton.classList.add('host-list-edit'); |
| - editButtonImg.classList.add('host-list-rename-icon'); |
| - editButton.appendChild(editButtonImg); |
| - tableRow.appendChild(editButton); |
| - // Create the host delete cell. |
| - var deleteButton = |
| - /** @type {HTMLElement} */ (document.createElement('span')); |
| - var deleteButtonImg = |
| - /** @type {HTMLElement} */ (document.createElement('img')); |
| - deleteButtonImg.title = |
| - chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_DELETE'); |
| - deleteButtonImg.src = 'icon_cross.webp'; |
| - deleteButton.tabIndex = 0; |
| - deleteButton.classList.add('clickable'); |
| - deleteButton.classList.add('host-list-edit'); |
| - deleteButtonImg.classList.add('host-list-remove-icon'); |
| - deleteButton.appendChild(deleteButtonImg); |
| - tableRow.appendChild(deleteButton); |
| - |
| - this.init(tableRow, warningOverlay, hostNameCell, editButton, deleteButton); |
| +remoting.HostTableEntry.prototype.createDom_ = function() { |
| + var container = /** @type {HTMLElement} */ (document.createElement('div')); |
| + container.innerHTML = this.getHTML_(); |
| + |
| + // Setup DOM references. |
| + this.rootElement_ = /** @type {HTMLElement} */ (container.firstElementChild); |
| + this.warningOverlay_ = container.querySelector('.warning-overlay'); |
| + this.hostNameLabel_ = container.querySelector('.box-spacer a'); |
| + this.renameInputField_ = /** @type {HTMLInputElement} */ ( |
| + container.querySelector('.box-spacer input')); |
|
Jamie
2015/02/27 18:19:40
This should be accessed explicitly by class name,
kelvinp
2015/03/03 21:57:53
Done.
|
| + |
| + // Register event handlers and set tooltips. |
| + var editButton = container.querySelector('.rename-button'); |
| + var deleteButton = container.querySelector('.delete-button'); |
| + editButton.title = chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_RENAME'); |
| + this.registerButton_(editButton, this.beginRename_.bind(this)); |
| + this.registerButton_(this.rootElement_, this.onConnectButton_.bind(this)); |
| + if (deleteButton) { |
| + this.registerButton_(deleteButton, this.showDeleteConfirmation_.bind(this)); |
| + deleteButton.title = |
| + chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_DELETE'); |
| + } |
| }; |
| -/** |
| - * Associate the table row with the specified elements and callbacks, and set |
| - * up event handlers. |
| - * |
| - * @param {HTMLElement} tableRow The top-level <div> for the table entry. |
| - * @param {HTMLElement} warningOverlay The <span> element to render a warning |
| - * icon on top of the host icon. |
| - * @param {HTMLElement} hostNameCell The element containing the host name. |
| - * @param {HTMLElement} editButton The <img> containing the pencil icon for |
| - * editing the host name. |
| - * @param {HTMLElement=} opt_deleteButton The <img> containing the cross icon |
| - * for deleting the host, if present. |
| - * @return {void} Nothing. |
| - */ |
| -remoting.HostTableEntry.prototype.init = function( |
| - tableRow, warningOverlay, hostNameCell, editButton, opt_deleteButton) { |
| - this.tableRow = tableRow; |
| - this.warningOverlay_ = warningOverlay; |
| - this.hostNameCell_ = hostNameCell; |
| - this.setHostName_(); |
| - |
| - /** @type {remoting.HostTableEntry} */ |
| - var that = this; |
| - |
| - /** @param {Event} event The click event. */ |
| - var beginRename = function(event) { |
| - that.beginRename_(); |
| - event.stopPropagation(); |
| - }; |
| - /** @param {Event} event The keyup event. */ |
| - var beginRenameKeyboard = function(event) { |
| - if (event.which == 13 || event.which == 32) { |
| - that.beginRename_(); |
| - event.stopPropagation(); |
| +/** @return {base.Disposable} @private */ |
| +remoting.HostTableEntry.prototype.registerButton_ = function( |
| + /** HTMLElement */ button, /** Function */ callback) { |
|
Jamie
2015/02/27 18:19:41
Don't you need @type to annotate parameters in thi
kelvinp
2015/03/03 21:57:53
No, this is magic of the new syntax that John show
|
| + var onKeyDown = function(/** Event */ e) { |
| + if (e.which === KeyCodes.ENTER || e.which === KeyCodes.SPACEBAR) { |
| + callback(); |
| + e.stopPropagation(); |
| } |
| }; |
| - editButton.addEventListener('click', beginRename, true); |
| - editButton.addEventListener('keyup', beginRenameKeyboard, true); |
| - this.registerFocusHandlers_(editButton); |
| - |
| - if (opt_deleteButton) { |
| - /** @param {Event} event The click event. */ |
| - var confirmDelete = function(event) { |
| - that.showDeleteConfirmation_(); |
| - event.stopPropagation(); |
| - }; |
| - /** @param {Event} event The keyup event. */ |
| - var confirmDeleteKeyboard = function(event) { |
| - if (event.which == 13 || event.which == 32) { |
| - that.showDeleteConfirmation_(); |
| - } |
| - }; |
| - opt_deleteButton.addEventListener('click', confirmDelete, false); |
| - opt_deleteButton.addEventListener('keyup', confirmDeleteKeyboard, false); |
| - this.registerFocusHandlers_(opt_deleteButton); |
| + var onClick = function(/** Event */ e) { |
| + callback(); |
| + e.stopPropagation(); |
| + }; |
| + var onFocusChanged = this.onFocusChange_.bind(this); |
| + this.disposables_.add( |
| + new base.DomEventHook(button, 'click', onClick, false), |
| + new base.DomEventHook(button, 'keydown', onKeyDown, false), |
| + // Register focus and blur handlers to cause the parent node to be |
| + // highlighted whenever a child link has keyboard focus. Note that this is |
| + // only necessary because Chrome does not yet support the draft CSS |
| + // Selectors 4 specification (http://www.w3.org/TR/selectors4/#subject), |
| + // which provides a more elegant solution to this problem. |
| + new base.DomEventHook(button, 'focus', onFocusChanged, false), |
| + new base.DomEventHook(button, 'blur', onFocusChanged, false)); |
| +}; |
| + |
| + |
| +/** @return {void} @private */ |
| +remoting.HostTableEntry.prototype.onConnectButton_ = function() { |
| + if (!this.isRenaming_() && this.isOnline_()) { |
|
Jamie
2015/02/27 18:19:40
Add a comment explaining why we don't connect if w
kelvinp
2015/03/03 21:57:53
That's exactly it :) Done.
|
| + var encodedHostId = encodeURIComponent(this.host.hostId); |
| + this.onConnect_(encodedHostId); |
| } |
| - this.updateStatus(); |
| }; |
| -/** |
| - * Update the row to reflect the current status of the host (online/offline and |
| - * clickable/unclickable). |
| - * |
| - * @param {boolean=} opt_forEdit True if the status is being updated in order |
| - * to allow the host name to be edited. |
| - * @return {void} Nothing. |
| - */ |
| -remoting.HostTableEntry.prototype.updateStatus = function(opt_forEdit) { |
| - var clickToConnect = this.host.status == 'ONLINE' && !opt_forEdit; |
| - if (clickToConnect) { |
| - if (!this.onConnectReference_) { |
| - /** @type {string} */ |
| - var encodedHostId = encodeURIComponent(this.host.hostId); |
| - this.onConnectReference_ = function() { |
| - remoting.connectMe2Me(encodedHostId); |
| - }; |
| - this.tableRow.addEventListener('click', this.onConnectReference_, false); |
| +/** @return {boolean} @private */ |
| +remoting.HostTableEntry.prototype.isOnline_ = function() { |
| + return Boolean(this.host) && this.host.status === 'ONLINE'; |
| +}; |
| + |
| +/** @return {string} @private */ |
| +remoting.HostTableEntry.prototype.getHostDisplayName_ = function() { |
| + if (this.isOnline_()) { |
| + if (remoting.Host.needsUpdate(this.host, this.webappMajorVersion_)) { |
| + return chrome.i18n.getMessage( |
| + /*i18n-content*/'UPDATE_REQUIRED', this.host.hostName); |
| } |
| - this.tableRow.classList.add('clickable'); |
| - this.tableRow.title = chrome.i18n.getMessage( |
| - /*i18n-content*/'TOOLTIP_CONNECT', this.host.hostName); |
| + return this.host.hostName; |
| } else { |
| - if (this.onConnectReference_) { |
| - this.tableRow.removeEventListener('click', this.onConnectReference_, |
| - false); |
| - this.onConnectReference_ = null; |
| + if (this.host.updatedTime) { |
| + var formattedTime = formatUpdateTime(this.host.updatedTime); |
| + return chrome.i18n.getMessage(/*i18n-content*/ 'LAST_ONLINE', |
| + [this.host.hostName, formattedTime]); |
| } |
| - this.tableRow.classList.remove('clickable'); |
| - this.tableRow.title = ''; |
| + return chrome.i18n.getMessage(/*i18n-content*/ 'OFFLINE', |
| + this.host.hostName); |
| } |
| - var showOffline = this.host.status != 'ONLINE'; |
| - if (showOffline) { |
| - this.tableRow.classList.remove('host-online'); |
| - this.tableRow.classList.add('host-offline'); |
| - } else { |
| - this.tableRow.classList.add('host-online'); |
| - this.tableRow.classList.remove('host-offline'); |
| +}; |
| + |
| +/** |
| + * Update the UI to reflect the current status of the host |
| + * |
| + * @return {void} Nothing. |
| + * @private |
| + */ |
| +remoting.HostTableEntry.prototype.updateUI_ = function() { |
| + if (!this.host) { |
| + return; |
| } |
| - var hostReportedError = this.host.hostOfflineReason != ''; |
| + var clickToConnect = this.isOnline_() && !this.isRenaming_(); |
| + var showOffline = !this.isOnline_(); |
| + var connectLabel = chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_CONNECT', |
| + this.host.hostName); |
| + this.rootElement_.classList.toggle('clickable', clickToConnect); |
| + this.rootElement_.title = (clickToConnect) ? connectLabel : ''; |
| + this.rootElement_.classList.toggle('host-online', !showOffline); |
| + this.rootElement_.classList.toggle('host-offline', showOffline); |
| + |
| + var hostReportedError = this.host.hostOfflineReason !== ''; |
| var hostNeedsUpdate = remoting.Host.needsUpdate( |
| this.host, this.webappMajorVersion_); |
| this.warningOverlay_.hidden = !hostNeedsUpdate && !hostReportedError; |
| + this.hostNameLabel_.innerText = this.getHostDisplayName_(); |
| + this.hostNameLabel_.title = |
| + formatHostOfflineReason(this.host.hostOfflineReason); |
| + this.renameInputField_.hidden = !this.isRenaming_(); |
| + this.hostNameLabel_.hidden = this.isRenaming_(); |
| }; |
| /** |
| - * Prepare the host for renaming by replacing its name with an edit box. |
| + * Prepares the host for renaming by showing 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_.innerText = ''; |
| - this.hostNameCell_.appendChild(editBox); |
| - editBox.select(); |
| - |
| - this.onBlurReference_ = this.commitRename_.bind(this); |
| - editBox.addEventListener('blur', this.onBlurReference_, false); |
| - |
| - editBox.addEventListener('keydown', this.onKeydown_.bind(this), false); |
| - this.updateStatus(true); |
| + this.renameInputField_.value = this.host.hostName; |
| + base.dispose(this.renameInputEventHooks_); |
|
Jamie
2015/02/27 18:19:41
You're using the existence of renameInputHooks_ el
kelvinp
2015/03/03 21:57:53
An assert may not work as it will fire if the user
Jamie
2015/03/04 00:09:25
I don't think that's true. Clicking a second renam
kelvinp
2015/03/04 02:02:28
Done.
|
| + this.renameInputEventHooks_ = new base.Disposables( |
| + new base.DomEventHook(this.renameInputField_, 'blur', |
| + this.commitRename_.bind(this), false), |
| + new base.DomEventHook(this.renameInputField_, 'keydown', |
| + this.onKeydown_.bind(this), false)); |
| + this.updateUI_(); |
| + this.renameInputField_.focus(); |
| +}; |
| + |
| +/** @return {boolean} @private */ |
| +remoting.HostTableEntry.prototype.isRenaming_ = function() { |
| + return Boolean(this.renameInputEventHooks_); |
| +}; |
| + |
| +/** @return {void} @private */ |
| +remoting.HostTableEntry.prototype.commitRename_ = function() { |
| + if (this.host.hostName != this.renameInputField_.value) { |
| + this.host.hostName = this.renameInputField_.value; |
| + this.onRename_(this); |
| + } |
| + this.hideEditBox_(); |
| +}; |
| + |
| +/** @return {void} @private */ |
| +remoting.HostTableEntry.prototype.hideEditBox_ = function() { |
| + // onblur will fire when the edit box is removed, so remove the hook. |
| + base.dispose(this.renameInputEventHooks_); |
| + this.renameInputEventHooks_ = null; |
| + // Update the tool-top and event handler. |
|
Jamie
2015/02/27 18:19:40
s/top/top/
kelvinp
2015/03/03 21:57:53
Done.
|
| + this.updateUI_(); |
| }; |
| /** |
| - * Accept the hostname entered by the user. |
| + * 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.commitRename_ = function() { |
| - var editBox = this.hostNameCell_.querySelector('input'); |
| - if (editBox) { |
| - if (this.host.hostName != editBox.value) { |
| - this.host.hostName = editBox.value; |
| - this.onRename_(this); |
| - } |
| - this.removeEditBox_(); |
| +remoting.HostTableEntry.prototype.onKeydown_ = function(event) { |
| + if (event.which == KeyCodes.ESCAPE) { |
| + this.hideEditBox_(); |
| + } else if (event.which == KeyCodes.ENTER) { |
| + this.commitRename_(); |
| + event.stopPropagation(); |
| } |
| }; |
| @@ -300,19 +332,20 @@ remoting.HostTableEntry.prototype.cleanUpConfirmationEventListeners_ = |
| }; |
| /** |
| - * Remove the edit box corresponding to the specified host, and reset its name. |
| + * Handle a focus change event within this table row. |
| * @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.removeEventListener('blur', this.onBlurReference_, false); |
| +remoting.HostTableEntry.prototype.onFocusChange_ = function() { |
| + var element = document.activeElement; |
| + while (element) { |
| + if (element == this.rootElement_) { |
| + this.rootElement_.classList.add('child-focused'); |
| + return; |
| + } |
| + element = element.parentNode; |
| } |
| - // Update the tool-top and event handler. |
| - this.updateStatus(); |
| - this.setHostName_(); |
| + this.rootElement_.classList.remove('child-focused'); |
| }; |
| /** |
| @@ -341,115 +374,32 @@ function formatUpdateTime(updateTime) { |
| * @return {string} |
| */ |
| function formatHostOfflineReason(hostOfflineReason) { |
| + if (!hostOfflineReason) { |
| + return ''; |
| + } |
| var knownReasonTags = [ |
| - /*i18n-content*/ 'OFFLINE_REASON_INITIALIZATION_FAILED', |
| - /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_CONFIGURATION', |
| - /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_ID', |
| - /*i18n-content*/ 'OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS', |
| - /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_DOMAIN', |
| - /*i18n-content*/ 'OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED', |
| - /*i18n-content*/ 'OFFLINE_REASON_USERNAME_MISMATCH' |
| + /*i18n-content*/'OFFLINE_REASON_INITIALIZATION_FAILED', |
| + /*i18n-content*/'OFFLINE_REASON_INVALID_HOST_CONFIGURATION', |
| + /*i18n-content*/'OFFLINE_REASON_INVALID_HOST_ID', |
| + /*i18n-content*/'OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS', |
| + /*i18n-content*/'OFFLINE_REASON_INVALID_HOST_DOMAIN', |
| + /*i18n-content*/'OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED', |
| + /*i18n-content*/'OFFLINE_REASON_USERNAME_MISMATCH' |
| ]; |
| var offlineReasonTag = 'OFFLINE_REASON_' + hostOfflineReason; |
| if (knownReasonTags.indexOf(offlineReasonTag) != (-1)) { |
| return chrome.i18n.getMessage(offlineReasonTag); |
| } else { |
| return chrome.i18n.getMessage( |
| - /*i18n-content*/ 'OFFLINE_REASON_UNKNOWN', |
| - hostOfflineReason); |
| + /*i18n-content*/'OFFLINE_REASON_UNKNOWN', hostOfflineReason); |
| } |
| } |
| -/** |
| - * Create the DOM nodes and event handlers for the hostname cell. |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.HostTableEntry.prototype.setHostName_ = function() { |
| - var hostNameNode = /** @type {HTMLElement} */ (document.createElement('a')); |
| - if (this.host.status == 'ONLINE') { |
| - if (remoting.Host.needsUpdate(this.host, this.webappMajorVersion_)) { |
| - hostNameNode.innerText = chrome.i18n.getMessage( |
| - /*i18n-content*/'UPDATE_REQUIRED', this.host.hostName); |
| - } else { |
| - hostNameNode.innerText = this.host.hostName; |
| - } |
| - hostNameNode.href = '#'; |
| - this.registerFocusHandlers_(hostNameNode); |
| - /** @type {remoting.HostTableEntry} */ |
| - var that = this; |
| - /** @param {Event} event */ |
| - var onKeyDown = function(event) { |
| - if (that.onConnectReference_ && |
| - (event.which == 13 || event.which == 32)) { |
| - that.onConnectReference_(); |
| - } |
| - }; |
| - hostNameNode.addEventListener('keydown', onKeyDown, false); |
| - } else { |
| - if (this.host.updatedTime) { |
| - var formattedTime = formatUpdateTime(this.host.updatedTime); |
| - hostNameNode.innerText = chrome.i18n.getMessage( |
| - /*i18n-content*/'LAST_ONLINE', [this.host.hostName, formattedTime]); |
| - if (this.host.hostOfflineReason) { |
| - var detailsText = formatHostOfflineReason(this.host.hostOfflineReason); |
| - // TODO(lukasza): Put detailsText into a hideable div (title/tooltip |
| - // is not as discoverable + doesn't work well for touchscreens). |
| - hostNameNode.title = detailsText; |
| - } |
| - } else { |
| - hostNameNode.innerText = chrome.i18n.getMessage( |
| - /*i18n-content*/'OFFLINE', this.host.hostName); |
| - } |
| - } |
| - hostNameNode.classList.add('host-list-label'); |
| - this.hostNameCell_.innerText = ''; // Remove previous contents (if any). |
| - this.hostNameCell_.appendChild(hostNameNode); |
| -}; |
| - |
| -/** |
| - * 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_(); |
| - } |
| -}; |
| - |
| -/** |
| - * Register focus and blur handlers to cause the parent node to be highlighted |
| - * whenever a child link has keyboard focus. Note that this is only necessary |
| - * because Chrome does not yet support the draft CSS Selectors 4 specification |
| - * (http://www.w3.org/TR/selectors4/#subject), which provides a more elegant |
| - * solution to this problem. |
| - * |
| - * @param {HTMLElement} e The element on which to register the event handlers. |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.HostTableEntry.prototype.registerFocusHandlers_ = function(e) { |
| - e.addEventListener('focus', this.onFocusChange_.bind(this), false); |
| - e.addEventListener('blur', this.onFocusChange_.bind(this), false); |
| +/** @enum {number} */ |
| +var KeyCodes = { |
| + ENTER: 13, |
| + ESCAPE: 27, |
| + SPACEBAR: 32 |
| }; |
| -/** |
| - * Handle a focus change event within this table row. |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.HostTableEntry.prototype.onFocusChange_ = function() { |
| - var element = document.activeElement; |
| - while (element) { |
| - if (element == this.tableRow) { |
| - this.tableRow.classList.add('child-focused'); |
| - return; |
| - } |
| - element = element.parentNode; |
| - } |
| - this.tableRow.classList.remove('child-focused'); |
| -}; |
| +})(); |