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

Side by Side Diff: remoting/webapp/crd/js/host_table_entry.js

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

Powered by Google App Engine
This is Rietveld 408576698