Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
|
|
@ -0,0 +1,316 @@
|
|||
'use strict';
|
||||
|
||||
/* globals addMixin, DebugMixin, PinView, DEFAULTS, PIN_DEFAULTS */
|
||||
/* exported PinController */
|
||||
|
||||
(function(exports) {
|
||||
|
||||
const PIN_STATE = {
|
||||
UNKNOWN: 'unknown',
|
||||
ENABLED: 'enabled',
|
||||
DISABLED: 'disabled',
|
||||
BLOCKED: 'blocked',
|
||||
};
|
||||
|
||||
var _defaultPin = DEFAULTS.PIN_VALUE;
|
||||
|
||||
var PinController = function(simDataSource, id, viewId, templateId) {
|
||||
this._simDataSource = simDataSource;
|
||||
this._id = id;
|
||||
this._view = new PinView(viewId, PIN_DEFAULTS.PIN_LEN, templateId);
|
||||
|
||||
addMixin(this, DebugMixin);
|
||||
};
|
||||
|
||||
PinController.prototype = {
|
||||
_id: null,
|
||||
_simDataSource: null,
|
||||
_view: null,
|
||||
|
||||
// TODO consider removing
|
||||
_simPinState: PIN_STATE.UNKNOWN,
|
||||
|
||||
_pinCheckInProgress: false,
|
||||
_lastInputedPin: null,
|
||||
|
||||
get pinCheckInProgress() {
|
||||
return this._pinCheckInProgress;
|
||||
},
|
||||
|
||||
set defaultPin(value) {
|
||||
_defaultPin = value;
|
||||
},
|
||||
|
||||
get view() {
|
||||
return this._view._el;
|
||||
},
|
||||
|
||||
hideView: function() {
|
||||
this._view.hide();
|
||||
},
|
||||
|
||||
verifyPin: function() {
|
||||
return this._requestPinVerification().then((result) => {
|
||||
if(result) {
|
||||
this.hideView();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
enablePin: function() {
|
||||
return this._requestPinVerification()
|
||||
.then((verified) => {
|
||||
if(verified) {
|
||||
this.debug('Pin verified');
|
||||
this._simPinState = PIN_STATE.ENABLED;
|
||||
this._lastInputedPin = null;
|
||||
return true;
|
||||
} else {
|
||||
this.debug('Pin verification failed, pin probably disabled');
|
||||
var pin = this._lastInputedPin;
|
||||
this._lastInputedPin = null;
|
||||
return this._enableSimPin(pin);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
disablePin: function() {
|
||||
return this._requestPinVerification()
|
||||
.then((verified) => {
|
||||
if(verified) {
|
||||
this.debug('Pin verified');
|
||||
var pin = this._lastInputedPin;
|
||||
this._lastInputedPin = null;
|
||||
return this._disableSimPin(pin);
|
||||
} else {
|
||||
this.debug('Pin verification failed, pin probably disabled');
|
||||
this._lastInputedPin = null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
changePin: function() {
|
||||
var pin;
|
||||
return this._requestPinVerification(this._view.REQUEST_BEFORE_CHANGE)
|
||||
.then((verified) => {
|
||||
if(!verified) {
|
||||
this.debug('Pin not verfied, not changing');
|
||||
this._lastInputedPin = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
pin = this._lastInputedPin;
|
||||
this._lastInputedPin = null;
|
||||
return this._getNewPin();
|
||||
})
|
||||
.then((newPin) => {
|
||||
this.debug('New pin value ' + newPin);
|
||||
var pinBytes = this._formatPin(pin);
|
||||
var newPinBytes = this._formatPin(newPin);
|
||||
return this._simDataSource.changePIN(pinBytes, newPinBytes);
|
||||
})
|
||||
.then((result) => this._handlePinChangeResult(result));
|
||||
},
|
||||
|
||||
cancelPinAction: function() {
|
||||
this._view.cancelPinRequest();
|
||||
},
|
||||
|
||||
_handlePinChangeResult: function(result) {
|
||||
this.debug('Pin change result ' + result);
|
||||
if(result === '9000') {
|
||||
return true;
|
||||
} else {
|
||||
this.debug('Unexpected error');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_getNewPin: function(prevAttempFailed) {
|
||||
var newPin;
|
||||
return this._getUserPin(PIN_DEFAULTS.MAX_PUK_ATTEMPTS, prevAttempFailed,
|
||||
this._view.REQUEST_NEW_PIN)
|
||||
.then((pin1) => {
|
||||
newPin = pin1;
|
||||
return this._getUserPin(PIN_DEFAULTS.MAX_PUK_ATTEMPTS, false,
|
||||
this._view.REQUEST_CONFIRM);
|
||||
})
|
||||
.then((pin2) => {
|
||||
if(newPin === pin2) {
|
||||
return newPin;
|
||||
}
|
||||
|
||||
this.debug('Pins do not match');
|
||||
return this._getNewPin(true);
|
||||
});
|
||||
},
|
||||
|
||||
_requestPinVerification: function(reqType) {
|
||||
this.debug('_requestPinVerification, SIM PIN : ' + this._simPinState);
|
||||
// TODO verify if we need this
|
||||
//if(this._pinCheckInProgress) {
|
||||
// return Promise.resolve(false);
|
||||
//}
|
||||
|
||||
this._pinCheckInProgress = true;
|
||||
this._view.show();
|
||||
return this._performVerification(PIN_DEFAULTS.MAX_PIN_ATTEMPTS, false, reqType)
|
||||
.then((verified) => {
|
||||
if(verified) {
|
||||
//this._view.hide();
|
||||
// TODO think this through
|
||||
this._pinCheckInProgress = false;
|
||||
}
|
||||
|
||||
return verified;
|
||||
});
|
||||
},
|
||||
|
||||
_getUserPin: function(attemptsLeft, lastVerFailed, reqType, pinLen) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var listener = {
|
||||
onEvent: (id, data) => {
|
||||
this._view.removeListener(listener);
|
||||
if (data.pin) {
|
||||
this.debug('got PIN: ' + data.pin);
|
||||
resolve(data.pin);
|
||||
} else {
|
||||
reject('pin-cancelled');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._view.addListener(listener);
|
||||
if(!this._view.isVisible) {
|
||||
this._view.show();
|
||||
}
|
||||
|
||||
this.debug('waiting for PIN input from user');
|
||||
this._view.requestPin(attemptsLeft, lastVerFailed, reqType, pinLen);
|
||||
});
|
||||
},
|
||||
|
||||
_performVerification: function(attemptsLeft, lastVerFailed, reqType) {
|
||||
return this._getUserPin(attemptsLeft, lastVerFailed, reqType)
|
||||
.then((pin) => {
|
||||
this._lastInputedPin = pin;
|
||||
var pinBytes = this._formatPin(pin);
|
||||
this.debug('verifying PIN ' + pinBytes);
|
||||
return this._simDataSource.verifyPIN(pinBytes);
|
||||
})
|
||||
.then((result) => this._handleVerficationResult(result));
|
||||
//TODO should we catch here?
|
||||
},
|
||||
|
||||
_handleVerficationResult: function(result) {
|
||||
this.debug('Verification finished, status word:' + result);
|
||||
if(result === '9000') {
|
||||
return true;
|
||||
} else if(result === '63C0' || result === '6983') {
|
||||
this.debug('Pin blocked');
|
||||
return this._handlePinBlocked();
|
||||
} else if(result.startsWith('63C')) {
|
||||
var attemptsLeft = Number.parseInt(result[3]);
|
||||
this.debug('Verification failed, tries left: ' + attemptsLeft);
|
||||
return this._performVerification(attemptsLeft, true);
|
||||
} else {
|
||||
this.debug('Unknown status word, PIN probably not enabled');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_handlePinBlocked: function() {
|
||||
this._simPinState = PIN_STATE.BLOCKED;
|
||||
|
||||
this.debug('PUK unblock flow start.');
|
||||
var unblockRequested = () => this._performPinUnblock(PIN_DEFAULTS.MAX_PUK_ATTEMPTS);
|
||||
return this._view.blockPin().then(unblockRequested);
|
||||
},
|
||||
|
||||
_performPinUnblock: function(attemptsLeft, lastUnblockFailed) {
|
||||
var puk;
|
||||
return this._getUserPin(attemptsLeft, lastUnblockFailed,
|
||||
this._view.REQUEST_PUK, PIN_DEFAULTS.MAX_SIM_PIN_LEN)
|
||||
.then((inputedPuk) => {
|
||||
this.debug('Inputed PUK ' + inputedPuk);
|
||||
puk = inputedPuk;
|
||||
return this._getNewPin(false);
|
||||
})
|
||||
.then((inputedPin) => {
|
||||
this.debug('Inputed PIN ' + inputedPin);
|
||||
var bytePuk = this._formatPin(puk);
|
||||
var bytePin = this._formatPin(inputedPin);
|
||||
// if we're part of disable/enable flow we need to save pin here
|
||||
this._lastInputedPin = inputedPin;
|
||||
return this._simDataSource.unblockPIN(bytePuk, bytePin);
|
||||
})
|
||||
.then((result) => this._handlePinUnblockResult(result));
|
||||
},
|
||||
|
||||
_handlePinUnblockResult: function(result) {
|
||||
this.debug('PIN unblock result ' + result);
|
||||
if(result === '9000') {
|
||||
return true;
|
||||
} else if(result === '63C0' || !result.startsWith('63C')) {
|
||||
this.debug('Complate block');
|
||||
return this._view.blockPuk();
|
||||
} else {
|
||||
var attemptsLeft = Number.parseInt(result[3]);
|
||||
this.debug('PIN unblock failed, tries left: ' + attemptsLeft);
|
||||
return this._performPinUnblock(attemptsLeft, true);
|
||||
}
|
||||
},
|
||||
|
||||
_enableSimPin: function(pin) {
|
||||
var pinBytes = this._formatPin(pin);
|
||||
return this._simDataSource.enablePIN(pinBytes)
|
||||
.then((result) => this._handlePinEnabledResult(result));
|
||||
},
|
||||
|
||||
_handlePinEnabledResult: function(result) {
|
||||
this.debug('PIN enable result ' + result);
|
||||
if(result === '9000') {
|
||||
this._simPinState = PIN_STATE.ENABLED;
|
||||
//this._view.hide();
|
||||
return true;
|
||||
} else if(result === '63C0' || result === '6983') {
|
||||
this.debug('Pin blocked');
|
||||
return this._handlePinBlocked();
|
||||
} else {
|
||||
var tries = result.startsWith('63C') ? Number.parseInt(result[3]) : 0;
|
||||
this._view.showEnableFailed(tries, _defaultPin);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_disableSimPin: function(pin) {
|
||||
var pinBytes = this._formatPin(pin);
|
||||
return this._simDataSource.disablePIN(pinBytes)
|
||||
.then((result) => this._handlePinDisableResult(result));
|
||||
},
|
||||
|
||||
_handlePinDisableResult: function(result) {
|
||||
this.debug('PIN disable result ' + result);
|
||||
if(result === '9000') {
|
||||
this._simPinState = PIN_STATE.DISABLED;
|
||||
//this._view.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_formatPin: function(pin) {
|
||||
var pinBytes = pin.split('').map((d) => d.charCodeAt());
|
||||
for(var i = pinBytes.length; i < PIN_DEFAULTS.MAX_SIM_PIN_LEN; i++) {
|
||||
pinBytes.push(PIN_DEFAULTS.PIN_PADDING);
|
||||
}
|
||||
|
||||
return pinBytes;
|
||||
}
|
||||
};
|
||||
|
||||
exports.PinController = PinController;
|
||||
}((typeof exports === 'undefined') ? window : exports));
|
||||
274
assets/webapps/mobile-wallet.gaiamobile.org/js/pin/pin_view.js
Normal file
274
assets/webapps/mobile-wallet.gaiamobile.org/js/pin/pin_view.js
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
'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));
|
||||
Loading…
Reference in a new issue