Javascript  |  362行  |  9.57 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


// Network status constants.
const StatusConnected = 'connected';
const StatusDisconnected = 'disconnected';
const StatusConnecting = 'connecting';
const StatusError = 'error';

const NetworkOther = 'other';

// Setup css canvas 'spinner-circle'
(function() {
  var lineWidth = 3;
  var r = 8;
  var ctx = document.getCSSCanvasContext('2d', 'spinner-circle', 2 * r, 2 * r);

  ctx.lineWidth = lineWidth;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';

  ctx.strokeStyle = '#4e73c7';
  ctx.beginPath();
  ctx.moveTo(lineWidth / 2, r - lineWidth / 2);
  ctx.arc(r, r, r - lineWidth / 2, Math.PI, Math.PI * 3 / 2);
  ctx.stroke();
})();

/**
 * Sends "connect" using the 'action' WebUI message.
 */
function sendConnect(index, passphrase, identity, auto_connect) {
  chrome.send('action',
      ['connect',
       String(index),
       passphrase,
       identity,
       auto_connect ? '1' : '0']);
}

var networkMenuItemProto = (function() {
    var networkMenuItem = cr.doc.createElement('div');
    networkMenuItem.innerHTML = '<div class="network-menu-item">' +
          '<div class="network-label-icon">' +
            '<div class="network-label"></div>' +
            '<div class="network-icon hidden"></div>' +
          '</div>' +
          '<div class="network-status hidden"></div>' +
          '<div class="hidden"></div>' +
        '</div>';
    return networkMenuItem;
  })();

var NetworkMenuItem = cr.ui.define(function() {
    return networkMenuItemProto.cloneNode(true);
  });

NetworkMenuItem.prototype = {
  __proto__: MenuItem.prototype,

  ssidEdit: null,
  passwordEdit: null,
  autoConnectCheckbox: null,

  /**
   * The label element.
   * @private
   */
  get label_() {
    return this.firstElementChild.firstElementChild.firstElementChild;
  },

  /**
   * The icon element.
   * @private
   */
  get icon_() {
    return this.label_.nextElementSibling;
  },

  /**
   * The status area element.
   * @private
   */
  get status_() {
    return this.firstElementChild.firstElementChild.nextElementSibling;
  },

  /**
   * The action area container element.
   * @private
   */
  get action_() {
    return this.status_.nextElementSibling;
  },

  /**
   * Set status message.
   * @param {string} message The message to display in status area.
   * @private
   */
  setStatus_: function(message) {
    if (message) {
      this.status_.textContent = message;
      this.status_.classList.remove('hidden');
    } else {
      this.status_.classList.add('hidden');
    }
  },

  /**
   * Set status icon.
   * @param {string} icon Source url for the icon image.
   * @private
   */
  setIcon_: function(icon) {
    if (icon) {
      this.icon_.style.backgroundImage = 'url(' + icon + ')';
      this.icon_.classList.remove('hidden');
    } else {
      this.icon_.classList.add('hidden');
    }
  },

  /**
   * Handle reconnect.
   * @private
   */
  handleConnect_ : function(e) {
    var index = this.menu_.getMenuItemIndexOf(this);
    if (this.ssidEdit && this.passwordEdit) {
      if (this.ssidEdit.value) {
        sendConnect(index,
            this.passwordEdit.value,
            this.ssidEdit.value,
            this.autoConnectCheckbox.checked);
      }
    } else if (this.passwordEdit) {
      if (this.passwordEdit.value) {
        sendConnect(index,
            this.passwordEdit.value, '', this.autoConnectCheckbox.checked);
      }
    } else {
      if (this.attrs.remembered) {
        sendConnect(index, this.attrs.passphrase, '', this.attrs.auto_connect);
      } else {
        sendConnect(index, '', '', this.autoConnectCheckbox.checked);
      }
    }
  },

  /**
   * Handle keydown event in ssid edit.
   * @private
   */
  handleSsidEditKeydown_: function(e) {
    if (e.target == this.ssidEdit &&
        e.keyIdentifier == 'Enter') {
      this.passwordEdit.focus();
    }
  },

  /**
   * Handle keydown event in password edit.
   * @private
   */
  handlePassEditKeydown_: function(e) {
    if (e.target == this.passwordEdit &&
        e.keyIdentifier == 'Enter') {
      this.handleConnect_();
    }
  },

  /**
   * Returns whether action area is visible.
   * @private
   */
  isActionVisible_: function() {
    return !this.action_.classList.contains('hidden');
  },

  /**
   * Show/hide action area.
   * @private
   */
  showAction_: function(show) {
    var visible = this.isActionVisible_();
    if (show && !visible) {
      this.action_.classList.remove('hidden');
    } else if (!show && visible) {
      this.action_.classList.add('hidden');
    }
  },

  /**
   * Add network name edit to action area.
   * @private
   */
  addSsidEdit_: function() {
    this.ssidEdit = this.ownerDocument.createElement('input');
    this.ssidEdit.type = 'text';
    this.ssidEdit.placeholder = localStrings.getString('ssid_prompt');
    this.ssidEdit.pattern = '^\\S+$';
    this.ssidEdit.addEventListener('keydown',
        this.handleSsidEditKeydown_.bind(this));

    var box = this.ownerDocument.createElement('div');
    box.appendChild(this.ssidEdit);
    this.action_.appendChild(box);
  },

  /**
   * Add password edit to action area.
   * @private
   */
  addPasswordEdit_: function() {
    this.passwordEdit = this.ownerDocument.createElement('input');
    this.passwordEdit.type = 'password';
    this.passwordEdit.placeholder = localStrings.getString('pass_prompt');
    this.passwordEdit.pattern = '^\\S+$';
    this.passwordEdit.addEventListener('keydown',
        this.handlePassEditKeydown_.bind(this));

    var box = this.ownerDocument.createElement('div');
    box.appendChild(this.passwordEdit);
    this.action_.appendChild(box);
  },

  /**
   * Add auto-connect this network check box to action area.
   * @private
   */
  addAutoConnectCheckbox_: function() {
    this.autoConnectCheckbox = this.ownerDocument.createElement('input');
    this.autoConnectCheckbox.type = 'checkbox';
    this.autoConnectCheckbox.checked = this.attrs.auto_connect;

    var autoConnectSpan = this.ownerDocument.createElement('span');
    autoConnectSpan.textContent =
        localStrings.getString('auto_connect_this_network');

    var autoConnectLabel = this.ownerDocument.createElement('label');
    autoConnectLabel.appendChild(this.autoConnectCheckbox);
    autoConnectLabel.appendChild(autoConnectSpan);

    this.action_.appendChild(autoConnectLabel);
  },

  /**
   * Internal method to initiailze the MenuItem.
   * @private
   */
  initMenuItem_: function() {
    // *TODO: eliminate code duplication with menu.js
    // MenuItem.prototype.initMenuItem_();
    var attrs = this.attrs;
    this.classList.add(attrs.type);
    this.menu_.addHandlers(this, this);

    //////// NetworkMenuItem specific code:
    // TODO: Handle specific types of network, connecting icon.
    this.label_.textContent = attrs.label;

    if (attrs.network_type == NetworkOther) {
      this.addSsidEdit_();
      this.addPasswordEdit_();
      this.addAutoConnectCheckbox_();
    } else if (attrs.status && attrs.status != 'unknown') {
      if (attrs.status == StatusConnected) {
        this.setStatus_(attrs.ip_address);
      } else if (attrs.status == StatusConnecting) {
        this.setStatus_(attrs.message);

        this.icon_.classList.add('spinner');
        this.icon_.classList.remove('hidden');
      } else if (attrs.status == StatusError) {
        this.setStatus_(attrs.message);
        this.setIcon_('chrome://theme/IDR_WARNING');

        var button = this.ownerDocument.createElement('button');
        button.textContent = localStrings.getString('reconnect');
        button.addEventListener('click', this.handleConnect_.bind(this));
        var box = this.ownerDocument.createElement('div');
        box.appendChild(button);
        this.action_.appendChild(box);

        this.showAction_(true);
      }

      if (attrs.need_passphrase) {
        this.addPasswordEdit_();
      }

      this.addAutoConnectCheckbox_();
    }
    //////// End NetworkMenuItem specifi code

    if (attrs.font) {
      this.label_.style.font = attrs.font;

      var base_font = attrs.font.replace(/bold/, '').replace(/italic/, '');
      this.status_.style.font = base_font;
      this.action_.style.font = base_font;
    }
  },

  /** @inheritDoc */
  activate: function() {
    // Close action area and connect if it is visible.
    if (this.isActionVisible_()) {
      this.showAction_(false);
      this.handleConnect_();
      return;
    }

    // Show action area for encrypted network and 'other' network.
    if ((this.attrs.network_type == NetworkOther ||
         this.attrs.status == StatusDisconnected) &&
        this.attrs.need_passphrase &&
        !this.isActionVisible_()) {
      this.showAction_(true);
      return;
    }

    MenuItem.prototype.activate.call(this);
  }
};


var NetworkMenu = cr.ui.define('div');

NetworkMenu.prototype = {
  __proto__: Menu.prototype,

  /** @inheritDoc */
  createMenuItem: function(attrs) {
    if (attrs.type == 'command') {
      return new NetworkMenuItem();
    } else {
      return new MenuItem();
    }
  },

  /** @inheritDoc */
  onClick_: function(event, item) {
    // If item is a NetworkMenuItem, it must have at least one of the following.
    if (item.autoConnectCheckbox || item.ssidEdit || item.passwordEdit) {
      // Ignore clicks other than on the NetworkMenuItem itself.
      if (event.target == item.autoConnectCheckbox ||
          event.target == item.autoConnectCheckbox.nextElementSibling ||
          event.target == item.ssidEdit ||
          event.target == item.passwordEdit) {
        return;
      }
    }

    Menu.prototype.onClick_.call(this, event, item);
  },
};