VulpesOS/gaia/tv_apps/remote-control-client/js/pairing.js
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

250 lines
6.5 KiB
JavaScript
Executable file

'use strict';
(function(exports) {
// The .sjs file is located in the Gecko since it needs chrome privilege.
var AJAX_URL = 'pairing.sjs';
function init() {
var btnSubmit = document.getElementById('connect');
var btnRestartPairing = document.getElementById('restart-pairing');
var pinCodeInput = new PinCodeInput(
document.getElementById('input-mask'),
[].slice.call(document.querySelectorAll('.code')),
btnSubmit
);
btnRestartPairing.addEventListener('click', function() {
window.location.reload();
});
btnSubmit.disabled = false;
btnSubmit.addEventListener('click', function(evt) {
var pincode = pinCodeInput.getCodes();
if (pincode === '') {
pinCodeInput.highlightEmptyCode();
return;
}
btnSubmit.disabled = true;
var onerror = function(message) {
pinCodeInput.empty();
btnSubmit.disabled = false;
showMessage(message, true);
};
exports.sendMessage(
AJAX_URL,
{
message: JSON.stringify({ pincode: pincode })
},
function success(data) {
if (data) {
if (data.verified) {
document.l10n.formatValue('connect-success')
.then(function(value) {
showMessage(value);
// The cookie will be used by server via http header.
exports.setCookie('uuid', data.uuid);
setTimeout(function() {
// Server will help redirecting to client.html when there is
// a UUID in cookie.
window.location.reload();
}, 1000);
});
} else if (data.reason == 'expired') {
document.l10n.formatValue('pin-code-expired-message')
.then(onerror);
document.getElementById('pairing-container')
.classList.add('pin-code-expired');
} else {
document.l10n.formatValue('wrong-pin').then(onerror);
}
} else {
document.l10n.formatValue('connect-error-invalid-response')
.then(onerror);
}
},
function error(status) {
document.l10n.formatValue('connect-error', {status: String(status)})
.then(onerror);
}
);
});
}
function showMessage(message, isError) {
var divMessage = document.getElementById('pairing-message');
divMessage.textContent = message;
divMessage.classList[isError ? 'add' : 'remove']('error');
}
function PinCodeInput(mask, codes, submitButton) {
var self = this;
self._mask = mask;
self._codes = codes;
self._submitButton = submitButton;
self._index = -1;
var createHandler = function(index) {
return function(evt) {
self.showMask(index);
evt.preventDefault();
};
};
for(var i = 0; i < codes.length; i++) {
var handler = createHandler(i);
codes[i].addEventListener('focus', handler);
codes[i].addEventListener('click', handler);
}
mask.addEventListener('blur', function() {
self.hideMask();
});
mask.addEventListener('keydown', function(evt) {
self.onKeyDown(evt);
});
}
PinCodeInput.prototype = {
showMask: function(index) {
this._mask.blur();
this._mask.classList.add('visible');
this.moveMask(index);
this._mask.focus();
},
hideMask: function() {
this.writeBack();
this._mask.blur();
this._mask.classList.remove('visible');
},
moveMask: function(index) {
this._index = index;
this._mask.style.left = this._codes[index].offsetLeft + 'px';
this._mask.value = this._codes[index].textContent;
this._mask.select();
},
moveNext: function() {
if (this._index < this._codes.length - 1) {
this.moveMask(this._index + 1);
return true;
}
return false;
},
movePrev: function() {
if (this._index > 0) {
this.moveMask(this._index - 1);
return true;
}
return false;
},
empty: function() {
for (var i = 0; i < this._codes.length; i++) {
this.setCode(i, '');
}
},
writeBack: function() {
this.setCode(this._index, this._mask.value);
},
setCode: function(index, value) {
if (value.length) {
this._codes[index].classList.remove('placeholder');
this._codes[index].textContent = value.charAt(0);
} else {
this._codes[index].classList.add('placeholder');
this._codes[index].textContent = '';
}
},
getCodes: function() {
var code = '';
for (var i = 0; i < this._codes.length; i++) {
if (this._codes[i].textContent !== '') {
code += this._codes[i].textContent;
} else {
return '';
}
}
return code;
},
highlightEmptyCode: function() {
for (var i = 0; i < this._codes.length; i++) {
if (this._codes[i].textContent === '') {
this.showMask(i);
return true;
}
}
return false;
},
onKeyDown: function(evt) {
var c = evt.keyCode;
if (evt.ctrlKey || evt.altKey || evt.metaKey) {
return;
}
// backspace to delete the previous field if current one is empty.
if (c == 8 && this._mask.value === '') {
if (this._index > 0) {
this.writeBack();
this.setCode(this._index - 1, '');
this.movePrev();
} else {
this.hideMask();
}
evt.preventDefault();
return;
}
// numbers
if (!evt.shiftKey && ((c >= 48 && c <= 57) || (c >= 96 && c <= 105))) {
showMessage('');
var num = String.fromCharCode(c >= 96 ? c - 96 + 48 : c);
this._mask.value = num;
this.writeBack();
if (!this.moveNext()) {
this._submitButton.focus();
}
evt.preventDefault();
return;
}
// tab
if (c == 9) {
this.writeBack();
if (evt.shiftKey) {
if (!this.movePrev()) {
this.hideMask();
}
} else {
if (!this.moveNext()) {
this._submitButton.focus();
}
}
evt.preventDefault();
return;
}
// allowed function keys: backspace, shift, del and F5.
if ([8, 16, 46, 116].indexOf(c) < 0) {
evt.preventDefault();
return;
}
}
};
exports.ready(init);
}(window));