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

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: Created 5 years, 10 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
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.
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
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698