Loosen up heuristics for detecting account creation forms.
[chromium-blink-merge.git] / remoting / webapp / host_table_entry.js
blob0407b9b6cce88041b5f58fb4fdb8f0c38e00d8a5
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.
5 /**
6 * @fileoverview
7 * Class representing an entry in the host-list portion of the home screen.
8 */
10 'use strict';
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
15 /**
16 * The deserialized form of the chromoting host as returned by Apiary.
17 * Note that the object has more fields than are detailed below--these
18 * are just the ones that we refer to directly.
19 * @constructor
21 remoting.Host = function() {
22 /** @type {string} */
23 this.hostName = '';
24 /** @type {string} */
25 this.hostId = '';
26 /** @type {string} */
27 this.status = '';
28 /** @type {string} */
29 this.jabberId = '';
30 /** @type {string} */
31 this.publicKey = '';
34 /**
35 * An entry in the host table.
36 * @constructor
38 remoting.HostTableEntry = function() {
39 /** @type {remoting.Host} */
40 this.host = null;
41 /** @type {HTMLElement} */
42 this.tableRow = null;
43 /** @type {HTMLElement} @private */
44 this.hostNameCell_ = null;
45 /** @type {function(remoting.HostTableEntry):void} @private */
46 this.onRename_ = function(host) {};
47 /** @type {undefined|function(remoting.HostTableEntry):void} @private */
48 this.onDelete_ = function(host) {};
49 // References to event handlers so that they can be removed.
50 /** @type {function():void} @private */
51 this.onBlurReference_ = function() {};
52 /** @type {function():void} @private */
53 this.onConfirmDeleteReference_ = function() {};
54 /** @type {function():void} @private */
55 this.onCancelDeleteReference_ = function() {};
56 /** @type {function():void?} @private */
57 this.onConnectReference_ = null;
60 /**
61 * Create the HTML elements for this entry and set up event handlers.
62 * @param {remoting.Host} host The host, as obtained from Apiary.
63 * @param {function(remoting.HostTableEntry):void} onRename Callback for
64 * rename operations.
65 * @param {function(remoting.HostTableEntry):void} onDelete Callback for
66 * delete operations.
67 * @return {void} Nothing.
69 remoting.HostTableEntry.prototype.create = function(host, onRename, onDelete) {
70 // Create the top-level <div>
71 var tableRow = /** @type {HTMLElement} */ document.createElement('div');
72 tableRow.classList.add('section-row');
73 // Create the host icon cell.
74 var hostIcon = /** @type {HTMLElement} */ document.createElement('img');
75 hostIcon.src = 'icon_host.png';
76 hostIcon.classList.add('host-list-main-icon');
77 tableRow.appendChild(hostIcon);
78 // Create the host name cell.
79 var hostNameCell = /** @type {HTMLElement} */ document.createElement('div');
80 hostNameCell.classList.add('box-spacer');
81 tableRow.appendChild(hostNameCell);
82 // Create the host rename cell.
83 var editButton = /** @type {HTMLElement} */ document.createElement('span');
84 var editButtonImg = /** @type {HTMLElement} */ document.createElement('img');
85 editButtonImg.title = chrome.i18n.getMessage(
86 /*i18n-content*/'TOOLTIP_RENAME');
87 editButtonImg.src = 'icon_pencil.png';
88 editButton.tabIndex = 0;
89 editButton.classList.add('clickable');
90 editButton.classList.add('host-list-edit');
91 editButtonImg.classList.add('host-list-rename-icon');
92 editButton.appendChild(editButtonImg);
93 tableRow.appendChild(editButton);
94 // Create the host delete cell.
95 var deleteButton = /** @type {HTMLElement} */ document.createElement('span');
96 var deleteButtonImg =
97 /** @type {HTMLElement} */ document.createElement('img');
98 deleteButtonImg.title =
99 chrome.i18n.getMessage(/*i18n-content*/'TOOLTIP_DELETE');
100 deleteButtonImg.src = 'icon_cross.png';
101 deleteButton.tabIndex = 0;
102 deleteButton.classList.add('clickable');
103 deleteButton.classList.add('host-list-edit');
104 deleteButtonImg.classList.add('host-list-remove-icon');
105 deleteButton.appendChild(deleteButtonImg);
106 tableRow.appendChild(deleteButton);
108 this.init(host, tableRow, hostNameCell, editButton, onRename,
109 deleteButton, onDelete);
114 * Associate the table row with the specified elements and callbacks, and set
115 * up event handlers.
117 * @param {remoting.Host} host The host, as obtained from Apiary.
118 * @param {HTMLElement} tableRow The top-level <div> for the table entry.
119 * @param {HTMLElement} hostNameCell The element containing the host name.
120 * @param {HTMLElement} editButton The <img> containing the pencil icon for
121 * editing the host name.
122 * @param {function(remoting.HostTableEntry):void} onRename Callback for
123 * rename operations.
124 * @param {HTMLElement=} opt_deleteButton The <img> containing the cross icon
125 * for deleting the host, if present.
126 * @param {function(remoting.HostTableEntry):void=} opt_onDelete Callback for
127 * delete operations.
128 * @return {void} Nothing.
130 remoting.HostTableEntry.prototype.init = function(
131 host, tableRow, hostNameCell, editButton, onRename,
132 opt_deleteButton, opt_onDelete) {
133 this.host = host;
134 this.onRename_ = onRename;
135 this.onDelete_ = opt_onDelete;
136 this.tableRow = tableRow;
137 this.hostNameCell_ = hostNameCell;
139 this.setHostName_();
141 /** @type {remoting.HostTableEntry} */
142 var that = this;
144 /** @param {Event} event The click event. */
145 var beginRename = function(event) {
146 that.beginRename_();
147 event.stopPropagation();
149 /** @param {Event} event The keyup event. */
150 var beginRenameKeyboard = function(event) {
151 if (event.which == 13 || event.which == 32) {
152 that.beginRename_();
153 event.stopPropagation();
156 editButton.addEventListener('click', beginRename, true);
157 editButton.addEventListener('keyup', beginRenameKeyboard, true);
158 this.registerFocusHandlers_(editButton);
160 if (opt_deleteButton) {
161 /** @param {Event} event The click event. */
162 var confirmDelete = function(event) {
163 that.showDeleteConfirmation_();
164 event.stopPropagation();
166 /** @param {Event} event The keyup event. */
167 var confirmDeleteKeyboard = function(event) {
168 if (event.which == 13 || event.which == 32) {
169 that.showDeleteConfirmation_();
172 opt_deleteButton.addEventListener('click', confirmDelete, false);
173 opt_deleteButton.addEventListener('keyup', confirmDeleteKeyboard, false);
174 this.registerFocusHandlers_(opt_deleteButton);
176 this.updateStatus();
180 * Update the row to reflect the current status of the host (online/offline and
181 * clickable/unclickable).
183 * @param {boolean=} opt_forEdit True if the status is being updated in order
184 * to allow the host name to be edited.
185 * @return {void} Nothing.
187 remoting.HostTableEntry.prototype.updateStatus = function(opt_forEdit) {
188 var clickToConnect = this.host.status == 'ONLINE' && !opt_forEdit;
189 if (clickToConnect) {
190 if (!this.onConnectReference_) {
191 /** @type {string} */
192 var encodedHostId = encodeURIComponent(this.host.hostId)
193 this.onConnectReference_ = function() {
194 var hostUrl = chrome.extension.getURL('main.html') +
195 '?mode=me2me&hostId=' + encodedHostId;
196 window.location.assign(hostUrl);
198 this.tableRow.addEventListener('click', this.onConnectReference_, false);
200 this.tableRow.classList.add('clickable');
201 this.tableRow.title = chrome.i18n.getMessage(
202 /*i18n-content*/'TOOLTIP_CONNECT', this.host.hostName);
203 } else {
204 if (this.onConnectReference_) {
205 this.tableRow.removeEventListener('click', this.onConnectReference_,
206 false);
207 this.onConnectReference_ = null;
209 this.tableRow.classList.remove('clickable');
210 this.tableRow.title = '';
212 var showOffline = this.host.status != 'ONLINE';
213 if (showOffline) {
214 this.tableRow.classList.remove('host-online');
215 this.tableRow.classList.add('host-offline');
216 } else {
217 this.tableRow.classList.add('host-online');
218 this.tableRow.classList.remove('host-offline');
223 * Prepare the host for renaming by replacing its name with an edit box.
224 * @return {void} Nothing.
225 * @private
227 remoting.HostTableEntry.prototype.beginRename_ = function() {
228 var editBox = /** @type {HTMLInputElement} */ document.createElement('input');
229 editBox.type = 'text';
230 editBox.value = this.host.hostName;
231 this.hostNameCell_.innerText = '';
232 this.hostNameCell_.appendChild(editBox);
233 editBox.select();
235 this.onBlurReference_ = this.commitRename_.bind(this);
236 editBox.addEventListener('blur', this.onBlurReference_, false);
238 editBox.addEventListener('keydown', this.onKeydown_.bind(this), false);
239 this.updateStatus(true);
243 * Accept the hostname entered by the user.
244 * @return {void} Nothing.
245 * @private
247 remoting.HostTableEntry.prototype.commitRename_ = function() {
248 var editBox = this.hostNameCell_.querySelector('input');
249 if (editBox) {
250 if (this.host.hostName != editBox.value) {
251 this.host.hostName = editBox.value;
252 this.onRename_(this);
254 // Update the tool-top and event handler.
255 this.updateStatus();
256 this.removeEditBox_();
261 * Prompt the user to confirm or cancel deletion of a host.
262 * @return {void} Nothing.
263 * @private
265 remoting.HostTableEntry.prototype.showDeleteConfirmation_ = function() {
266 var message = document.getElementById('confirm-host-delete-message');
267 l10n.localizeElement(message, this.host.hostName);
268 var confirm = document.getElementById('confirm-host-delete');
269 var cancel = document.getElementById('cancel-host-delete');
270 this.onConfirmDeleteReference_ = this.confirmDelete_.bind(this);
271 this.onCancelDeleteReference_ = this.cancelDelete_.bind(this);
272 confirm.addEventListener('click', this.onConfirmDeleteReference_, false);
273 cancel.addEventListener('click', this.onCancelDeleteReference_, false);
274 remoting.setMode(remoting.AppMode.CONFIRM_HOST_DELETE);
278 * Confirm deletion of a host.
279 * @return {void} Nothing.
280 * @private
282 remoting.HostTableEntry.prototype.confirmDelete_ = function() {
283 this.onDelete_(this);
284 this.cleanUpConfirmationEventListeners_();
285 remoting.setMode(remoting.AppMode.HOME);
289 * Cancel deletion of a host.
290 * @return {void} Nothing.
291 * @private
293 remoting.HostTableEntry.prototype.cancelDelete_ = function() {
294 this.cleanUpConfirmationEventListeners_();
295 remoting.setMode(remoting.AppMode.HOME);
299 * Remove the confirm and cancel event handlers, which refer to this object.
300 * @return {void} Nothing.
301 * @private
303 remoting.HostTableEntry.prototype.cleanUpConfirmationEventListeners_ =
304 function() {
305 var confirm = document.getElementById('confirm-host-delete');
306 var cancel = document.getElementById('cancel-host-delete');
307 confirm.removeEventListener('click', this.onConfirmDeleteReference_, false);
308 cancel.removeEventListener('click', this.onCancelDeleteReference_, false);
309 this.onCancelDeleteReference_ = function() {};
310 this.onConfirmDeleteReference_ = function() {};
314 * Remove the edit box corresponding to the specified host, and reset its name.
315 * @return {void} Nothing.
316 * @private
318 remoting.HostTableEntry.prototype.removeEditBox_ = function() {
319 var editBox = this.hostNameCell_.querySelector('input');
320 if (editBox) {
321 // onblur will fire when the edit box is removed, so remove the hook.
322 editBox.removeEventListener('blur', this.onBlurReference_, false);
324 this.setHostName_();
328 * Create the DOM nodes and event handlers for the hostname cell.
329 * @return {void} Nothing.
330 * @private
332 remoting.HostTableEntry.prototype.setHostName_ = function() {
333 var hostNameNode = /** @type {HTMLElement} */ document.createElement('span');
334 if (this.host.status == 'ONLINE') {
335 hostNameNode.innerText = this.host.hostName;
336 hostNameNode.tabIndex = 0;
337 this.registerFocusHandlers_(hostNameNode);
338 /** @type {remoting.HostTableEntry} */
339 var that = this;
340 /** @param {Event} event */
341 var onKeyDown = function(event) {
342 if (that.onConnectReference_ &&
343 (event.which == 13 || event.which == 32)) {
344 that.onConnectReference_();
347 hostNameNode.addEventListener('keydown', onKeyDown, false);
348 } else {
349 hostNameNode.innerText = chrome.i18n.getMessage(/*i18n-content*/'OFFLINE',
350 this.host.hostName);
352 hostNameNode.classList.add('host-list-label');
353 this.hostNameCell_.innerText = ''; // Remove previous contents (if any).
354 this.hostNameCell_.appendChild(hostNameNode);
358 * Handle a key event while the user is typing a host name
359 * @param {Event} event The keyboard event.
360 * @return {void} Nothing.
361 * @private
363 remoting.HostTableEntry.prototype.onKeydown_ = function(event) {
364 if (event.which == 27) { // Escape
365 this.removeEditBox_();
366 } else if (event.which == 13) { // Enter
367 this.commitRename_();
372 * Register focus and blur handlers to cause the parent node to be highlighted
373 * whenever a child link has keyboard focus. Note that this is only necessary
374 * because Chrome does not yet support the draft CSS Selectors 4 specification
375 * (http://www.w3.org/TR/selectors4/#subject), which provides a more elegant
376 * solution to this problem.
378 * @param {HTMLElement} e The element on which to register the event handlers.
379 * @return {void} Nothing.
380 * @private
382 remoting.HostTableEntry.prototype.registerFocusHandlers_ = function(e) {
383 e.addEventListener('focus', this.onFocusChange_.bind(this), false);
384 e.addEventListener('blur', this.onFocusChange_.bind(this), false);
388 * Handle a focus change event within this table row.
389 * @return {void} Nothing.
390 * @private
392 remoting.HostTableEntry.prototype.onFocusChange_ = function() {
393 var element = document.activeElement;
394 while (element) {
395 if (element == this.tableRow) {
396 this.tableRow.classList.add('child-focused');
397 return;
399 element = element.parentNode;
401 this.tableRow.classList.remove('child-focused');