Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
274 lines
7.7 KiB
JavaScript
Executable file
274 lines
7.7 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/* globals addMixin, ObserverSubjectMixin, DebugMixin */
|
|
/* exported PinView */
|
|
|
|
(function(exports) {
|
|
// number of pin input attempts which should
|
|
// trigger the pin warning display
|
|
const PIN_ATTEMPTS_WARNING = 2;
|
|
const PIN_INCORRECT_TIMEOUT = 1000; //ms
|
|
const PIN_DEFAULT_LEN = 4;
|
|
|
|
const PIN_REQ_DEF = 'request-default-pin';
|
|
const PIN_REQ_BEFORE_CHANGE = 'request-pin-before-change';
|
|
const PIN_REQ_PUK = 'request-puk';
|
|
const PIN_REQ_NEW_PIN = 'request-new-pin';
|
|
const PIN_REQ_REPEAT_NEW_PIN = 'request-repeat-new-pin';
|
|
|
|
const PIN_ENTRY_TEXT_MAP = {};
|
|
|
|
PIN_ENTRY_TEXT_MAP[PIN_REQ_DEF] = {
|
|
entry: 'Please enter your PIN',
|
|
error: 'Incorrect PIN'
|
|
};
|
|
|
|
PIN_ENTRY_TEXT_MAP[PIN_REQ_BEFORE_CHANGE] = {
|
|
entry: 'Please enter current PIN',
|
|
error: 'Incorrect PIN'
|
|
},
|
|
|
|
PIN_ENTRY_TEXT_MAP[PIN_REQ_PUK] = {
|
|
entry: 'Please enter your PUK',
|
|
error: 'Incorrect PUK'
|
|
};
|
|
|
|
PIN_ENTRY_TEXT_MAP[PIN_REQ_NEW_PIN] = {
|
|
entry: 'Please enter new PIN',
|
|
error: 'New PIN mismatch',
|
|
};
|
|
|
|
PIN_ENTRY_TEXT_MAP[PIN_REQ_REPEAT_NEW_PIN] = {
|
|
entry: 'Please confirm new PIN',
|
|
error: ''
|
|
};
|
|
|
|
var PinView = function(id, defPinLen, templateId) {
|
|
this._id = id;
|
|
this._defaultPinLen = defPinLen || PIN_DEFAULT_LEN;
|
|
this._maxPinLen = this._defaultPinLen;
|
|
|
|
addMixin(this, ObserverSubjectMixin);
|
|
addMixin(this, DebugMixin);
|
|
|
|
if(templateId) {
|
|
this._el = document.querySelector('#' + templateId).cloneNode(true);
|
|
this._el.id = this._id;
|
|
} else {
|
|
this._el = document.querySelector('#' + this._id);
|
|
}
|
|
|
|
this._init();
|
|
};
|
|
|
|
PinView.prototype = {
|
|
REQUEST_PIN: PIN_REQ_DEF,
|
|
REQUEST_BEFORE_CHANGE: PIN_REQ_BEFORE_CHANGE,
|
|
REQUEST_PUK: PIN_REQ_PUK,
|
|
REQUEST_NEW_PIN: PIN_REQ_NEW_PIN,
|
|
REQUEST_CONFIRM: PIN_REQ_REPEAT_NEW_PIN,
|
|
|
|
_el: null,
|
|
_id: null,
|
|
_visible: false,
|
|
|
|
_pinInputEl: null,
|
|
_inputedPin: '',
|
|
|
|
// we have a default PIN length but we can do one time request for PINs
|
|
// of different length (i.e. PUK)
|
|
_defaultPinLen: 0,
|
|
_maxPinLen: 0,
|
|
|
|
_inputBlocked: false,
|
|
|
|
isVisible() {
|
|
return this._visible;
|
|
},
|
|
|
|
show: function() {
|
|
this._visible = true;
|
|
this._el.classList.remove('hide');
|
|
this._clearPin();
|
|
},
|
|
|
|
hide: function() {
|
|
this._visible = false;
|
|
this._el.classList.add('hide');
|
|
},
|
|
|
|
_init: function() {
|
|
this._pinInputEl = this._el.querySelector('.pin-input');
|
|
|
|
var keyHandler = this._keyHandler.bind(this);
|
|
var keypad = this._el.querySelector('.pin-keypad');
|
|
keypad.addEventListener('touchstart', keyHandler, true);
|
|
|
|
this._updatePinInput();
|
|
},
|
|
|
|
_keyHandler: function(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
var key = event.target.dataset.value;
|
|
if(!key || this._inputBlocked) {
|
|
return;
|
|
}
|
|
|
|
switch(key) {
|
|
case 'clear': this._clearPin(); break;
|
|
case 'backspace': this._backspacePin(); break;
|
|
default: this._appendPin(key);
|
|
}
|
|
},
|
|
|
|
_clearPin: function() {
|
|
this._inputedPin = '';
|
|
this._updatePinInput();
|
|
},
|
|
|
|
_backspacePin: function() {
|
|
if(!this._inputedPin.length) {
|
|
return;
|
|
}
|
|
|
|
this._inputedPin = this._inputedPin.slice(0, -1);
|
|
this._updatePinInput();
|
|
},
|
|
|
|
_appendPin: function(digit) {
|
|
if(this._inputedPin.length >= this._maxPinLen) {
|
|
return;
|
|
}
|
|
|
|
this._inputedPin += digit;
|
|
this._updatePinInput();
|
|
|
|
if(this._inputedPin.length === this._maxPinLen) {
|
|
if(this._maxPinLen !== this._defaultPinLen) {
|
|
this._maxPinLen = this._defaultPinLen;
|
|
}
|
|
this._notify({ action: 'pin-inputed', pin: this._inputedPin });
|
|
}
|
|
},
|
|
|
|
_updatePinInput: function() {
|
|
var inputedDigits = this._inputedPin.length;
|
|
var fragment = document.createDocumentFragment();
|
|
for(var i = 0; i < this._maxPinLen; i++) {
|
|
var pinDigit = document.createElement('div');
|
|
if(i < inputedDigits) {
|
|
pinDigit.classList.add('pin-digit-entered');
|
|
}
|
|
fragment.appendChild(pinDigit);
|
|
}
|
|
this._pinInputEl.innerHTML = '';
|
|
this._pinInputEl.appendChild(fragment);
|
|
},
|
|
|
|
_updatePinInputTexts: function _updatePinInputTexts(reqType) {
|
|
reqType = (reqType in PIN_ENTRY_TEXT_MAP) ? reqType : PIN_REQ_DEF;
|
|
this._el.querySelector('.pin-entry-text')
|
|
.textContent = PIN_ENTRY_TEXT_MAP[reqType].entry;
|
|
this._el.querySelector('.pin-entry-error-text')
|
|
.textContent = PIN_ENTRY_TEXT_MAP[reqType].error;
|
|
},
|
|
|
|
requestPin: function(attemptsLeft, showPinIncorrect, reqType, pinLen) {
|
|
this.debug('requestPIN, tries: ' + attemptsLeft + ', type: ' + reqType);
|
|
this._maxPinLen = pinLen || this._maxPinLen;
|
|
this._clearPin();
|
|
|
|
this._updatePinInputTexts(reqType);
|
|
this._showByClass('pin-unblocked');
|
|
|
|
if(showPinIncorrect) {
|
|
this._showPinIncorrect();
|
|
if(reqType !== PIN_REQ_NEW_PIN) {
|
|
this._showWarning(attemptsLeft);
|
|
}
|
|
} else if(attemptsLeft <= PIN_ATTEMPTS_WARNING) {
|
|
this._showWarning(attemptsLeft);
|
|
} else {
|
|
this._clearWarning();
|
|
}
|
|
},
|
|
|
|
cancelPinRequest: function() {
|
|
this._maxPinLen = this._defaultPinLen;
|
|
this._notify({});
|
|
},
|
|
|
|
blockPin: function() {
|
|
window.navigator.vibrate([200, 100, 200, 100, 200]);
|
|
this._showByClass('pin-blocked');
|
|
|
|
return new Promise((resolve, reject) => {
|
|
var unblockBtn = this._el.querySelector('.pin-unblock-btn');
|
|
var onclick = () => {
|
|
unblockBtn.removeEventListener('click', onclick);
|
|
resolve();
|
|
};
|
|
|
|
unblockBtn.addEventListener('click', onclick);
|
|
});
|
|
},
|
|
|
|
blockPuk: function() {
|
|
window.navigator.vibrate([200, 100, 200, 100, 200]);
|
|
this._showByClass('puk-blocked');
|
|
return new Promise(()=>{});
|
|
},
|
|
|
|
showEnableFailed: function(tries, defaultPin) {
|
|
window.navigator.vibrate([200, 100, 200, 100, 200]);
|
|
this._showByClass('pin-enable-error');
|
|
this._el.querySelector('.pin-default-val').textContent = defaultPin;
|
|
if(tries || tries === 0) {
|
|
this._el.querySelector('.pin-enable-warning').classList.remove('hide');
|
|
var att = (tries === 1) ? ' entry attempt' : ' entry attempts';
|
|
this._el.querySelector('.pin-enable-attempt').textContent = tries + att;
|
|
} else {
|
|
this._el.querySelector('.pin-enable-warning').classList.add('hide');
|
|
}
|
|
},
|
|
|
|
_showByClass: function(clss) {
|
|
var nodesList = this._el.querySelectorAll('article');
|
|
Array.prototype.slice.call(nodesList).forEach(el => {
|
|
if(el.classList.contains(clss)) {
|
|
el.classList.remove('hide');
|
|
} else {
|
|
el.classList.add('hide');
|
|
}
|
|
});
|
|
},
|
|
|
|
_showWarning: function(attemptsLeft) {
|
|
this._el.querySelector('.pin-warning').classList.remove('hide');
|
|
var warning = attemptsLeft + ' attempt';
|
|
warning = (attemptsLeft !== 1) ? (warning + 's') : warning;
|
|
warning += ' remaining';
|
|
this._el.querySelector('.pin-attempts').textContent = warning;
|
|
},
|
|
|
|
_togglePinIncorrect: function(show) {
|
|
this._inputBlocked = show;
|
|
this._el.querySelector('.pin-entry').classList.toggle('hide');
|
|
this._el.querySelector('.pin-incorrect').classList.toggle('hide');
|
|
},
|
|
|
|
_showPinIncorrect: function() {
|
|
window.navigator.vibrate([200, 100, 200]);
|
|
this._togglePinIncorrect(true);
|
|
setTimeout(() => this._togglePinIncorrect(false), PIN_INCORRECT_TIMEOUT);
|
|
},
|
|
|
|
_clearWarning: function() {
|
|
this._el.querySelector('.pin-warning').classList.add('hide');
|
|
}
|
|
};
|
|
|
|
exports.PinView = PinView;
|
|
}((typeof exports === 'undefined') ? window : exports));
|