1// Copyright (c) 2011 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
5cr.define('options.internet', function() {
6
7  /**
8   * Network settings constants. These enums usually match their C++
9   * counterparts.
10   */
11  function Constants() {}
12  // Minimum length for wireless network password.
13  Constants.MIN_WIRELESS_PASSWORD_LENGTH = 5;
14  // Minimum length for SSID name.
15  Constants.MIN_WIRELESS_SSID_LENGTH = 1;
16  // Cellular activation states:
17  Constants.ACTIVATION_STATE_UNKNOWN             = 0;
18  Constants.ACTIVATION_STATE_ACTIVATED           = 1;
19  Constants.ACTIVATION_STATE_ACTIVATING          = 2;
20  Constants.ACTIVATION_STATE_NOT_ACTIVATED       = 3;
21  Constants.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4;
22  // Network types:
23  Constants.TYPE_UNKNOWN   = 0;
24  Constants.TYPE_ETHERNET  = 1;
25  Constants.TYPE_WIFI      = 2;
26  Constants.TYPE_WIMAX     = 3;
27  Constants.TYPE_BLUETOOTH = 4;
28  Constants.TYPE_CELLULAR  = 5;
29
30  /**
31   * Creates a new network list div.
32   * @param {Object=} opt_propertyBag Optional properties.
33   * @constructor
34   * @extends {HTMLDivElement}
35   */
36  var NetworkElement = cr.ui.define('div');
37
38  NetworkElement.prototype = {
39    __proto__: HTMLDivElement.prototype,
40
41    /** @inheritDoc */
42    decorate: function() {
43      this.addEventListener('click', this.handleClick_);
44    },
45
46    /**
47     * Loads given network list.
48     * @param {Array} networks An array of network object.
49     */
50    load: function(networks) {
51      this.textContent = '';
52
53      for (var i = 0; i < networks.length; ++i) {
54        this.appendChild(new NetworkItem(networks[i]));
55      }
56    },
57
58    /**
59     * Handles click on network list and triggers actions when clicked on
60     * a NetworkListItem button.
61     * @private
62     * @param {!Event} e The click event object.
63     */
64    handleClick_: function(e) {
65      // We shouldn't respond to click events selecting an input,
66      // so return on those.
67      if (e.target.tagName == 'INPUT') {
68        return;
69      }
70      // Handle left button click
71      if (e.button == 0) {
72        var el = e.target;
73        // If click is on action buttons of a network item.
74        if (!(el.buttonType && el.networkType && el.servicePath)) {
75          if (el.buttonType) {
76            return;
77          }
78          // If click is on a network item or its label, walk up the DOM tree
79          // to find the network item.
80          var item = el;
81          while (item && !item.data) {
82            item = item.parentNode;
83          }
84          if (item.connecting)
85            return;
86
87          if (item) {
88            var data = item.data;
89            // Don't try to connect to Ethernet or unactivated Cellular.
90            if (data && (data.networkType == 1 ||
91                        (data.networkType == 5 && data.activation_state != 1)))
92              return;
93            for (var i = 0; i < this.childNodes.length; i++) {
94              if (this.childNodes[i] != item)
95                this.childNodes[i].hidePassword();
96            }
97            InternetOptions.unlockUpdates();
98            // If clicked on other networks item.
99            if (data && data.servicePath == '?') {
100              if (InternetOptions.useSettingsUI &&
101                  data.type != options.internet.Constants.TYPE_CELLULAR) {
102                item.showOtherLogin();
103              } else {
104                chrome.send('buttonClickCallback',
105                            [String(data.networkType),
106                             data.servicePath,
107                             'connect']);
108              }
109            }
110          }
111        }
112      }
113    }
114  };
115
116  /**
117   * Creates a new network item.
118   * @param {Object} network The network this represents.
119   * @constructor
120   * @extends {HTMLDivElement}
121   */
122  function NetworkItem(network) {
123    var el = cr.doc.createElement('div');
124    el.data = {
125      servicePath: network[0],
126      networkName: network[1],
127      networkStatus: network[2],
128      networkType: network[3],
129      connected: network[4],
130      connecting: network[5],
131      iconURL: network[6],
132      remembered: network[7],
133      activation_state: network[8],
134      needs_new_plan: network[9],
135      connectable: network[10]
136    };
137    NetworkItem.decorate(el);
138    return el;
139  }
140
141
142  /**
143   * Decorates an element as a network item.
144   * @param {!HTMLElement} el The element to decorate.
145   */
146  NetworkItem.decorate = function(el) {
147    el.__proto__ = NetworkItem.prototype;
148    el.decorate();
149  };
150
151  NetworkItem.prototype = {
152    __proto__: HTMLDivElement.prototype,
153
154    /** @inheritDoc */
155    decorate: function() {
156      this.className = 'network-item';
157      this.connected = this.data.connected;
158      this.connectable = this.data.connectable;
159      this.other = this.data.servicePath == '?';
160      this.id = this.data.servicePath;
161      // textDiv holds icon, name and status text.
162      var textDiv = this.ownerDocument.createElement('div');
163      textDiv.className = 'network-item-text';
164      if (this.data.iconURL) {
165        textDiv.style.backgroundImage = url(this.data.iconURL);
166      }
167
168      var nameEl = this.ownerDocument.createElement('div');
169      nameEl.className = 'network-name-label';
170      nameEl.textContent = this.data.networkName;
171      textDiv.appendChild(nameEl);
172
173      if (this.other) {
174        // No status and buttons for "Other..."
175        this.appendChild(textDiv);
176        return;
177      }
178
179      // Only show status text for networks other than "remembered".
180      if (!this.data.remembered) {
181        var statusEl = this.ownerDocument.createElement('div');
182        statusEl.className = 'network-status-label';
183        statusEl.textContent = this.data.networkStatus;
184        textDiv.appendChild(statusEl);
185      }
186
187      this.appendChild(textDiv);
188
189      var spacerDiv = this.ownerDocument.createElement('div');
190      spacerDiv.className = 'network-item-box-spacer';
191      this.appendChild(spacerDiv);
192
193      var buttonsDiv = this.ownerDocument.createElement('div');
194      var self = this;
195      if (!this.data.remembered) {
196        var no_plan =
197            this.data.networkType == Constants.TYPE_CELLULAR &&
198            this.data.needs_new_plan;
199        var show_activate =
200          (this.data.networkType == Constants.TYPE_CELLULAR &&
201           this.data.activation_state !=
202               Constants.ACTIVATION_STATE_ACTIVATED &&
203           this.data.activation_state !=
204               Constants.ACTIVATION_STATE_ACTIVATING);
205
206        // Show [Activate] button for non-activated Cellular network.
207        if (show_activate || no_plan) {
208          var button_name = no_plan ? 'buyplan_button' : 'activate_button';
209          buttonsDiv.appendChild(
210              this.createButton_(button_name, 'activate',
211                                 function(e) {
212                chrome.send('buttonClickCallback',
213                            [String(self.data.networkType),
214                             self.data.servicePath,
215                             'activate']);
216              }));
217        }
218        // Show disconnect button if not ethernet.
219        if (this.data.networkType != Constants.TYPE_ETHERNET &&
220            this.data.connected) {
221          buttonsDiv.appendChild(
222              this.createButton_('disconnect_button', 'disconnect',
223                                  function(e) {
224                 chrome.send('buttonClickCallback',
225                             [String(self.data.networkType),
226                              self.data.servicePath,
227                              'disconnect']);
228               }));
229        }
230        if (!this.data.connected && !this.data.connecting) {
231          // connect button (if not ethernet and not showing activate button)
232          if (this.data.networkType != Constants.TYPE_ETHERNET &&
233              !show_activate && !no_plan) {
234            buttonsDiv.appendChild(
235                this.createButton_('connect_button', 'connect',
236                                   function(e) {
237                  chrome.send('buttonClickCallback',
238                              [String(self.data.networkType),
239                               self.data.servicePath,
240                               'connect']);
241                }));
242          }
243        }
244        if (this.data.connected ||
245            this.data.networkType == Constants.TYPE_CELLULAR) {
246          buttonsDiv.appendChild(
247              this.createButton_('options_button', 'options',
248                                 function(e) {
249                chrome.send('buttonClickCallback',
250                            [String(self.data.networkType),
251                             self.data.servicePath,
252                             'options']);
253              }));
254        }
255      } else {
256        // Put "Forget this network" button.
257        var button = this.createButton_('forget_button', 'forget',
258                                        function(e) {
259                       chrome.send('buttonClickCallback',
260                                   [String(self.data.networkType),
261                                   self.data.servicePath,
262                                   'forget']);
263                     });
264        if (!AccountsOptions.currentUserIsOwner()) {
265          // Disable this for guest non-Owners.
266          button.disabled = true;
267        }
268
269        buttonsDiv.appendChild(button);
270      }
271      this.appendChild(buttonsDiv);
272    },
273
274    showPassword: function() {
275      if (this.connecting)
276        return;
277
278      InternetOptions.lockUpdates();
279
280      var passwordDiv = this.ownerDocument.createElement('div');
281      passwordDiv.className = 'network-password';
282      var passInput = this.ownerDocument.createElement('input');
283      passwordDiv.appendChild(passInput);
284      passInput.placeholder = localStrings.getString('inetPassPrompt');
285      passInput.type = 'password';
286      var buttonEl = this.ownerDocument.createElement('button');
287      buttonEl.textContent = localStrings.getString('inetLogin');
288      buttonEl.addEventListener('click', this.handleLogin_);
289      buttonEl.servicePath = this.data.servicePath;
290      buttonEl.style.right = '0';
291      buttonEl.style.position = 'absolute';
292      buttonEl.style.visibility = 'visible';
293      buttonEl.disabled = true;
294
295      var togglePassLabel = this.ownerDocument.createElement('label');
296      togglePassLabel.style.display = 'inline';
297      var togglePassSpan = this.ownerDocument.createElement('span');
298      var togglePassCheckbox = this.ownerDocument.createElement('input');
299      togglePassCheckbox.type = 'checkbox';
300      togglePassCheckbox.checked = false;
301      togglePassCheckbox.target = passInput;
302      togglePassCheckbox.addEventListener('change', this.handleShowPass_);
303      togglePassSpan.textContent = localStrings.getString('inetShowPass');
304      togglePassLabel.appendChild(togglePassCheckbox);
305      togglePassLabel.appendChild(togglePassSpan);
306      passwordDiv.appendChild(togglePassLabel);
307
308      // Disable login button if there is no password.
309      passInput.addEventListener('keyup', function(e) {
310        buttonEl.disabled =
311          passInput.value.length < Constants.MIN_WIRELESS_PASSWORD_LENGTH;
312      });
313
314      passwordDiv.appendChild(buttonEl);
315      this.connecting = true;
316      this.appendChild(passwordDiv);
317    },
318
319    handleShowPass_: function(e) {
320      var target = e.target;
321      if (target.checked) {
322        target.target.type = 'text';
323      } else {
324        target.target.type = 'password';
325      }
326    },
327
328    hidePassword: function() {
329      this.connecting = false;
330      var children = this.childNodes;
331      // Remove all password divs starting from the end.
332      for (var i = children.length-1; i >= 0; i--) {
333        if (children[i].className == 'network-password') {
334          this.removeChild(children[i]);
335        }
336      }
337    },
338
339    showOtherLogin: function() {
340      if (this.connecting)
341        return;
342
343      InternetOptions.lockUpdates();
344
345      var ssidDiv = this.ownerDocument.createElement('div');
346      ssidDiv.className = 'network-password';
347      var ssidInput = this.ownerDocument.createElement('input');
348      ssidInput.placeholder = localStrings.getString('inetSsidPrompt');
349      ssidDiv.appendChild(ssidInput);
350
351      var securityDiv = this.ownerDocument.createElement('div');
352      securityDiv.className = 'network-password';
353      var securityInput = this.ownerDocument.createElement('select');
354      var securityNoneOption = this.ownerDocument.createElement('option');
355      securityNoneOption.value = 'none';
356      securityNoneOption.label = localStrings.getString('inetSecurityNone');
357      securityInput.appendChild(securityNoneOption);
358      var securityWEPOption = this.ownerDocument.createElement('option');
359      securityWEPOption.value = 'wep';
360      securityWEPOption.label = localStrings.getString('inetSecurityWEP');
361      securityInput.appendChild(securityWEPOption);
362      var securityWPAOption = this.ownerDocument.createElement('option');
363      securityWPAOption.value = 'wpa';
364      securityWPAOption.label = localStrings.getString('inetSecurityWPA');
365      securityInput.appendChild(securityWPAOption);
366      var securityRSNOption = this.ownerDocument.createElement('option');
367      securityRSNOption.value = 'rsn';
368      securityRSNOption.label = localStrings.getString('inetSecurityRSN');
369      securityInput.appendChild(securityRSNOption);
370      securityDiv.appendChild(securityInput);
371
372      var passwordDiv = this.ownerDocument.createElement('div');
373      passwordDiv.className = 'network-password';
374      var passInput = this.ownerDocument.createElement('input');
375      passInput.placeholder = localStrings.getString('inetPassPrompt');
376      passInput.type = 'password';
377      passInput.disabled = true;
378      passwordDiv.appendChild(passInput);
379
380      var togglePassLabel = this.ownerDocument.createElement('label');
381      togglePassLabel.style.display = 'inline';
382      var togglePassSpan = this.ownerDocument.createElement('span');
383      var togglePassCheckbox = this.ownerDocument.createElement('input');
384      togglePassCheckbox.type = 'checkbox';
385      togglePassCheckbox.checked = false;
386      togglePassCheckbox.target = passInput;
387      togglePassCheckbox.addEventListener('change', this.handleShowPass_);
388      togglePassSpan.textContent = localStrings.getString('inetShowPass');
389      togglePassLabel.appendChild(togglePassCheckbox);
390      togglePassLabel.appendChild(togglePassSpan);
391      passwordDiv.appendChild(togglePassLabel);
392
393      var buttonEl =
394        this.createButton_('inetLogin', true, this.handleOtherLogin_);
395      buttonEl.style.right = '0';
396      buttonEl.style.position = 'absolute';
397      buttonEl.style.visibility = 'visible';
398      buttonEl.disabled = true;
399      passwordDiv.appendChild(buttonEl);
400
401      this.appendChild(ssidDiv);
402      this.appendChild(securityDiv);
403      this.appendChild(passwordDiv);
404
405      securityInput.addEventListener('change', function(e) {
406        // If changed to None, then disable passInput and clear it out.
407        // Otherwise enable it.
408        if (securityInput.value == 'none') {
409          passInput.disabled = true;
410          passInput.value = '';
411        } else {
412          passInput.disabled = false;
413        }
414      });
415
416      var keyup_listener = function(e) {
417        // Disable login button if ssid is not long enough or
418        // password is not long enough (unless no security)
419        var ssid_good =
420          ssidInput.value.length >= Constants.MIN_WIRELESS_SSID_LENGTH;
421        var pass_good =
422          securityInput.value == 'none' ||
423            passInput.value.length >= Constants.MIN_WIRELESS_PASSWORD_LENGTH;
424        buttonEl.disabled = !ssid_good || !pass_good;
425      };
426      ssidInput.addEventListener('keyup', keyup_listener);
427      securityInput.addEventListener('change', keyup_listener);
428      passInput.addEventListener('keyup', keyup_listener);
429      this.connecting = true;
430    },
431
432    handleLogin_: function(e) {
433      // The user has clicked on the Login button. It's now safe to
434      // unclock UI updates.
435      InternetOptions.unlockUpdates();
436      var el = e.target;
437      var parent = el.parentNode;
438      el.disabled = true;
439      var input = parent.firstChild;
440      input.disabled = true;
441      chrome.send('loginToNetwork', [el.servicePath, input.value]);
442    },
443
444    handleOtherLogin_: function(e) {
445      // See comments in handleLogin_().
446      InternetOptions.unlockUpdates();
447      var el = e.target;
448      var parent = el.parentNode.parentNode;
449      el.disabled = true;
450      var ssid = parent.childNodes[1].firstChild;
451      var sec = parent.childNodes[2].firstChild;
452      var pass = parent.childNodes[3].firstChild;
453      sec.disabled = true;
454      ssid.disabled = true;
455      pass.disabled = true;
456      chrome.send('loginToOtherNetwork', [sec.value, ssid.value, pass.value]);
457    },
458
459    /**
460     * Creates a button for interacting with a network.
461     * @param {Object} name The name of the localStrings to use for the text.
462     * @param {Object} type The type of button.
463     */
464    createButton_: function(name, type, callback) {
465      var buttonEl = this.ownerDocument.createElement('button');
466      buttonEl.buttonType = type;
467      buttonEl.textContent = localStrings.getString(name);
468      buttonEl.addEventListener('click', callback);
469      return buttonEl;
470    }
471  };
472
473  /**
474   * Whether the underlying network is connected. Only used for display purpose.
475   * @type {boolean}
476   */
477  cr.defineProperty(NetworkItem, 'connected', cr.PropertyKind.BOOL_ATTR);
478
479  /**
480   * Whether the underlying network is currently connecting.
481   * Only used for display purpose.
482   * @type {boolean}
483   */
484  cr.defineProperty(NetworkItem, 'connecting', cr.PropertyKind.BOOL_ATTR);
485
486  /**
487   * Whether the underlying network is an other network for adding networks.
488   * Only used for display purpose.
489   * @type {boolean}
490   */
491  cr.defineProperty(NetworkItem, 'other', cr.PropertyKind.BOOL_ATTR);
492
493  /**
494   * Whether the underlying network is connectable.
495   * @type {boolean}
496   */
497  cr.defineProperty(NetworkItem, 'connectable', cr.PropertyKind.BOOL_ATTR);
498
499  return {
500    Constants: Constants,
501    NetworkElement: NetworkElement
502  };
503});
504