Source: js/sim_lock_system_dialog.js

/* global SystemDialog, SIMSlotManager, applications */
'use strict';

(function(exports) {
  /**
   * @class SimLockSystemDialog
   * @param {options} object for attributes `onShow`, `onHide` callback.
   * @extends SystemDialog
   */
  var SimLockSystemDialog = function(controller) {
    this.instanceID = 'simlock-dialog';
    this.controller = controller;
    this.options = {};
    /**
     * render the dialog
     */
    this.render();
    this._dispatchEvent('created');
  };

  var ORIENTATION = 'portrait-primary';

  SimLockSystemDialog.prototype = Object.create(SystemDialog.prototype,
    {
      visible: {
        configurable: false,
        get: function() {
          return this._visible;
        }
      },
    });

  SimLockSystemDialog.prototype.lockType = 'pin';

  SimLockSystemDialog.prototype.lockTypeMap = {
    'pinRequired': 'pin',
    'pukRequired': 'puk',
    'networkLocked': 'nck',
    'corporateLocked': 'cck',
    'serviceProviderLocked': 'spck',
    'network1Locked': 'nck1',
    'network2Locked': 'nck2',
    'hrpdNetworkLocked': 'hnck',
    'ruimCorporateLocked': 'rcck',
    'ruimServiceProviderLocked': 'rspck'
  };

  SimLockSystemDialog.prototype.customID = 'simlock-dialog';

  SimLockSystemDialog.prototype.DEBUG = false;

  SimLockSystemDialog.prototype.name = 'SimLockSystemDialog';

  SimLockSystemDialog.prototype.EVENT_PREFIX = 'simlock';

  SimLockSystemDialog.prototype.view = function spd_view() {
    return `<div id="${this.instanceID}" role="dialog"
           class="generic-dialog" data-z-index-level="system-dialog" hidden>
           <section role="region">
             <gaia-header>
               <h1></h1>
             </gaia-header>
             <div class="container">
             <div id="errorMsg" class="error" hidden>
               <div id="messageHeader">The PIN was incorrect.</div>
               <span id="messageBody">3 tries left.</span>
             </div>
             <!-- tries left -->
             <div id="triesLeft" data-l10n-id="inputCodeRetriesLeft" hidden>
            </div>
             <!-- sim pin input field -->
             <div id="pinArea" hidden>
               <div data-l10n-id="simPin"></div>
               <div class="input-wrapper">
                 <input name="simpin" type="password" x-inputmode="digit"
                 size="8" maxlength="8" />
               </div>
             </div>
             <!-- sim puk input field -->
             <div id="pukArea" hidden>
               <div data-l10n-id="pukCode"></div>
               <div class="input-wrapper">
                 <input name="simpuk" type="password" x-inputmode="digit"
                 size="8" maxlength="8" />
               </div>
             </div>
             <!-- sim nck/cck/spck input field -->
             <div id="xckArea" hidden>
               <div name="xckDesc" data-l10n-id="nckCode"></div>
               <div class="input-wrapper">
                 <input name="xckpin" type="number" size="16"
                 maxlength="16" />
               </div>
             </div>
             <!-- new sim pin input field -->
             <div id="newPinArea" hidden>
               <div data-l10n-id="newSimPinMsg"></div>
               <div class="input-wrapper">
                 <input name="newSimpin" type="password"
                 x-inputmode="digit" size="8" maxlength="8" />
               </div>
             </div>
             <!-- confirm new sim pin input field -->
             <div id="confirmPinArea" hidden>
               <div data-l10n-id="confirmNewSimPinMsg"></div>
               <div class="input-wrapper">
                 <input name="confirmNewSimpin" type="password"
                 x-inputmode="digit" size="8" maxlength="8" />
               </div>
             </div>
             </div>
           </section>
           <menu data-items="2">
             <button type="reset" data-l10n-id="skip"></button>
             <button data-l10n-id="ok" type="submit"></button>
           </menu>
           </div>`;
  };

  SimLockSystemDialog.prototype.onHide = function() {
    this._visible = false;
    this.controller && this.controller.onHide();
  };

  SimLockSystemDialog.prototype.updateHeight = function() {
    SystemDialog.prototype.updateHeight.apply(this, arguments);
    document.activeElement.scrollIntoView(false);
  };

  SimLockSystemDialog.prototype._registerEvents = function() {
    this.dialogDone.onclick = this.verify.bind(this);
    this.dialogSkip.onclick = this.skip.bind(this);
    this.dialogDone.onmousedown = this._handle_mousedown.bind(this);
    this.dialogSkip.onmousedown = this._handle_mousedown.bind(this);
    this.header.addEventListener('action', this.back.bind(this));
    this.pinInput = this.getNumberPasswordInputField('simpin');
    this.pukInput = this.getNumberPasswordInputField('simpuk');
    this.xckInput = this.getNumberPasswordInputField('xckpin');
    this.newPinInput = this.getNumberPasswordInputField('newSimpin');
    this.confirmPinInput =
      this.getNumberPasswordInputField('confirmNewSimpin');

    this.ensureFocusInView(this.pinInput, this.containerDiv);
    this.ensureFocusInView(this.pukInput, this.containerDiv);
  };

  SimLockSystemDialog.prototype._fetchElements = function spl_initElements() {
    this.dialogTitle =
      document.querySelector('#simlock-dialog gaia-header h1');
    this.dialogDone =
      document.querySelector('#simlock-dialog button[type="submit"]');
    this.dialogSkip =
      document.querySelector('#simlock-dialog button[type="reset"]');
    this.header = document.querySelector('#simlock-dialog gaia-header');

    this.pinArea = document.getElementById('pinArea');
    this.pukArea = document.getElementById('pukArea');
    this.xckArea = document.getElementById('xckArea');
    this.desc = document.querySelector('#xckArea div[name="xckDesc"]');
    this.newPinArea = document.getElementById('newPinArea');
    this.confirmPinArea = document.getElementById('confirmPinArea');

    this.triesLeftMsg = document.getElementById('triesLeft');

    this.errorMsg = document.getElementById('errorMsg');
    this.errorMsgHeader = document.getElementById('messageHeader');
    this.errorMsgBody = document.getElementById('messageBody');

    this.containerDiv = document.querySelector('#simlock-dialog .container');
  };

  SimLockSystemDialog.prototype.getNumberPasswordInputField = function(name) {
    var inputField = document.querySelector('input[name="' + name + '"]');
    var self = this;

    inputField.addEventListener('input', function(evt) {
      if (evt.target !== inputField) {
        return;
      }

      checkDialogDone();
    });

    inputField.addEventListener('focus', function() {
      checkDialogDone();
    });

    function checkDialogDone() {
      if (inputField.value.length >= 4) {
        self.dialogDone.disabled = false;
      } else {
        self.dialogDone.disabled = true;
      }
    }


    return inputField;
  };

  SimLockSystemDialog.prototype.handleCardState = function() {
    var _ = navigator.mozL10n.setAttributes;

    if (!this._currentSlot) {
      return;
    }

    var card = this._currentSlot.simCard;

    var cardState = card.cardState;
    var lockType = this.lockTypeMap[cardState];

    var request = this._currentSlot.getCardLockRetryCount(lockType);
    request.onsuccess = (function() {
      var retryCount = request.result.retryCount;
      if (retryCount) {
        var l10nArgs = { n: retryCount };
        _(this.triesLeftMsg, 'inputCodeRetriesLeft', l10nArgs);
        this.triesLeftMsg.hidden = false;
      }
    }).bind(this);
    request.onerror = function() {
      console.log('Could not fetch CardLockRetryCount', request.error.name);
    };

    switch (lockType) {
      case 'pin':
        this.lockType = lockType;
        this.errorMsg.hidden = true;
        this.inputFieldControl(true, false, false, false);
        break;
      case 'puk':
        this.lockType = lockType;
        _(this.errorMsgHeader, 'simCardLockedMsg');
        _(this.errorMsgBody, 'enterPukMsg');
        this.errorMsg.hidden = false;
        this.inputFieldControl(false, true, false, true);
        break;
      case 'nck':
      case 'cck':
      case 'spck':
      case 'nck1':
      case 'nck2':
      case 'hnck':
      case 'rcck':
      case 'rspck':
        this.lockType = lockType;
        this.errorMsg.hidden = true;
        this.inputFieldControl(false, false, true, false);
        _(this.desc, lockType + 'Code');
        break;
      default:
        this.skip();
        break;
    }
    if (this.lockType !== 'pin' || !SIMSlotManager.isMultiSIM()) {
      _(this.dialogTitle, this.lockType + 'Title');
    } else {
      _(this.dialogTitle, 'multiSIMpinTitle',
        { n: this._currentSlot.index + 1 });
    }
  };

  SimLockSystemDialog.prototype.handleError = function(lockType, retryCount) {
    var retry = (retryCount) ? retryCount : -1;
    this.showErrorMsg(retry, lockType);
    if (retry === -1) {
      this.skip();
      return;
    }
    if (lockType === 'pin') {
      this.pinInput.focus();
    } else if (lockType === 'puk') {
      this.pukInput.focus();
    } else {
      this.xckInput.focus();
    }
  };

  SimLockSystemDialog.prototype.showErrorMsg = function(retry, type) {
    var _ = navigator.mozL10n.setAttributes;
    var l10nArgs = { n: retry };

    _(this.triesLeftMsg, 'inputCodeRetriesLeft', l10nArgs);
    _(this.errorMsgHeader, type + 'ErrorMsg');
    if (retry !== 1) {
      _(this.errorMsgBody, type + 'AttemptMsg2', l10nArgs);
    } else {
      _(this.errorMsgBody, type + 'LastChanceMsg');
    }

    this.errorMsg.hidden = false;
  };

  SimLockSystemDialog.prototype.unlockPin = function() {
    var pin = this.pinInput.value;
    if (pin === '') {
      return;
    }

    var options = { lockType: 'pin', pin: pin };
    this.unlockCardLock(options);
  };

  SimLockSystemDialog.prototype.unlockPuk = function() {
    var _ = navigator.mozL10n.setAttributes;

    var puk = this.pukInput.value;
    var newPin = this.newPinInput.value;
    var confirmPin = this.confirmPinInput.value;
    if (puk === '' || newPin === '' || confirmPin === '') {
      return;
    }

    if (newPin !== confirmPin) {
      _(this.errorMsgHeader, 'newPinErrorMsg');
      _(this.errorMsgBody, '');
      this.errorMsg.hidden = false;
      return;
    }
    var options = {lockType: 'puk', puk: puk, newPin: newPin };
    this.unlockCardLock(options);
    this.clear();
  };

  SimLockSystemDialog.prototype.unlockXck = function() {
    var xck = this.xckInput.value;
    if (xck === '') {
      return;
    }

    var options = {lockType: this.lockType, pin: xck };
    this.unlockCardLock(options);
    this.clear();
  };

  SimLockSystemDialog.prototype.unlockCardLock = function(options) {
    var req = this._currentSlot.unlockCardLock(options);
    this.disableInput();
    req.onsuccess = this.requestClose.bind(this, 'success');
    req.onerror = (function spl_unlockCardLockError(result) {
      this.clear();
      this.handleError(options.lockType, req.error.retryCount);
    }).bind(this);
  };

  SimLockSystemDialog.prototype.focus = function() {
    switch (this.lockType) {
      case 'pin':
        this.pinInput.focus();
        break;
      case 'puk':
        this.pukInput.focus();
        break;
      default:
        this.xckInput.focus();
        break;
    }
  };

  SimLockSystemDialog.prototype.inputFieldControl =
    function(isPin, isPuk, isXck, isNewPin) {
      this.pinArea.hidden = !isPin;
      this.pukArea.hidden = !isPuk;
      this.xckArea.hidden = !isXck;
      this.newPinArea.hidden = !isNewPin;
      this.confirmPinArea.hidden = !isNewPin;
      this.requestFocus();
    };

  SimLockSystemDialog.prototype._handle_mousedown = function(evt) {
    evt.preventDefault();
  };

  SimLockSystemDialog.prototype.verify = function() {
    if (this.lockType === 'pin') {
      this.unlockPin();
    } else if (this.lockType === 'puk') {
      this.unlockPuk();
    } else {
      this.unlockXck();
    }
    return false;
  };

  SimLockSystemDialog.prototype.onHide = function() {
    this.clear();
    if (this.onclose) {
      this.onclose();
    }
  };

  SimLockSystemDialog.prototype.disableInput = function() {
    this.pinInput.disabled = true;
    this.pukInput.disabled = true;
    this.xckInput.disabled = true;
    this.newPinInput.disabled = true;
    this.confirmPinInput.disabled = true;
  };

  SimLockSystemDialog.prototype.enableInput = function() {
    this.pinInput.disabled = false;
    this.pukInput.disabled = false;
    this.xckInput.disabled = false;
    this.newPinInput.disabled = false;
    this.confirmPinInput.disabled = false;
  };

  SimLockSystemDialog.prototype.clear = function() {
    this.enableInput();
    this.errorMsg.hidden = true;
    this.pinInput.value = '';
    this.pinInput.blur();
    this.pukInput.value = '';
    this.pukInput.blur();
    this.xckInput.value = '';
    this.xckInput.blur();
    this.newPinInput.value = '';
    this.newPinInput.blur();
    this.confirmPinInput.value = '';
    this.confirmPinInput.blur();
  };

  SimLockSystemDialog.prototype.onclose = null;

  SimLockSystemDialog.prototype._visible = false;

  /**
   * Show the SIM pin dialog
   * @param {Object} slot SIMSlot instance.
   * @param {Boolean} [skipped] If the last slot is skipped or not.
   */
  SimLockSystemDialog.prototype.show = function(slot, skipped) {
    this.clear();
    if (slot) {
      this._currentSlot = slot;
    }

    SystemDialog.prototype.show.apply(this);
    screen.mozLockOrientation(ORIENTATION);
    this._visible = true;
    this.lockType = 'pin';
    this.handleCardState();

    if (skipped) {
      this.header.setAttribute('action', 'back');
      // Force header-text to be repositioned
      // now the action-button is present
      this.dialogTitle.textContent = this.dialogTitle.textContent;
    } else {
      this.header.removeAttribute('action');
    }
  };

  SimLockSystemDialog.prototype.requestClose = function() {
    this._dispatchEvent('requestclose');
  };

  SimLockSystemDialog.prototype.hide = function() {
    this._visible = false;
    SystemDialog.prototype.hide.apply(this);
    screen.mozUnlockOrientation();
  };

  SimLockSystemDialog.prototype.close = function() {
    this._dispatchEvent('close');
    this.hide();
    this._visible = false;
  };

  SimLockSystemDialog.prototype.skip = function() {
    this._dispatchEvent('skip');
  };

  SimLockSystemDialog.prototype.back = function() {
    this._dispatchEvent('back');
  };

  // With the keyboard active the inputs, ensure they get scrolled
  // into view
  SimLockSystemDialog.prototype.ensureFocusInView =
    function(element, container) {
      element.addEventListener('focus', function(e) {
        window.addEventListener('system-resize', function resize() {
          window.removeEventListener('system-resize', resize);
          // The layout always has the input at the bottom, so
          // just always ensure we scroll to the bottom
          container.scrollTop = container.offsetHeight;
        });
      });
    };

  SimLockSystemDialog.prototype.requestFocus = function() {
    this._dispatchEvent('requestfocus');
  };

  SimLockSystemDialog.prototype._dispatchEvent = function _dispatchEvent(name) {
    var self = this;
    if (applications.ready) {
      this.publish(name);
    } else {
      window.addEventListener('applicationready', function onReady() {
        window.removeEventListener('applicationready', onReady);
        self.publish(name);
      });
    }
  };

  exports.SimLockSystemDialog = SimLockSystemDialog;
}(window));