Home | History | Annotate | Download | only in webapp
      1 // Copyright 2013 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.
      4 
      5 /**
      6  * @fileoverview
      7  * Dialog for showing the list of clients that are paired with this host.
      8  */
      9 
     10 'use strict';
     11 
     12 /** @suppress {duplicate} */
     13 var remoting = remoting || {};
     14 
     15 /**
     16  * Extract the appropriate fields from the input parameter, if present. Use the
     17  * isValid() method to determine whether or not a valid paired client instance
     18  * was provided.
     19  *
     20  * @param {Object} pairedClient The paired client, as returned by the native
     21  *     host instance.
     22  * @constructor
     23  */
     24 remoting.PairedClient = function(pairedClient) {
     25   if (!pairedClient || typeof(pairedClient) != 'object') {
     26     return;
     27   }
     28 
     29   this.clientId = /** @type {string} */ (pairedClient['clientId']);
     30   this.clientName = /** @type {string} */ (pairedClient['clientName']);
     31   this.createdTime = /** @type {number} */ (pairedClient['createdTime']);
     32 
     33   /** @type {Element} */
     34   this.tableRow = null;
     35   /** @type {Element} */
     36   this.deleteButton = null;
     37 };
     38 
     39 /**
     40  * Create the DOM elements representing this client in the paired client
     41  * manager dialog.
     42  *
     43  * @param {remoting.PairedClientManager} parent The paired client manager
     44  *     dialog containing this row.
     45  * @param {Element} tbody The <tbody> element to which to append the row.
     46  */
     47 remoting.PairedClient.prototype.createDom = function(parent, tbody) {
     48   this.tableRow = document.createElement('tr');
     49   var td = document.createElement('td');
     50   td.innerText = new Date(this.createdTime).toLocaleDateString();
     51   this.tableRow.appendChild(td);
     52   td = document.createElement('td');
     53   td.innerText = this.clientName;
     54   this.tableRow.appendChild(td);
     55   td = document.createElement('td');
     56   this.deleteButton = document.createElement('a');
     57   this.deleteButton.href = '#';
     58   this.deleteButton.innerText = chrome.i18n.getMessage(
     59       /*i18n-content*/'DELETE_PAIRED_CLIENT');
     60   this.deleteButton.id = 'delete-client-' + this.clientId;
     61   this.deleteButton.addEventListener(
     62       'click',
     63       parent.deletePairedClient.bind(parent, this),
     64       false);
     65   td.appendChild(this.deleteButton);
     66   this.tableRow.appendChild(td);
     67   tbody.appendChild(this.tableRow);
     68 };
     69 
     70 /**
     71  * Show or hide the "Delete" button for this row.
     72  *
     73  * @param {boolean} show True to show the button; false to hide it.
     74  */
     75 remoting.PairedClient.prototype.showButton = function(show) {
     76   this.deleteButton.hidden = !show;
     77 };
     78 
     79 /**
     80  * @return {boolean} True if the constructor parameter was a well-formed
     81  *     paired client instance.
     82  */
     83 remoting.PairedClient.prototype.isValid = function() {
     84   return typeof(this.clientId) == 'string' &&
     85          typeof(this.clientName) == 'string' &&
     86          typeof(this.createdTime) == 'number';
     87 };
     88 
     89 /**
     90  * Converts a raw object to an array of PairedClient instances. Returns null if
     91  * the input object is incorrectly formatted.
     92  *
     93  * @param {*} pairedClients The object to convert.
     94  * @return {Array.<remoting.PairedClient>} The converted result.
     95  */
     96 remoting.PairedClient.convertToPairedClientArray = function(pairedClients) {
     97   if (!(pairedClients instanceof Array)) {
     98     console.error('pairedClients is not an Array:', pairedClients);
     99     return null;
    100   }
    101 
    102   var result = [];
    103   for (var i = 0; i < pairedClients.length; i++) {
    104     var pairedClient = new remoting.PairedClient(pairedClients[i]);
    105     if (!pairedClient.isValid()) {
    106       console.error('pairedClient[' + i + '] has incorrect format:',
    107                     /** @type {*} */(pairedClients[i]));
    108       return null;
    109     }
    110     result.push(pairedClient);
    111   }
    112   return result;
    113 }
    114 
    115 /**
    116  * @param {remoting.HostController} hostController
    117  * @param {HTMLElement} listContainer HTML <div> to contain the list of paired
    118  *     clients.
    119  * @param {HTMLElement} message HTML <div> containing the message notifying
    120  *     the user that clients are paired and containing the link to open the
    121  *     dialog.
    122  * @param {HTMLElement} deleteAllButton HTML <button> inititating the "delete
    123  *     all" action.
    124  * @param {HTMLElement} closeButton HTML <button> to close the dialog.
    125  * @param {HTMLElement} noPairedClients HTML <div> containing a message shown
    126  *     when all clients have been deleted.
    127  * @param {HTMLElement} workingSpinner HTML element containing a spinner
    128  *     graphic shown while a deletion is in progress.
    129  * @param {HTMLElement} errorDiv HTML <div> containing an error message shown
    130  *     if a delete operation fails.
    131  * @constructor
    132  */
    133 remoting.PairedClientManager = function(hostController, listContainer, message,
    134                                         deleteAllButton, closeButton,
    135                                         noPairedClients, workingSpinner,
    136                                         errorDiv) {
    137   /**
    138    * @private
    139    */
    140   this.hostController_ = hostController;
    141   /**
    142    * @private
    143    */
    144   this.message_ = message;
    145   /**
    146    * @private
    147    */
    148   this.deleteAllButton_ = deleteAllButton;
    149   /**
    150    * @private
    151    */
    152   this.closeButton_ = closeButton;
    153   /**
    154    * @private
    155    */
    156   this.noPairedClients_ = noPairedClients;
    157   /**
    158    * @private
    159    */
    160   this.workingSpinner_ = workingSpinner;
    161   /**
    162    * @private
    163    */
    164   this.errorDiv_ = errorDiv;
    165   /**
    166    * @type {Element}
    167    * @private
    168    */
    169   this.clientRows_ = listContainer.querySelector('tbody');
    170   /**
    171    * @type {Array.<remoting.PairedClient>}
    172    */
    173   this.pairedClients_ = [];
    174 
    175   this.deleteAllButton_.addEventListener('click',
    176                                          this.deleteAll_.bind(this),
    177                                          false);
    178 };
    179 
    180 /**
    181  * Populate the dialog with the list of paired clients and show or hide the
    182  * message as appropriate.
    183  *
    184  * @param {*} pairedClients The list of paired clients as returned by the
    185  *     native host component.
    186  * @return {void} Nothing.
    187  */
    188 remoting.PairedClientManager.prototype.setPairedClients =
    189     function(pairedClients) {
    190   // Reset table.
    191   while (this.clientRows_.lastChild) {
    192     this.clientRows_.removeChild(this.clientRows_.lastChild);
    193   }
    194 
    195   this.pairedClients_ =
    196     remoting.PairedClient.convertToPairedClientArray(pairedClients);
    197   for (var i = 0; i < this.pairedClients_.length; ++i) {
    198     var client = this.pairedClients_[i];
    199     client.createDom(this, this.clientRows_);
    200   }
    201 
    202   // Show or hide the "this computer has paired clients" message.
    203   this.setWorking_(false)
    204 };
    205 
    206 /**
    207  * Enter or leave "working" mode. This indicates to the user that a delete
    208  * operation is in progress. All dialog UI is disabled until it completes.
    209  *
    210  * @param {boolean} working True to enter "working" mode; false to leave it.
    211  * @private
    212  */
    213 remoting.PairedClientManager.prototype.setWorking_ = function(working) {
    214   var hasPairedClients = (this.pairedClients_.length != 0);
    215   for (var i = 0; i < this.pairedClients_.length; ++i) {
    216     this.pairedClients_[i].showButton(!working);
    217   }
    218   this.closeButton_.disabled = working;
    219   this.workingSpinner_.hidden = !working;
    220   this.errorDiv_.hidden = true;
    221   this.message_.hidden = !hasPairedClients;
    222   this.deleteAllButton_.disabled = working || !hasPairedClients;
    223   this.noPairedClients_.hidden = hasPairedClients;
    224 };
    225 
    226 /**
    227  * Error callback for delete operations.
    228  *
    229  * @param {remoting.Error} error The error message.
    230  * @private
    231  */
    232 remoting.PairedClientManager.prototype.onError_ = function(error) {
    233   this.setWorking_(false);
    234   l10n.localizeElementFromTag(this.errorDiv_, error);
    235   this.errorDiv_.hidden = false;
    236 };
    237 
    238 /**
    239  * Delete a single paired client.
    240  *
    241  * @param {remoting.PairedClient} client The pairing to delete.
    242  */
    243 remoting.PairedClientManager.prototype.deletePairedClient = function(client) {
    244   this.setWorking_(true);
    245   this.hostController_.deletePairedClient(client.clientId,
    246       this.setWorking_.bind(this, false),
    247       this.onError_.bind(this));
    248   this.clientRows_.removeChild(client.tableRow);
    249   for (var i = 0; i < this.pairedClients_.length; ++i) {
    250     if (this.pairedClients_[i] == client) {
    251       this.pairedClients_.splice(i, 1);
    252       break;
    253     }
    254   }
    255 };
    256 
    257 /**
    258  * Delete all paired clients.
    259  *
    260  * @private
    261  */
    262 remoting.PairedClientManager.prototype.deleteAll_ = function() {
    263   this.setWorking_(true);
    264   this.hostController_.clearPairedClients(
    265       this.setWorking_.bind(this, false),
    266       this.onError_.bind(this));
    267 
    268   while (this.clientRows_.lastChild) {
    269     this.clientRows_.removeChild(this.clientRows_.lastChild);
    270   }
    271   this.pairedClients_ = [];
    272 };
    273 
    274 /**
    275  * Get the id of the first paired client for testing.
    276  *
    277  * @private
    278  * @return {string} The client id of the first paired client in the list.
    279  */
    280 remoting.PairedClientManager.prototype.getFirstClientIdForTesting_ =
    281     function() {
    282   return this.pairedClients_.length > 0 ? this.pairedClients_[0].clientId : '';
    283 };
    284 
    285 
    286 /** @type {remoting.PairedClientManager} */
    287 remoting.pairedClientManager = null;
    288