Home | History | Annotate | Download | only in chromeos
      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 
      5 
      6 // Network status constants.
      7 const StatusConnected = 'connected';
      8 const StatusDisconnected = 'disconnected';
      9 const StatusConnecting = 'connecting';
     10 const StatusError = 'error';
     11 
     12 const NetworkOther = 'other';
     13 
     14 // Setup css canvas 'spinner-circle'
     15 (function() {
     16   var lineWidth = 3;
     17   var r = 8;
     18   var ctx = document.getCSSCanvasContext('2d', 'spinner-circle', 2 * r, 2 * r);
     19 
     20   ctx.lineWidth = lineWidth;
     21   ctx.lineCap = 'round';
     22   ctx.lineJoin = 'round';
     23 
     24   ctx.strokeStyle = '#4e73c7';
     25   ctx.beginPath();
     26   ctx.moveTo(lineWidth / 2, r - lineWidth / 2);
     27   ctx.arc(r, r, r - lineWidth / 2, Math.PI, Math.PI * 3 / 2);
     28   ctx.stroke();
     29 })();
     30 
     31 /**
     32  * Sends "connect" using the 'action' WebUI message.
     33  */
     34 function sendConnect(index, passphrase, identity, auto_connect) {
     35   chrome.send('action',
     36       ['connect',
     37        String(index),
     38        passphrase,
     39        identity,
     40        auto_connect ? '1' : '0']);
     41 }
     42 
     43 var networkMenuItemProto = (function() {
     44     var networkMenuItem = cr.doc.createElement('div');
     45     networkMenuItem.innerHTML = '<div class="network-menu-item">' +
     46           '<div class="network-label-icon">' +
     47             '<div class="network-label"></div>' +
     48             '<div class="network-icon hidden"></div>' +
     49           '</div>' +
     50           '<div class="network-status hidden"></div>' +
     51           '<div class="hidden"></div>' +
     52         '</div>';
     53     return networkMenuItem;
     54   })();
     55 
     56 var NetworkMenuItem = cr.ui.define(function() {
     57     return networkMenuItemProto.cloneNode(true);
     58   });
     59 
     60 NetworkMenuItem.prototype = {
     61   __proto__: MenuItem.prototype,
     62 
     63   ssidEdit: null,
     64   passwordEdit: null,
     65   autoConnectCheckbox: null,
     66 
     67   /**
     68    * The label element.
     69    * @private
     70    */
     71   get label_() {
     72     return this.firstElementChild.firstElementChild.firstElementChild;
     73   },
     74 
     75   /**
     76    * The icon element.
     77    * @private
     78    */
     79   get icon_() {
     80     return this.label_.nextElementSibling;
     81   },
     82 
     83   /**
     84    * The status area element.
     85    * @private
     86    */
     87   get status_() {
     88     return this.firstElementChild.firstElementChild.nextElementSibling;
     89   },
     90 
     91   /**
     92    * The action area container element.
     93    * @private
     94    */
     95   get action_() {
     96     return this.status_.nextElementSibling;
     97   },
     98 
     99   /**
    100    * Set status message.
    101    * @param {string} message The message to display in status area.
    102    * @private
    103    */
    104   setStatus_: function(message) {
    105     if (message) {
    106       this.status_.textContent = message;
    107       this.status_.classList.remove('hidden');
    108     } else {
    109       this.status_.classList.add('hidden');
    110     }
    111   },
    112 
    113   /**
    114    * Set status icon.
    115    * @param {string} icon Source url for the icon image.
    116    * @private
    117    */
    118   setIcon_: function(icon) {
    119     if (icon) {
    120       this.icon_.style.backgroundImage = 'url(' + icon + ')';
    121       this.icon_.classList.remove('hidden');
    122     } else {
    123       this.icon_.classList.add('hidden');
    124     }
    125   },
    126 
    127   /**
    128    * Handle reconnect.
    129    * @private
    130    */
    131   handleConnect_ : function(e) {
    132     var index = this.menu_.getMenuItemIndexOf(this);
    133     if (this.ssidEdit && this.passwordEdit) {
    134       if (this.ssidEdit.value) {
    135         sendConnect(index,
    136             this.passwordEdit.value,
    137             this.ssidEdit.value,
    138             this.autoConnectCheckbox.checked);
    139       }
    140     } else if (this.passwordEdit) {
    141       if (this.passwordEdit.value) {
    142         sendConnect(index,
    143             this.passwordEdit.value, '', this.autoConnectCheckbox.checked);
    144       }
    145     } else {
    146       if (this.attrs.remembered) {
    147         sendConnect(index, this.attrs.passphrase, '', this.attrs.auto_connect);
    148       } else {
    149         sendConnect(index, '', '', this.autoConnectCheckbox.checked);
    150       }
    151     }
    152   },
    153 
    154   /**
    155    * Handle keydown event in ssid edit.
    156    * @private
    157    */
    158   handleSsidEditKeydown_: function(e) {
    159     if (e.target == this.ssidEdit &&
    160         e.keyIdentifier == 'Enter') {
    161       this.passwordEdit.focus();
    162     }
    163   },
    164 
    165   /**
    166    * Handle keydown event in password edit.
    167    * @private
    168    */
    169   handlePassEditKeydown_: function(e) {
    170     if (e.target == this.passwordEdit &&
    171         e.keyIdentifier == 'Enter') {
    172       this.handleConnect_();
    173     }
    174   },
    175 
    176   /**
    177    * Returns whether action area is visible.
    178    * @private
    179    */
    180   isActionVisible_: function() {
    181     return !this.action_.classList.contains('hidden');
    182   },
    183 
    184   /**
    185    * Show/hide action area.
    186    * @private
    187    */
    188   showAction_: function(show) {
    189     var visible = this.isActionVisible_();
    190     if (show && !visible) {
    191       this.action_.classList.remove('hidden');
    192     } else if (!show && visible) {
    193       this.action_.classList.add('hidden');
    194     }
    195   },
    196 
    197   /**
    198    * Add network name edit to action area.
    199    * @private
    200    */
    201   addSsidEdit_: function() {
    202     this.ssidEdit = this.ownerDocument.createElement('input');
    203     this.ssidEdit.type = 'text';
    204     this.ssidEdit.placeholder = localStrings.getString('ssid_prompt');
    205     this.ssidEdit.pattern = '^\\S+$';
    206     this.ssidEdit.addEventListener('keydown',
    207         this.handleSsidEditKeydown_.bind(this));
    208 
    209     var box = this.ownerDocument.createElement('div');
    210     box.appendChild(this.ssidEdit);
    211     this.action_.appendChild(box);
    212   },
    213 
    214   /**
    215    * Add password edit to action area.
    216    * @private
    217    */
    218   addPasswordEdit_: function() {
    219     this.passwordEdit = this.ownerDocument.createElement('input');
    220     this.passwordEdit.type = 'password';
    221     this.passwordEdit.placeholder = localStrings.getString('pass_prompt');
    222     this.passwordEdit.pattern = '^\\S+$';
    223     this.passwordEdit.addEventListener('keydown',
    224         this.handlePassEditKeydown_.bind(this));
    225 
    226     var box = this.ownerDocument.createElement('div');
    227     box.appendChild(this.passwordEdit);
    228     this.action_.appendChild(box);
    229   },
    230 
    231   /**
    232    * Add auto-connect this network check box to action area.
    233    * @private
    234    */
    235   addAutoConnectCheckbox_: function() {
    236     this.autoConnectCheckbox = this.ownerDocument.createElement('input');
    237     this.autoConnectCheckbox.type = 'checkbox';
    238     this.autoConnectCheckbox.checked = this.attrs.auto_connect;
    239 
    240     var autoConnectSpan = this.ownerDocument.createElement('span');
    241     autoConnectSpan.textContent =
    242         localStrings.getString('auto_connect_this_network');
    243 
    244     var autoConnectLabel = this.ownerDocument.createElement('label');
    245     autoConnectLabel.appendChild(this.autoConnectCheckbox);
    246     autoConnectLabel.appendChild(autoConnectSpan);
    247 
    248     this.action_.appendChild(autoConnectLabel);
    249   },
    250 
    251   /**
    252    * Internal method to initiailze the MenuItem.
    253    * @private
    254    */
    255   initMenuItem_: function() {
    256     // *TODO: eliminate code duplication with menu.js
    257     // MenuItem.prototype.initMenuItem_();
    258     var attrs = this.attrs;
    259     this.classList.add(attrs.type);
    260     this.menu_.addHandlers(this, this);
    261 
    262     //////// NetworkMenuItem specific code:
    263     // TODO: Handle specific types of network, connecting icon.
    264     this.label_.textContent = attrs.label;
    265 
    266     if (attrs.network_type == NetworkOther) {
    267       this.addSsidEdit_();
    268       this.addPasswordEdit_();
    269       this.addAutoConnectCheckbox_();
    270     } else if (attrs.status && attrs.status != 'unknown') {
    271       if (attrs.status == StatusConnected) {
    272         this.setStatus_(attrs.ip_address);
    273       } else if (attrs.status == StatusConnecting) {
    274         this.setStatus_(attrs.message);
    275 
    276         this.icon_.classList.add('spinner');
    277         this.icon_.classList.remove('hidden');
    278       } else if (attrs.status == StatusError) {
    279         this.setStatus_(attrs.message);
    280         this.setIcon_('chrome://theme/IDR_WARNING');
    281 
    282         var button = this.ownerDocument.createElement('button');
    283         button.textContent = localStrings.getString('reconnect');
    284         button.addEventListener('click', this.handleConnect_.bind(this));
    285         var box = this.ownerDocument.createElement('div');
    286         box.appendChild(button);
    287         this.action_.appendChild(box);
    288 
    289         this.showAction_(true);
    290       }
    291 
    292       if (attrs.need_passphrase) {
    293         this.addPasswordEdit_();
    294       }
    295 
    296       this.addAutoConnectCheckbox_();
    297     }
    298     //////// End NetworkMenuItem specifi code
    299 
    300     if (attrs.font) {
    301       this.label_.style.font = attrs.font;
    302 
    303       var baseFont = attrs.font.replace(/bold/, '').replace(/italic/, '');
    304       this.status_.style.font = baseFont;
    305       this.action_.style.font = baseFont;
    306     }
    307   },
    308 
    309   /** @override */
    310   activate: function() {
    311     // Close action area and connect if it is visible.
    312     if (this.isActionVisible_()) {
    313       this.showAction_(false);
    314       this.handleConnect_();
    315       return;
    316     }
    317 
    318     // Show action area for encrypted network and 'other' network.
    319     if ((this.attrs.network_type == NetworkOther ||
    320          this.attrs.status == StatusDisconnected) &&
    321         this.attrs.need_passphrase &&
    322         !this.isActionVisible_()) {
    323       this.showAction_(true);
    324       return;
    325     }
    326 
    327     MenuItem.prototype.activate.call(this);
    328   }
    329 };
    330 
    331 
    332 var NetworkMenu = cr.ui.define('div');
    333 
    334 NetworkMenu.prototype = {
    335   __proto__: Menu.prototype,
    336 
    337   /** @override */
    338   createMenuItem: function(attrs) {
    339     if (attrs.type == 'command') {
    340       return new NetworkMenuItem();
    341     } else {
    342       return new MenuItem();
    343     }
    344   },
    345 
    346   /** @override */
    347   onClick_: function(event, item) {
    348     // If item is a NetworkMenuItem, it must have at least one of the following.
    349     if (item.autoConnectCheckbox || item.ssidEdit || item.passwordEdit) {
    350       // Ignore clicks other than on the NetworkMenuItem itself.
    351       if (event.target == item.autoConnectCheckbox ||
    352           event.target == item.autoConnectCheckbox.nextElementSibling ||
    353           event.target == item.ssidEdit ||
    354           event.target == item.passwordEdit) {
    355         return;
    356       }
    357     }
    358 
    359     Menu.prototype.onClick_.call(this, event, item);
    360   },
    361 };
    362