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
8
gaia/tv_apps/remote-control-client/.gitignore
vendored
Executable file
8
gaia/tv_apps/remote-control-client/.gitignore
vendored
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
bower_components/l20n/dist/aisle/
|
||||
bower_components/l20n/dist/gaia/
|
||||
bower_components/l20n/dist/gaiabuild/
|
||||
bower_components/l20n/dist/jsshell/
|
||||
bower_components/l20n/dist/node/
|
||||
bower_components/l20n/dist/tooling/
|
||||
bower_components/l20n/dist/web/
|
||||
bower_components/l20n/dist/webcompat/l20n.min.js
|
||||
19
gaia/tv_apps/remote-control-client/bower.json
Executable file
19
gaia/tv_apps/remote-control-client/bower.json
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "remote-control-client",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/mozilla-b2g/gaia/tree/master/tv_apps/remote-control-client",
|
||||
"description": "Remote Control Client Page",
|
||||
"main": "index.html",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"l20n": "l20n/l20n.js#~3.1.0"
|
||||
}
|
||||
}
|
||||
35
gaia/tv_apps/remote-control-client/bower_components/l20n/.bower.json
vendored
Executable file
35
gaia/tv_apps/remote-control-client/bower_components/l20n/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "l20n",
|
||||
"description": "A natural-language localization framework",
|
||||
"version": "3.1.0",
|
||||
"homepage": "http://l20n.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/l20n/l20n.js.git"
|
||||
},
|
||||
"authors": [
|
||||
"Mozilla <l10n-drivers@mozilla.org>",
|
||||
"Zbigniew Braniecki",
|
||||
"Staś Małolepszy"
|
||||
],
|
||||
"license": "Apache 2.0",
|
||||
"keywords": [
|
||||
"localization",
|
||||
"l10n",
|
||||
"l20n"
|
||||
],
|
||||
"main": [],
|
||||
"ignore": [
|
||||
"**/*",
|
||||
"!dist/**/*"
|
||||
],
|
||||
"_release": "3.1.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v3.1.0",
|
||||
"commit": "ea1272acd8ef68c8ca168499a2e698b2d612ebfa"
|
||||
},
|
||||
"_source": "git://github.com/l20n/l20n.js.git",
|
||||
"_target": "~3.1.0",
|
||||
"_originalSource": "l20n/l20n.js"
|
||||
}
|
||||
26
gaia/tv_apps/remote-control-client/bower_components/l20n/bower.json
vendored
Executable file
26
gaia/tv_apps/remote-control-client/bower_components/l20n/bower.json
vendored
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "l20n",
|
||||
"description": "A natural-language localization framework",
|
||||
"version": "3.1.0",
|
||||
"homepage": "http://l20n.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/l20n/l20n.js.git"
|
||||
},
|
||||
"authors": [
|
||||
"Mozilla <l10n-drivers@mozilla.org>",
|
||||
"Zbigniew Braniecki",
|
||||
"Staś Małolepszy"
|
||||
],
|
||||
"license": "Apache 2.0",
|
||||
"keywords": [
|
||||
"localization",
|
||||
"l10n",
|
||||
"l20n"
|
||||
],
|
||||
"main": [],
|
||||
"ignore": [
|
||||
"**/*",
|
||||
"!dist/**/*"
|
||||
]
|
||||
}
|
||||
2762
gaia/tv_apps/remote-control-client/bower_components/l20n/dist/webcompat/l20n.js
vendored
Executable file
2762
gaia/tv_apps/remote-control-client/bower_components/l20n/dist/webcompat/l20n.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
33
gaia/tv_apps/remote-control-client/client.html
Executable file
33
gaia/tv_apps/remote-control-client/client.html
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>TV remote control</title>
|
||||
<link rel="stylesheet" type="text/css" href="style/main.css">
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/panel_element.js"></script>
|
||||
<script src="js/client.js"></script>
|
||||
<script src="bower_components/l20n/dist/webcompat/l20n.js"></script>
|
||||
<!-- L10n properties -->
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="locales/remote-control-client.{locale}.properties">
|
||||
</head>
|
||||
<body class="client">
|
||||
<h1 data-l10n-id="client-title"></h1>
|
||||
<section id="section-input">
|
||||
<input id="input-string" type="text" data-l10n-id="input">
|
||||
<div id="send-string" class="button" data-l10n-id="send"></div>
|
||||
</section>
|
||||
<section id="section-touch-panel">
|
||||
<div id="touch-panel" class="panel"></div>
|
||||
<div id="scroll-panel" class="panel"></div>
|
||||
</section>
|
||||
<section id="section-buttons">
|
||||
<div id="button-back" class="button" data-key="DOM_VK_BACK_SPACE"></div>
|
||||
<div id="button-home" class="button" data-key="DOM_VK_HOME"></div>
|
||||
<div id="button-option" class="button" data-key="DOM_VK_CONTEXT_MENU"></div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
140
gaia/tv_apps/remote-control-client/js/client.js
Executable file
140
gaia/tv_apps/remote-control-client/js/client.js
Executable file
|
|
@ -0,0 +1,140 @@
|
|||
/* global PanelElement */
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
// The .sjs file is located in the Gecko since it needs chrome privilege.
|
||||
var AJAX_URL = 'client.sjs';
|
||||
|
||||
// The client will send the currently-inputed string to server automatically
|
||||
// whenever a user stops typing for the specified period.
|
||||
var INPUT_STRING_SYNC_PERIOD = 200; // in milliseconds
|
||||
|
||||
var enabled = false;
|
||||
var inputStringSyncTimer = null;
|
||||
|
||||
function sendMessage(type, detail) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = {
|
||||
type: type,
|
||||
detail: (typeof detail === 'object') ? detail : detail.toString()
|
||||
};
|
||||
|
||||
exports.sendMessage(AJAX_URL, {
|
||||
message: JSON.stringify(data)
|
||||
}, function onsuccess(data) {
|
||||
if (!data || !data.verified) {
|
||||
enabled = false;
|
||||
document.l10n.formatValue('session-expired').then(function(value) {
|
||||
alert(value);
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function inputStringSyncHandler() {
|
||||
if (inputStringSyncTimer) {
|
||||
clearTimeout(inputStringSyncTimer);
|
||||
}
|
||||
inputStringSyncTimer = setTimeout(function() {
|
||||
inputStringSyncTimer = null;
|
||||
sendMessage('textinput', {
|
||||
clear: true,
|
||||
string: document.getElementById('input-string').value
|
||||
});
|
||||
}, INPUT_STRING_SYNC_PERIOD);
|
||||
}
|
||||
|
||||
function sendFinalInputString() {
|
||||
if (inputStringSyncTimer) {
|
||||
clearTimeout(inputStringSyncTimer);
|
||||
inputStringSyncTimer = null;
|
||||
}
|
||||
var input = document.getElementById('input-string');
|
||||
sendMessage('textinput', {
|
||||
clear: true,
|
||||
string: input.value,
|
||||
keycode: 13
|
||||
});
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
function init() {
|
||||
var input = document.getElementById('input-string');
|
||||
var btnSend = document.getElementById('send-string');
|
||||
|
||||
input.value = '';
|
||||
input.addEventListener('keydown', function(evt) {
|
||||
switch(evt.keyCode) {
|
||||
case 13: //Enter
|
||||
setTimeout(function() {
|
||||
document.activeElement.blur();
|
||||
sendFinalInputString();
|
||||
});
|
||||
break;
|
||||
case 27: //Escape
|
||||
input.value = '';
|
||||
|
||||
// Workaround Firefox's bug
|
||||
input.blur();
|
||||
input.focus();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
||||
input.addEventListener('focus', function() {
|
||||
// The "select()" doesn't work if it's triggered in a "focus" handler.
|
||||
setTimeout(function() {
|
||||
input.select();
|
||||
});
|
||||
});
|
||||
|
||||
input.addEventListener('input', inputStringSyncHandler);
|
||||
input.addEventListener('keyup', inputStringSyncHandler);
|
||||
|
||||
btnSend.addEventListener('click', sendFinalInputString);
|
||||
|
||||
/* jshint nonew: false */
|
||||
new PanelElement(document.getElementById('touch-panel'), {
|
||||
touchingClass: 'touching',
|
||||
dblClickTimeThreshold: 0,
|
||||
handler: sendMessage
|
||||
});
|
||||
|
||||
/* jshint nonew: false */
|
||||
new PanelElement(document.getElementById('scroll-panel'), {
|
||||
touchingClass: 'touching',
|
||||
dblClickTimeThreshold: 0,
|
||||
clickTimeThreshold: 0,
|
||||
clickMoveThreshold: 0,
|
||||
handler: function(type, detail) {
|
||||
if (detail.dx !== undefined) {
|
||||
detail.dx = 0;
|
||||
}
|
||||
sendMessage(type.replace('touch', 'scroll'), detail);
|
||||
}
|
||||
});
|
||||
|
||||
var buttonOnClick = function() {
|
||||
var key = this.dataset.key;
|
||||
if (key) {
|
||||
sendMessage('keypress', key);
|
||||
}
|
||||
};
|
||||
|
||||
var buttons = document.querySelectorAll('#section-buttons .button');
|
||||
[].slice.call(buttons).forEach(function(elem) {
|
||||
elem.addEventListener('click', buttonOnClick);
|
||||
});
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
exports.ready(init);
|
||||
}(window));
|
||||
95
gaia/tv_apps/remote-control-client/js/main.js
Executable file
95
gaia/tv_apps/remote-control-client/js/main.js
Executable file
|
|
@ -0,0 +1,95 @@
|
|||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
var DEBUG = false;
|
||||
|
||||
// Polyfill for Safari
|
||||
if (!navigator.languages) {
|
||||
var language = navigator.language.split('-', 2);
|
||||
if (language[1].length == 2) {
|
||||
language = language[0].toLowerCase() + '-' + language[1].toUpperCase();
|
||||
} else {
|
||||
language = language[0].toLowerCase() + '-' +
|
||||
language[1].charAt(0).toUpperCase() +
|
||||
language[1].substring(1).toLowerCase();
|
||||
}
|
||||
navigator.languages = [language];
|
||||
}
|
||||
|
||||
exports.ready = function(handler) {
|
||||
var handled = false;
|
||||
function wrapper() {
|
||||
if (!handled) {
|
||||
handled = true;
|
||||
document.removeEventListener('DOMContentLoaded', wrapper);
|
||||
document.removeEventListener('load', wrapper);
|
||||
handler();
|
||||
}
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', wrapper);
|
||||
document.addEventListener('load', wrapper);
|
||||
};
|
||||
|
||||
exports.sendMessage = function(url, data, success, error) {
|
||||
if (typeof data !== 'object') {
|
||||
data = {};
|
||||
}
|
||||
|
||||
var param = [];
|
||||
for (var name in data) {
|
||||
param.push(encodeURIComponent(name) + '=' +
|
||||
encodeURIComponent(data[name]));
|
||||
}
|
||||
|
||||
var paramString = param.length ? '?' + param.join('&') : '';
|
||||
var finalURL = encodeURI(url) + paramString;
|
||||
|
||||
if (DEBUG) {
|
||||
console.log('Ajax URL: ' + finalURL);
|
||||
}
|
||||
|
||||
try {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', finalURL, true);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
var data = xhr.responseText;
|
||||
if (DEBUG) {
|
||||
console.log('Ajax response: ' + data);
|
||||
}
|
||||
if (success) {
|
||||
success(data ? JSON.parse(data) : undefined);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
console.error('Ajax error: ' + xhr.status);
|
||||
}
|
||||
if (error) {
|
||||
error(xhr.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
} catch(err) {
|
||||
if (DEBUG) {
|
||||
console.error('Ajax error: ' + err.name);
|
||||
}
|
||||
if (error) {
|
||||
error(err.name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.setCookie = function(key, value, expires, path) {
|
||||
var cookie = [];
|
||||
cookie.push(encodeURIComponent(String(key)) + '=' +
|
||||
encodeURIComponent(String(value)));
|
||||
if (typeof expires === 'number') {
|
||||
cookie.push(new Date(Date.now() + expires * 864e+5).toUTCString());
|
||||
}
|
||||
if (path) {
|
||||
cookie.push('path=' + encodeURI(path));
|
||||
}
|
||||
document.cookie = cookie.join('; ');
|
||||
};
|
||||
}(window));
|
||||
250
gaia/tv_apps/remote-control-client/js/pairing.js
Executable file
250
gaia/tv_apps/remote-control-client/js/pairing.js
Executable file
|
|
@ -0,0 +1,250 @@
|
|||
'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));
|
||||
323
gaia/tv_apps/remote-control-client/js/panel_element.js
Executable file
323
gaia/tv_apps/remote-control-client/js/panel_element.js
Executable file
|
|
@ -0,0 +1,323 @@
|
|||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
var DEBUG = false;
|
||||
|
||||
var defaultOptions = {
|
||||
touchReportPeriod: 60, // milliseconds
|
||||
dblClickTimeThreshold: 250, // milliseconds
|
||||
clickTimeThreshold: 200, // milliseconds
|
||||
clickMoveThreshold: 10, // pixels
|
||||
swipeMoveThreshold: 25, // pixels
|
||||
touchingClass: null // CSS class
|
||||
};
|
||||
|
||||
function PanelElement(elem, options) {
|
||||
if (typeof options === 'function') {
|
||||
options = {
|
||||
handler: options
|
||||
};
|
||||
} else if (!options || typeof options !== 'object') {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.options = {};
|
||||
for (var key in defaultOptions) {
|
||||
this.options[key] = (typeof options[key] === 'undefined') ?
|
||||
defaultOptions[key] : options[key];
|
||||
}
|
||||
|
||||
if (typeof options.handler === 'function') {
|
||||
this._handler = options.handler.bind(elem);
|
||||
} else {
|
||||
this._handler = function(type, detail) {};
|
||||
}
|
||||
|
||||
this._element = elem;
|
||||
|
||||
this._hasMouseDown = false;
|
||||
this._eventIdentifier = null;
|
||||
this._startX = 0;
|
||||
this._startY = 0;
|
||||
this._prevDx = null;
|
||||
this._prevDy = null;
|
||||
this._startTime = 0;
|
||||
this._waitForClickTimer = null;
|
||||
this._pendingClickTimer = null;
|
||||
this._reportTimer = null;
|
||||
this._pendingMoveEvent = null;
|
||||
this._touchIdentifier = null;
|
||||
this._panelX = 0;
|
||||
this._panelY = 0;
|
||||
this._panelWidth = 0;
|
||||
this._panelHeight = 0;
|
||||
|
||||
this._element.addEventListener('mousedown', this);
|
||||
window.addEventListener('mousemove', this);
|
||||
window.addEventListener('mouseup', this);
|
||||
|
||||
this._element.addEventListener('touchstart', this);
|
||||
this._element.addEventListener('touchmove', this);
|
||||
this._element.addEventListener('touchend', this);
|
||||
|
||||
window.addEventListener('resize', this);
|
||||
this.updatePanelInfo();
|
||||
}
|
||||
|
||||
PanelElement.prototype = {
|
||||
uninit: function() {
|
||||
window.removeEventListener('resize', this);
|
||||
|
||||
this._element.removeEventListener('touchend', this);
|
||||
this._element.removeEventListener('touchmove', this);
|
||||
this._element.removeEventListener('touchstart', this);
|
||||
|
||||
window.removeEventListener('mouseup', this);
|
||||
window.removeEventListener('mousemove', this);
|
||||
this._element.removeEventListener('mousedown', this);
|
||||
|
||||
this._element = null;
|
||||
this._handler = null;
|
||||
this.options = null;
|
||||
},
|
||||
|
||||
updatePanelInfo: function() {
|
||||
var rect = this._element.getBoundingClientRect();
|
||||
this._panelX = Math.round(rect.left);
|
||||
this._panelY = Math.round(rect.top);
|
||||
this._panelWidth = Math.round(rect.width);
|
||||
this._panelHeight = Math.round(rect.height);
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
var touches, touch;
|
||||
|
||||
switch(evt.type) {
|
||||
case 'resize':
|
||||
this.updatePanelInfo();
|
||||
break;
|
||||
case 'mousedown':
|
||||
if (evt.button === 0) {
|
||||
this._hasMouseDown = true;
|
||||
this.onStart(evt.clientX, evt.clientY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'mousemove':
|
||||
if (this._hasMouseDown) {
|
||||
this.onMove(evt.clientX, evt.clientY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'mouseup':
|
||||
if (this._hasMouseDown && evt.button === 0) {
|
||||
this._hasMouseDown = false;
|
||||
this.onEnd(evt.clientX, evt.clientY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'touchstart':
|
||||
if (this._touchIdentifier !== null) {
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
touches = evt.changedTouches;
|
||||
if (touches.length == 1) {
|
||||
touch = touches[0];
|
||||
this._touchIdentifier = touch.identifier;
|
||||
this.onStart(touch.pageX - this._panelX,
|
||||
touch.pageY - this._panelY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'touchmove':
|
||||
case 'touchend':
|
||||
touches = evt.changedTouches;
|
||||
touch = null;
|
||||
for(var i = 0; i < touches.length; i++) {
|
||||
if (touches[i].identifier == this._touchIdentifier) {
|
||||
touch = touches[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (touch) {
|
||||
if (evt.type == 'touchend') {
|
||||
this._touchIdentifier = null;
|
||||
this.onEnd(touch.pageX - this._panelX,
|
||||
touch.pageY - this._panelY);
|
||||
} else {
|
||||
this.onMove(touch.pageX - this._panelX,
|
||||
touch.pageY - this._panelY);
|
||||
}
|
||||
}
|
||||
evt.preventDefault();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onStart: function(x, y) {
|
||||
document.activeElement.blur();
|
||||
|
||||
if (this.options.touchingClass) {
|
||||
this._element.classList.add(this.options.touchingClass);
|
||||
}
|
||||
|
||||
this._startX = x;
|
||||
this._startY = y;
|
||||
this._startTime = Date.now();
|
||||
|
||||
var handleTouchStart = function() {
|
||||
this._waitForClickTimer = null;
|
||||
this.handleTouch('touchstart', 0, 0);
|
||||
}.bind(this);
|
||||
|
||||
if (this.options.clickTimeThreshold) {
|
||||
this._waitForClickTimer = setTimeout(handleTouchStart,
|
||||
this.options.clickTimeThreshold);
|
||||
} else {
|
||||
handleTouchStart();
|
||||
}
|
||||
},
|
||||
|
||||
onMove: function(x, y) {
|
||||
var dx = x - this._startX;
|
||||
var dy = y - this._startY;
|
||||
|
||||
if (this._waitForClickTimer) {
|
||||
var clickMoveThreshold = this.options.clickMoveThreshold;
|
||||
if (Math.abs(dx) <= clickMoveThreshold &&
|
||||
Math.abs(dy) <= clickMoveThreshold) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this._waitForClickTimer);
|
||||
this._waitForClickTimer = null;
|
||||
this.handleTouch('touchstart', 0, 0);
|
||||
}
|
||||
|
||||
this.handleTouch('touchmove', dx, dy);
|
||||
},
|
||||
|
||||
onEnd: function(x, y) {
|
||||
var dx = x - this._startX;
|
||||
var dy = y - this._startY;
|
||||
|
||||
if (this._waitForClickTimer) {
|
||||
clearTimeout(this._waitForClickTimer);
|
||||
this._waitForClickTimer = null;
|
||||
|
||||
if (this.options.dblClickTimeThreshold) {
|
||||
if (this._pendingClickTimer) {
|
||||
clearTimeout(this._pendingClickTimer);
|
||||
this._pendingClickTimer = null;
|
||||
this.handleTouch('dblclick');
|
||||
} else {
|
||||
this._pendingClickTimer = setTimeout(function() {
|
||||
this._pendingClickTimer = null;
|
||||
this.handleTouch('click');
|
||||
}.bind(this), this.options.dblClickTimeThreshold);
|
||||
}
|
||||
} else {
|
||||
this.handleTouch('click');
|
||||
}
|
||||
} else {
|
||||
var direction;
|
||||
var distance = Math.round(Math.sqrt(dx * dx + dy * dy));
|
||||
if (distance >= this.options.swipeMoveThreshold) {
|
||||
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
|
||||
if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
if (angle >= 315 || angle < 45) {
|
||||
direction = 'right';
|
||||
} else if (angle >= 45 && angle < 135) {
|
||||
direction = 'down';
|
||||
} else if (angle >= 135 && angle < 225) {
|
||||
direction = 'left';
|
||||
} else if (angle >= 225 && angle < 315) {
|
||||
direction = 'up';
|
||||
}
|
||||
}
|
||||
|
||||
this.handleTouch('touchend', dx, dy, direction);
|
||||
}
|
||||
|
||||
if (this.options.touchingClass) {
|
||||
this._element.classList.remove(this.options.touchingClass);
|
||||
}
|
||||
},
|
||||
|
||||
handleTouch: function(type, dx, dy, swipe) {
|
||||
if (DEBUG) {
|
||||
console.log('[PanelElement] handling ' + type);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'touchstart':
|
||||
this._prevDx = undefined;
|
||||
this._prevDy = undefined;
|
||||
// A simple unique identifier for server to distinguish touch events
|
||||
// between different clients.
|
||||
this._eventIdentifier =
|
||||
String(Date.now()) + String(Math.floor(Math.random() * 100));
|
||||
|
||||
this._handler(type, {
|
||||
width: this._panelWidth,
|
||||
height: this._panelHeight,
|
||||
identifier: this._eventIdentifier
|
||||
});
|
||||
|
||||
this._reportTimer = setInterval(function() {
|
||||
if (this._pendingMoveEvent) {
|
||||
this._handler(
|
||||
this._pendingMoveEvent.type,
|
||||
this._pendingMoveEvent.detail
|
||||
);
|
||||
this._pendingMoveEvent = null;
|
||||
}
|
||||
}.bind(this), this.options.touchReportPeriod);
|
||||
|
||||
break;
|
||||
case 'touchmove':
|
||||
if (dx === this._prevDx && dy === this._prevDy) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prevDx = dx;
|
||||
this._prevDy = dy;
|
||||
|
||||
this._pendingMoveEvent = {
|
||||
type: type,
|
||||
detail: {
|
||||
dx: dx,
|
||||
dy: dy,
|
||||
identifier: this._eventIdentifier,
|
||||
duration: Date.now() - this._startTime
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case 'touchend':
|
||||
this._pendingMoveEvent = null;
|
||||
clearInterval(this._reportTimer);
|
||||
this._reportTimer = null;
|
||||
|
||||
this._handler(type, {
|
||||
dx: dx,
|
||||
dy: dy,
|
||||
identifier: this._eventIdentifier,
|
||||
duration: Date.now() - this._startTime,
|
||||
swipe: swipe
|
||||
});
|
||||
|
||||
this._eventIdentifier = null;
|
||||
break;
|
||||
case 'click':
|
||||
case 'dblclick':
|
||||
this._handler(type, {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.PanelElement = PanelElement;
|
||||
}(window));
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# client
|
||||
client-title=TV Remote Control
|
||||
input.placeholder=type here and send
|
||||
send=Send
|
||||
session-expired=Your pairing session has expired. Please try again.
|
||||
|
||||
# pairing
|
||||
pairing-title=Remote Control Pairing
|
||||
pairing-information-1=Please enter the pairing code shown on the TV and then tap Connect.
|
||||
pairing-information-2=You will only be asked to enter pairing code on the first connection from a new device.
|
||||
connect=Connect
|
||||
pin-code-expired-message=The PIN code has expired. Please try again.
|
||||
restart-pairing=Restart
|
||||
connect-success=Connected successfully!
|
||||
connect-error=ERROR: {{status}}!
|
||||
connect-error-invalid-response=ERROR: Invalid response!
|
||||
wrong-pin=Wrong PIN code. Please try again.
|
||||
22
gaia/tv_apps/remote-control-client/manifest.webapp
Executable file
22
gaia/tv_apps/remote-control-client/manifest.webapp
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "Remote Control Client Page",
|
||||
"description": "Remote Control Client Page",
|
||||
"launch_path": "/index.html",
|
||||
"type": "certified",
|
||||
"role": "system",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"locales": {
|
||||
"en-US": {
|
||||
"name": "Remote Control Client Page",
|
||||
"description": "Remote Control Client Page"
|
||||
}
|
||||
},
|
||||
"default_locale": "en-US",
|
||||
"icons": {
|
||||
"128": "/style/icons/blank.png"
|
||||
},
|
||||
"orientation": "landscape"
|
||||
}
|
||||
37
gaia/tv_apps/remote-control-client/pairing.html
Executable file
37
gaia/tv_apps/remote-control-client/pairing.html
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>TV remote control</title>
|
||||
<link rel="stylesheet" type="text/css" href="style/main.css">
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/pairing.js"></script>
|
||||
<script src="bower_components/l20n/dist/webcompat/l20n.js"></script>
|
||||
<!-- L10n properties -->
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="locales/remote-control-client.{locale}.properties">
|
||||
</head>
|
||||
<body class="pairing">
|
||||
<div id="pairing-container">
|
||||
<h1 data-l10n-id="pairing-title"></h1>
|
||||
<div id="pairing-information">
|
||||
<p data-l10n-id="pairing-information-1"></p>
|
||||
<p data-l10n-id="pairing-information-2"></p>
|
||||
</div>
|
||||
<div id="pairing-code">
|
||||
<span class="code placeholder" tabindex="1"></span>
|
||||
<span class="code placeholder" tabindex="2"></span>
|
||||
<span class="code placeholder" tabindex="3"></span>
|
||||
<span class="code placeholder" tabindex="4"></span>
|
||||
<input id="input-mask" type="number" min="0" max="9" pattern="\d*" tabindex="-1">
|
||||
</div>
|
||||
<div id="pairing-message"></div>
|
||||
<div id="pairing-button-area">
|
||||
<button id="connect" tabindex="5" data-l10n-id="connect"></button>
|
||||
<button id="restart-pairing" tabindex="6" data-l10n-id="restart-pairing"></button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
gaia/tv_apps/remote-control-client/style/icons/blank.png
Executable file
BIN
gaia/tv_apps/remote-control-client/style/icons/blank.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
gaia/tv_apps/remote-control-client/style/images/btn_back.png
Executable file
BIN
gaia/tv_apps/remote-control-client/style/images/btn_back.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 692 B |
BIN
gaia/tv_apps/remote-control-client/style/images/btn_home.png
Executable file
BIN
gaia/tv_apps/remote-control-client/style/images/btn_home.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 763 B |
BIN
gaia/tv_apps/remote-control-client/style/images/btn_option.png
Executable file
BIN
gaia/tv_apps/remote-control-client/style/images/btn_option.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 449 B |
BIN
gaia/tv_apps/remote-control-client/style/images/scrollbar.png
Executable file
BIN
gaia/tv_apps/remote-control-client/style/images/scrollbar.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 87 B |
381
gaia/tv_apps/remote-control-client/style/main.css
Executable file
381
gaia/tv_apps/remote-control-client/style/main.css
Executable file
|
|
@ -0,0 +1,381 @@
|
|||
html {
|
||||
font-size: 10px;
|
||||
font-family: "Fira Sans", sans-serif;
|
||||
height: 100%;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body, section, input, div {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:focus, :active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**********
|
||||
* Client *
|
||||
**********/
|
||||
|
||||
body.client {
|
||||
background-color: #353535;
|
||||
}
|
||||
|
||||
.client section {
|
||||
font-size: 0;
|
||||
margin: 17px 11px 0 11px;
|
||||
}
|
||||
|
||||
.client section > * {
|
||||
vertical-align: middle;
|
||||
font-size: 1rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.client h1 {
|
||||
margin: 0;
|
||||
height: 52px;
|
||||
line-height: 52px;
|
||||
font-size: 23px;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
background-color: #242424;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.client .button {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.client .button:active {
|
||||
left: 1px;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
#section-input {
|
||||
height: 34px;
|
||||
border-radius: 17px;
|
||||
background-color: #1f1f1f;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#input-string {
|
||||
-webkit-appearance: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-right: 1px solid #313131;
|
||||
border-radius: 0;
|
||||
width: calc(100% - 60px);
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 17px;
|
||||
padding: 0 3px 0 15px;
|
||||
color: #909090;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#input-string::-moz-placeholder {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
color: #5f5f5f;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#input-string::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
color: #5f5f5f;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#input-string::-ms-input-placeholder {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
color: #5f5f5f;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#send-string {
|
||||
width: 60px;
|
||||
height: 100%;
|
||||
line-height: 34px;
|
||||
text-align: center;
|
||||
color: #00aacc;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#section-touch-panel {
|
||||
position: relative;
|
||||
height: calc(100% - 210px);
|
||||
border: 1px solid #4c4c4c;
|
||||
border-radius: 15px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
/* inner border */
|
||||
#section-touch-panel:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: calc(100% - 2px);
|
||||
width: calc(100% - 2px);
|
||||
border: 1px solid #252525;
|
||||
border-radius: 15px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: #545454;
|
||||
height: 100%;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.panel.touching {
|
||||
background-color: #4a4a4a;
|
||||
}
|
||||
|
||||
#touch-panel {
|
||||
width: calc(100% - 27px);
|
||||
border-top-left-radius: 15px;
|
||||
border-bottom-left-radius: 15px;
|
||||
}
|
||||
|
||||
#scroll-panel {
|
||||
width: 27px;
|
||||
border-top-right-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
background-image: url('images/scrollbar.png');
|
||||
}
|
||||
|
||||
/* extend the touching area */
|
||||
#scroll-panel:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 39px
|
||||
}
|
||||
|
||||
#section-buttons {
|
||||
width: 290px;
|
||||
margin: 25px auto 0 auto;
|
||||
text-align: justify;
|
||||
text-justify: inter-ideograph;
|
||||
-ms-text-justify: inter-ideograph;
|
||||
}
|
||||
|
||||
/* Workaround "text-align: justify" on Chrome */
|
||||
#section-buttons:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#section-buttons .button {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
#section-buttons .button:active {
|
||||
background-color: #3d3d3d;
|
||||
}
|
||||
|
||||
#button-back {
|
||||
background-image: url('images/btn_back.png');
|
||||
}
|
||||
|
||||
#button-home {
|
||||
background-image: url('images/btn_home.png');
|
||||
}
|
||||
|
||||
#button-option {
|
||||
background-image: url('images/btn_option.png');
|
||||
}
|
||||
|
||||
/***********
|
||||
* Pairing *
|
||||
***********/
|
||||
|
||||
body.pairing {
|
||||
background-color: #353535;
|
||||
padding: 24px 18px 0 18px;
|
||||
}
|
||||
|
||||
.pairing h1 {
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-size: 34px;
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
line-height: 45px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#pairing-container {
|
||||
margin: 0 auto 14px auto;
|
||||
width: 100%;
|
||||
min-width: 284px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
#pairing-information {
|
||||
color: #858585;
|
||||
font-size: 17px;
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
margin-top: 28px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#pairing-information p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
#pairing-code {
|
||||
position: relative;
|
||||
width: 275px;
|
||||
text-align: center;
|
||||
margin: 20px auto 0 auto;
|
||||
font-size: 0;
|
||||
text-align: justify;
|
||||
text-justify: inter-ideograph;
|
||||
-ms-text-justify: inter-ideograph;
|
||||
}
|
||||
|
||||
/* Workaround "text-align: justify" on Chrome */
|
||||
#pairing-code:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#pairing-code .code, #input-mask {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
line-height: 52px;
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #00caf2;
|
||||
color: white;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
#pairing-code .placeholder:after {
|
||||
content: '-';
|
||||
color: #909ca7;
|
||||
}
|
||||
|
||||
#input-mask {
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
outline: none;
|
||||
background-color: #353535;
|
||||
background-image: none; /* remove inner box shadow on mobile Firefox */
|
||||
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
-ms-appearance: textfield;
|
||||
-o-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
#input-mask.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#input-mask::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#pairing-message {
|
||||
color: #00caf2;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
margin: 16px auto 0 auto;
|
||||
padding: 0 4px;
|
||||
height: 60px;
|
||||
width: 284px;
|
||||
}
|
||||
|
||||
#pairing-message.error {
|
||||
color: #e2443a;
|
||||
}
|
||||
|
||||
#pairing-button-area {
|
||||
width: 284px;
|
||||
margin: 0px auto 0 auto;
|
||||
}
|
||||
|
||||
#pairing-button-area button {
|
||||
-webkit-appearance: none;
|
||||
|
||||
background-color: #00caf2;
|
||||
border: 0;
|
||||
border-radius: 25px;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pairing-button-area button:active {
|
||||
background-color: #00758c;
|
||||
}
|
||||
|
||||
#restart-pairing {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pin-code-expired #pairing-code {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.pin-code-expired #connect {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pin-code-expired #restart-pairing {
|
||||
display: block;
|
||||
}
|
||||
Loading…
Reference in a new issue