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
3 // found in the LICENSE file.
7 * Class representing an entry in the host-list portion of the home screen.
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
16 * 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 * used to identify out-of-date hosts.
20 * @param {function(remoting.HostTableEntry):void} onRename Callback for
22 * @param {function(remoting.HostTableEntry):void=} opt_onDelete Callback for
26 remoting.HostTableEntry = function(
27 host, webappMajorVersion, onRename, opt_onDelete) {
28 /** @type {remoting.Host} */
31 this.webappMajorVersion_ = webappMajorVersion;
32 /** @type {function(remoting.HostTableEntry):void} @private */
33 this.onRename_ = onRename;
34 /** @type {undefined|function(remoting.HostTableEntry):void} @private */
35 this.onDelete_ = opt_onDelete;
37 /** @type {HTMLElement} */
39 /** @type {HTMLElement} @private */
40 this.hostNameCell_ = null;
41 /** @type {HTMLElement} @private */
42 this.warningOverlay_ = null;
43 // References to event handlers so that they can be removed.
44 /** @type {function():void} @private */
45 this.onBlurReference_ = function() {};
46 /** @type {function():void} @private */
47 this.onConfirmDeleteReference_ = function() {};
48 /** @type {function():void} @private */
49 this.onCancelDeleteReference_ = function() {};
50 /** @type {function():void?} @private */
51 this.onConnectReference_ = null;
55 * Create the HTML elements for this entry and set up event handlers.
56 * @return {void} Nothing.
58 remoting.HostTableEntry.prototype.createDom = function() {
59 // Create the top-level <div>
60 var tableRow = /** @type {HTMLElement} */ document.createElement('div');
61 tableRow.classList.add('section-row');
62 // Create the host icon cell.
63 var hostIconDiv = /** @type {HTMLElement} */ document.createElement('div');
64 hostIconDiv.classList.add('host-list-main-icon');
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 tableRow.appendChild(hostNameCell);
76 // Create the host rename cell.
77 var editButton = /** @type {HTMLElement} */ document.createElement('span');
78 var editButtonImg = /** @type {HTMLElement} */ document.createElement('img');
79 editButtonImg.title = chrome.i18n.getMessage(
80 /*i18n-content*/'TOOLTIP_RENAME');
81 editButtonImg.src = 'icon_pencil.webp';
82 editButton.tabIndex = 0;
83 editButton.classList.add('clickable');
84 editButton.classList.add('host-list-edit');
85 editButtonImg.classList.add('host-list-rename-icon');
86 editButton.appendChild(editButtonImg);
87 tableRow.appendChild(editButton);
88 // Create the host delete cell.
89 var deleteButton = /** @type {HTMLElement} */ document.createElement('span');
91 /** @type {HTMLElement} */ document.createElement('img');
92 deleteButtonImg.title =
93 chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_DELETE');
94 deleteButtonImg.src = 'icon_cross.webp';
95 deleteButton.tabIndex = 0;
96 deleteButton.classList.add('clickable');
97 deleteButton.classList.add('host-list-edit');
98 deleteButtonImg.classList.add('host-list-remove-icon');
99 deleteButton.appendChild(deleteButtonImg);
100 tableRow.appendChild(deleteButton);
102 this.init(tableRow, warningOverlay, hostNameCell, editButton, deleteButton);
106 * Associate the table row with the specified elements and callbacks, and set
109 * @param {HTMLElement} tableRow The top-level <div> for the table entry.
110 * @param {HTMLElement} warningOverlay The <span> element to render a warning
111 * icon on top of the host icon.
112 * @param {HTMLElement} hostNameCell The element containing the host name.
113 * @param {HTMLElement} editButton The <img> containing the pencil icon for
114 * editing the host name.
115 * @param {HTMLElement=} opt_deleteButton The <img> containing the cross icon
116 * for deleting the host, if present.
117 * @return {void} Nothing.
119 remoting.HostTableEntry.prototype.init = function(
120 tableRow, warningOverlay, hostNameCell, editButton, opt_deleteButton) {
121 this.tableRow = tableRow;
122 this.warningOverlay_ = warningOverlay;
123 this.hostNameCell_ = hostNameCell;
126 /** @type {remoting.HostTableEntry} */
129 /** @param {Event} event The click event. */
130 var beginRename = function(event) {
132 event.stopPropagation();
134 /** @param {Event} event The keyup event. */
135 var beginRenameKeyboard = function(event) {
136 if (event.which == 13 || event.which == 32) {
138 event.stopPropagation();
141 editButton.addEventListener('click', beginRename, true);
142 editButton.addEventListener('keyup', beginRenameKeyboard, true);
143 this.registerFocusHandlers_(editButton);
145 if (opt_deleteButton) {
146 /** @param {Event} event The click event. */
147 var confirmDelete = function(event) {
148 that.showDeleteConfirmation_();
149 event.stopPropagation();
151 /** @param {Event} event The keyup event. */
152 var confirmDeleteKeyboard = function(event) {
153 if (event.which == 13 || event.which == 32) {
154 that.showDeleteConfirmation_();
157 opt_deleteButton.addEventListener('click', confirmDelete, false);
158 opt_deleteButton.addEventListener('keyup', confirmDeleteKeyboard, false);
159 this.registerFocusHandlers_(opt_deleteButton);
165 * Update the row to reflect the current status of the host (online/offline and
166 * clickable/unclickable).
168 * @param {boolean=} opt_forEdit True if the status is being updated in order
169 * to allow the host name to be edited.
170 * @return {void} Nothing.
172 remoting.HostTableEntry.prototype.updateStatus = function(opt_forEdit) {
173 var clickToConnect = this.host.status == 'ONLINE' && !opt_forEdit;
174 if (clickToConnect) {
175 if (!this.onConnectReference_) {
176 /** @type {string} */
177 var encodedHostId = encodeURIComponent(this.host.hostId)
178 this.onConnectReference_ = function() {
179 remoting.connectMe2Me(encodedHostId);
181 this.tableRow.addEventListener('click', this.onConnectReference_, false);
183 this.tableRow.classList.add('clickable');
184 this.tableRow.title = chrome.i18n.getMessage(
185 /*i18n-content*/'TOOLTIP_CONNECT', this.host.hostName);
187 if (this.onConnectReference_) {
188 this.tableRow.removeEventListener('click', this.onConnectReference_,
190 this.onConnectReference_ = null;
192 this.tableRow.classList.remove('clickable');
193 this.tableRow.title = '';
195 var showOffline = this.host.status != 'ONLINE';
197 this.tableRow.classList.remove('host-online');
198 this.tableRow.classList.add('host-offline');
200 this.tableRow.classList.add('host-online');
201 this.tableRow.classList.remove('host-offline');
203 this.warningOverlay_.hidden = !remoting.Host.needsUpdate(
204 this.host, this.webappMajorVersion_);
208 * Prepare the host for renaming by replacing its name with an edit box.
209 * @return {void} Nothing.
212 remoting.HostTableEntry.prototype.beginRename_ = function() {
213 var editBox = /** @type {HTMLInputElement} */ document.createElement('input');
214 editBox.type = 'text';
215 editBox.value = this.host.hostName;
216 this.hostNameCell_.innerText = '';
217 this.hostNameCell_.appendChild(editBox);
220 this.onBlurReference_ = this.commitRename_.bind(this);
221 editBox.addEventListener('blur', this.onBlurReference_, false);
223 editBox.addEventListener('keydown', this.onKeydown_.bind(this), false);
224 this.updateStatus(true);
228 * Accept the hostname entered by the user.
229 * @return {void} Nothing.
232 remoting.HostTableEntry.prototype.commitRename_ = function() {
233 var editBox = this.hostNameCell_.querySelector('input');
235 if (this.host.hostName != editBox.value) {
236 this.host.hostName = editBox.value;
237 this.onRename_(this);
239 this.removeEditBox_();
244 * Prompt the user to confirm or cancel deletion of a host.
245 * @return {void} Nothing.
248 remoting.HostTableEntry.prototype.showDeleteConfirmation_ = function() {
249 var message = document.getElementById('confirm-host-delete-message');
250 l10n.localizeElement(message, this.host.hostName);
251 var confirm = document.getElementById('confirm-host-delete');
252 var cancel = document.getElementById('cancel-host-delete');
253 this.onConfirmDeleteReference_ = this.confirmDelete_.bind(this);
254 this.onCancelDeleteReference_ = this.cancelDelete_.bind(this);
255 confirm.addEventListener('click', this.onConfirmDeleteReference_, false);
256 cancel.addEventListener('click', this.onCancelDeleteReference_, false);
257 remoting.setMode(remoting.AppMode.CONFIRM_HOST_DELETE);
261 * Confirm deletion of a host.
262 * @return {void} Nothing.
265 remoting.HostTableEntry.prototype.confirmDelete_ = function() {
266 this.onDelete_(this);
267 this.cleanUpConfirmationEventListeners_();
268 remoting.setMode(remoting.AppMode.HOME);
272 * Cancel deletion of a host.
273 * @return {void} Nothing.
276 remoting.HostTableEntry.prototype.cancelDelete_ = function() {
277 this.cleanUpConfirmationEventListeners_();
278 remoting.setMode(remoting.AppMode.HOME);
282 * Remove the confirm and cancel event handlers, which refer to this object.
283 * @return {void} Nothing.
286 remoting.HostTableEntry.prototype.cleanUpConfirmationEventListeners_ =
288 var confirm = document.getElementById('confirm-host-delete');
289 var cancel = document.getElementById('cancel-host-delete');
290 confirm.removeEventListener('click', this.onConfirmDeleteReference_, false);
291 cancel.removeEventListener('click', this.onCancelDeleteReference_, false);
292 this.onCancelDeleteReference_ = function() {};
293 this.onConfirmDeleteReference_ = function() {};
297 * Remove the edit box corresponding to the specified host, and reset its name.
298 * @return {void} Nothing.
301 remoting.HostTableEntry.prototype.removeEditBox_ = function() {
302 var editBox = this.hostNameCell_.querySelector('input');
304 // onblur will fire when the edit box is removed, so remove the hook.
305 editBox.removeEventListener('blur', this.onBlurReference_, false);
307 // Update the tool-top and event handler.
313 * Create the DOM nodes and event handlers for the hostname cell.
314 * @return {void} Nothing.
317 remoting.HostTableEntry.prototype.setHostName_ = function() {
318 var hostNameNode = /** @type {HTMLElement} */ document.createElement('a');
319 if (this.host.status == 'ONLINE') {
320 if (remoting.Host.needsUpdate(this.host, this.webappMajorVersion_)) {
321 hostNameNode.innerText = chrome.i18n.getMessage(
322 /*i18n-content*/'UPDATE_REQUIRED', this.host.hostName);
324 hostNameNode.innerText = this.host.hostName;
326 hostNameNode.href = '#';
327 this.registerFocusHandlers_(hostNameNode);
328 /** @type {remoting.HostTableEntry} */
330 /** @param {Event} event */
331 var onKeyDown = function(event) {
332 if (that.onConnectReference_ &&
333 (event.which == 13 || event.which == 32)) {
334 that.onConnectReference_();
337 hostNameNode.addEventListener('keydown', onKeyDown, false);
339 hostNameNode.innerText = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE',
342 hostNameNode.classList.add('host-list-label');
343 this.hostNameCell_.innerText = ''; // Remove previous contents (if any).
344 this.hostNameCell_.appendChild(hostNameNode);
348 * Handle a key event while the user is typing a host name
349 * @param {Event} event The keyboard event.
350 * @return {void} Nothing.
353 remoting.HostTableEntry.prototype.onKeydown_ = function(event) {
354 if (event.which == 27) { // Escape
355 this.removeEditBox_();
356 } else if (event.which == 13) { // Enter
357 this.commitRename_();
362 * Register focus and blur handlers to cause the parent node to be highlighted
363 * whenever a child link has keyboard focus. Note that this is only necessary
364 * because Chrome does not yet support the draft CSS Selectors 4 specification
365 * (http://www.w3.org/TR/selectors4/#subject), which provides a more elegant
366 * solution to this problem.
368 * @param {HTMLElement} e The element on which to register the event handlers.
369 * @return {void} Nothing.
372 remoting.HostTableEntry.prototype.registerFocusHandlers_ = function(e) {
373 e.addEventListener('focus', this.onFocusChange_.bind(this), false);
374 e.addEventListener('blur', this.onFocusChange_.bind(this), false);
378 * Handle a focus change event within this table row.
379 * @return {void} Nothing.
382 remoting.HostTableEntry.prototype.onFocusChange_ = function() {
383 var element = document.activeElement;
385 if (element == this.tableRow) {
386 this.tableRow.classList.add('child-focused');
389 element = element.parentNode;
391 this.tableRow.classList.remove('child-focused');