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.
|
|
@ -0,0 +1,5 @@
|
|||
The contents of these fake contacts are licensed under the Creative Commons Attribution-Share Alike 3.0 United States License.
|
||||
|
||||
Original source attributed to: Fake Name Generator (http://www.fakenamegenerator.com/)
|
||||
|
||||
For more information, please see http://creativecommons.org/licenses/by-sa/3.0/us/.
|
||||
19237
assets/webapps/uitest.gaiamobile.org/data/fakecontacts/fakecontacts.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"name": ["DJ Trasto"],
|
||||
"givenName": ["DJ"],
|
||||
"familyName": ["Trasto"],
|
||||
"category": ["twitter"],
|
||||
"bday": "1979-11-02"
|
||||
},
|
||||
{
|
||||
"name": ["Armin van Beer"],
|
||||
"givenName": ["Armin"],
|
||||
"familyName": ["van Beer"],
|
||||
"category": ["facebook","twitter"],
|
||||
"org": ["State of Web"],
|
||||
"bday": "1974-03-15"
|
||||
},
|
||||
{
|
||||
"name": ["Spaniard Web Mafia"],
|
||||
"givenName": ["Spaniard"],
|
||||
"familyName": ["Web Mafia"],
|
||||
"category": ["twitter"],
|
||||
"org": ["Libraries.JS"]
|
||||
},
|
||||
{
|
||||
"name": ["David Siestta"],
|
||||
"givenName": ["David"],
|
||||
"familyName": ["Siestta"],
|
||||
"category": ["facebook"],
|
||||
"bday": "1980-04-20"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/webapps/uitest.gaiamobile.org/data/ringtones/classic.ogg
Normal file
BIN
assets/webapps/uitest.gaiamobile.org/data/video/meetthecubs.webm
Normal file
BIN
assets/webapps/uitest.gaiamobile.org/data/video/stapler.3gp
Normal file
152
assets/webapps/uitest.gaiamobile.org/index.html
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<title>UI tests</title>
|
||||
<!-- We don't need localization in this app -->
|
||||
<link rel="stylesheet" type="text/css" href="style/uitest.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/tabs.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/lists.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/lists.css">
|
||||
|
||||
<!-- Gaia Components -->
|
||||
<link rel="stylesheet" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/style.css">
|
||||
<link rel="stylesheet" href="shared/elements/gaia-icons/gaia-icons.css">
|
||||
|
||||
<!-- Gaia Components -->
|
||||
<script src="shared/elements/config.js"></script>
|
||||
<script src="shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/uitest.js"></script>
|
||||
<!-- <script src="shared/js/mime_mapper.js"></script> -->
|
||||
<!-- <script src="shared/js/settings_listener.js"></script> -->
|
||||
<!-- <script src="shared/js/download/download_formatter.js"></script> -->
|
||||
<!-- <script src="shared/js/download/download_helper.js"></script> -->
|
||||
<!-- <script src="shared/js/download/download_store.js"></script> -->
|
||||
<script defer="" src="shared/js/lazy_loader.js"></script>
|
||||
<script defer="" src="shared/js/accessibility_helper.js"></script>
|
||||
<meta content="ar:201601260729, bg:201601260729, bn-BD:201601260729, ca:201601260729, cs:201601260729, da:201601260729, de:201601260729, el:201601260729, en-US:201601260729, es:201601260729, eu:201601260729, fr:201601260729, gl:201601260729, hr:201601260729, hu:201601260729, it:201601260729, ja:201601260729, km:201601260729, ko:201601260729, lt:201601260729, mk:201601260729, ms:201601260729, ne-NP:201601260729, nl:201601260729, pa:201601260729, pl:201601260729, pt-BR:201601260729, ro:201601260729, ru:201601260729, sk:201601260729, sq:201601260729, sr-Cyrl:201601260729, sr-Latn:201601260729, sv-SE:201601260729, tr:201601260729, zh-CN:201601260729, zh-TW:201601260729" name="availableLanguages"><meta content="en-US" name="defaultLanguage"><meta content="2.5" name="appVersion"></head>
|
||||
<body class="theme-productivity">
|
||||
<div class="panel list-panel">
|
||||
<div id="UI-tests" class="page">
|
||||
<section role="region">
|
||||
<gaia-header>
|
||||
<h1>UI Tests</h1>
|
||||
</gaia-header>
|
||||
</section>
|
||||
<section data-type="list">
|
||||
<ul class="sublist">
|
||||
<li><a href="#test=UI/alert">Alert/Prompt</a></li>
|
||||
<li><a href="#test=UI/canvas">Canvas Tag</a></li>
|
||||
<li><a href="#test=UI/contextmenu">Contextmenu</a></li>
|
||||
<li><a href="#test=UI/css_dimension">CSS dimension</a></li>
|
||||
<li><a href="#test=UI/empty">Empty</a></li>
|
||||
<li><a href="#test=UI/fullscreen">mozRequestFullScreen</a></li>
|
||||
<li><a href="#test=UI/fullscreen_controls">Fullscreen Ctr</a></li>
|
||||
<li><a href="#test=UI/keyboard">Keyboard</a></li>
|
||||
<li><a href="#test=UI/newnotification">new Notification()</a></li>
|
||||
<li><a href="#test=UI/notification">mozNotification</a></li>
|
||||
<li><a href="#test=UI/open">window.open</a></li>
|
||||
<li><a href="#test=UI/scrollbar">Scroll bar</a></li>
|
||||
<li><a href="#test=UI/select">Select</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<div id="API-tests" class="page invisible">
|
||||
<section role="region">
|
||||
<gaia-header>
|
||||
<h1>API Tests</h1>
|
||||
</gaia-header>
|
||||
</section>
|
||||
<section data-type="list">
|
||||
<ul class="sublist">
|
||||
<li><a href="#test=API/audiotag">audio tag</a></li>
|
||||
<li><a href="#test=API/browseractivities">Browser Activities</a></li>
|
||||
<li><a href="#test=API/composeemail">Compose Email</a></li>
|
||||
<li><a href="#test=API/contacts">Contacts</a></li>
|
||||
<li><a href="#test=API/copypaste">Copy Paste</a></li>
|
||||
<li><a href="#test=API/csp">CSP Warning</a></li>
|
||||
<li><a href="#test=API/dialnumber">Dial Number</a></li>
|
||||
<li><a href="#test=API/downloads">Downloads</a></li>
|
||||
<li><a href="#test=API/geolocation">Geolocation</a></li>
|
||||
<li><a href="#test=API/getusermedia">getUserMedia</a></li>
|
||||
<li><a href="#test=API/httpauthentication">HTTP Auth</a></li>
|
||||
<li><a href="#test=API/fxa" id="mozId-fxa">Firefox Accounts mozId</a></li>
|
||||
<li><a href="#test=API/identity" id="mozId-persona">Persona mozId</a></li>
|
||||
<li><a href="#test=API/identity-get" id="mozId-persona-get">Persona mozId.get</a></li>
|
||||
<li><a href="#test=API/inlineactivities">Inline Activities</a></li>
|
||||
<li><a href="#test=API/multiactivities">Multi Activities</a></li>
|
||||
<li><a href="#test=API/openaudio">Open Audio</a></li>
|
||||
<li><a href="#test=API/openvideo">Open Video</a></li>
|
||||
<li><a href="#test=API/orientation">mozLockOrientation</a></li>
|
||||
<li><a href="#test=API/pay">navigator.mozPay</a></li>
|
||||
<li><a href="#test=API/pickphoto">Pick Photo</a></li>
|
||||
<li><a href="#test=API/picktype">Pick Photo/Video</a></li>
|
||||
<li><a href="#test=API/pickvideo">Pick Video</a></li>
|
||||
<li><a href="#test=API/pickdownload">Pick Download</a></li>
|
||||
<li><a href="#test=API/presentation_api">Presentation API</a></li>
|
||||
<li><a href="#test=API/savebookmark">Save bookmark</a></li>
|
||||
<li><a href="#test=API/sendsms">Send SMS</a></li>
|
||||
<li><a href="#test=API/shareimage">Share Image</a></li>
|
||||
<li><a href="#test=API/shareurl">Share URL</a></li>
|
||||
<li><a href="#test=API/viewphoto">View Photo</a></li>
|
||||
<li><a href="#test=API/search">Search keyword</a></li>
|
||||
<li><a href="#test=API/secure_element">Secure Element (Wallet)</a></li><li>
|
||||
</li></ul>
|
||||
</section>
|
||||
</div>
|
||||
<div id="HW-tests" class="page invisible">
|
||||
<section role="region">
|
||||
<gaia-header>
|
||||
<h1 class="header-title">HW Tests</h1>
|
||||
</gaia-header>
|
||||
</section>
|
||||
<section data-type="list">
|
||||
<ul class="sublist">
|
||||
<li><a href="#test=HW/battery">Battery</a></li>
|
||||
<li><a href="#test=HW/bluetooth">Bluetooth</a></li>
|
||||
<li><a href="#test=HW/camera">Camera</a></li>
|
||||
<li><a href="#test=HW/fmradio">FM radio</a></li>
|
||||
<li><a href="#test=HW/headphone">Headphone</a></li>
|
||||
<li><a href="#test=HW/lcd">LCD</a></li>
|
||||
<li><a href="#test=HW/physicalkeys">Physical keys</a></li>
|
||||
<li><a href="#test=HW/radio">Radio information</a></li>
|
||||
<li><a href="#test=HW/ringer">Ringer</a></li>
|
||||
<li><a href="#test=HW/sdcards">SD cards</a></li>
|
||||
<li><a href="#test=HW/sensors">Sensors</a></li>
|
||||
<li><a href="#test=HW/touchscreen">Touch screen</a></li>
|
||||
<li><a href="#test=HW/vibrator">Vibrator</a></li>
|
||||
<li><a href="#test=HW/wifi">Wi-Fi</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<div id="tabs" class="tabs">
|
||||
<ul role="tablist" class="bb-tablist" data-items="3">
|
||||
<li id="panel1" role="presentation">
|
||||
<a href="#UI" id="UI" role="tab" aria-controls="UIpanel">UI</a>
|
||||
<div id="UIpanel" class="bb-tabpanel" role="tabpanel" aria-labelledby="UI"></div>
|
||||
</li>
|
||||
<li id="panel2" role="presentation">
|
||||
<a href="#API" id="API" role="tab" aria-controls="APIpanel">API</a>
|
||||
<div id="APIpanel" class="bb-tabpanel" role="tabpanel" aria-labelledby="API"></div>
|
||||
</li>
|
||||
<li id="panel3" role="presentation">
|
||||
<a href="#HW" id="HW" role="tab" aria-controls="HWpanel">HW</a>
|
||||
<div id="HWpanel" class="bb-tabpanel" role="tabpanel" aria-labelledby="HW"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="test-panel" class="panel test-panel">
|
||||
<section role="region">
|
||||
<gaia-header id="test-panel-header" action="back">
|
||||
<h1 id="test-panel-title" class="header-title"></h1>
|
||||
</gaia-header>
|
||||
</section>
|
||||
<iframe id="test-iframe" mozallowfullscreen="true"></iframe>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
20
assets/webapps/uitest.gaiamobile.org/js/API/audiotag.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
var clickHandlers = {
|
||||
'play': function play() {
|
||||
var src = this.dataset.src;
|
||||
var ringtonePlayer = new Audio();
|
||||
ringtonePlayer.loop = true;
|
||||
var selectedSound = '../../data/ringtones/' + src;
|
||||
ringtonePlayer.src = selectedSound;
|
||||
ringtonePlayer.play();
|
||||
window.setTimeout(function _pauseRingtone() {
|
||||
ringtonePlayer.pause();
|
||||
}, 20000);
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id || evt.target.dataset.fn])
|
||||
clickHandlers[evt.target.id || evt.target.dataset.fn].call(evt.target, evt);
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
var clickHandlers = {
|
||||
'go': function _go() {
|
||||
var URL = document.getElementById('URL').value;
|
||||
var a = new MozActivity({ name: 'view', data: {type: 'url', url: URL }});
|
||||
a.onsuccess = function() { alert('Success!'); };
|
||||
a.onerror = function() { alert('Failure going to URL'); };
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id || evt.target.dataset.fn])
|
||||
clickHandlers[evt.target.id || evt.target.dataset.fn].call(this, evt);
|
||||
});
|
||||
12
assets/webapps/uitest.gaiamobile.org/js/API/composeemail.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
var composeEmail = document.querySelector('#compose-email');
|
||||
if (composeEmail) {
|
||||
composeEmail.onclick = function() {
|
||||
var createEmail = new MozActivity({
|
||||
name: 'new', // Possibly compose-mail in future versions
|
||||
data: {
|
||||
type: 'mail',
|
||||
url: 'mailto:example@example.org'
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
370
assets/webapps/uitest.gaiamobile.org/js/API/contacts.js
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
'use stricts';
|
||||
|
||||
/**
|
||||
* Just a couple of testing tools for the Contacts app.
|
||||
*
|
||||
* Right now, it just allows you to insert a large number of fake contacts
|
||||
* into the database, and then clear the database.
|
||||
*/
|
||||
|
||||
var ContactsTest = {
|
||||
get loadButton() {
|
||||
delete this.loadButton;
|
||||
return this.loadButton = document.getElementById('insert-contacts');
|
||||
},
|
||||
|
||||
get clearButton() {
|
||||
delete this.clearButton;
|
||||
return this.clearButton = document.getElementById('clear-contacts');
|
||||
},
|
||||
|
||||
get getButton() {
|
||||
delete this.getButton;
|
||||
return this.getButton = document.getElementById('get-contacts');
|
||||
},
|
||||
|
||||
get pickActivityButton() {
|
||||
delete this.pickActivityButton;
|
||||
return this.pickActivityButton = document.getElementById('activities-pick');
|
||||
},
|
||||
|
||||
get newActivityButton() {
|
||||
delete this.newActivityButton;
|
||||
return this.newActivityButton = document.getElementById('activities-new');
|
||||
},
|
||||
get updateExistingActivityButton() {
|
||||
delete this.updateExistingActivityButton;
|
||||
return this.updateExistingActivityButton =
|
||||
document.getElementById('activities-update');
|
||||
},
|
||||
|
||||
get updateExistingActivityEmailButton() {
|
||||
delete this.updateExistingActivityEmailButton;
|
||||
return this.updateExistingActivityEmailButton =
|
||||
document.getElementById('activities-update-email');
|
||||
},
|
||||
|
||||
get updateExistingActivityEmailPhoneButton() {
|
||||
delete this.updateExistingActivityEmailPhoneButton;
|
||||
return this.updateExistingActivityEmailPhoneButton =
|
||||
document.getElementById('activities-update-email-phone');
|
||||
},
|
||||
|
||||
get newWithDataActivityButton() {
|
||||
delete this.newWithDataActivityButton;
|
||||
return this.newWithDataActivityButton =
|
||||
document.getElementById('activities-new-data');
|
||||
},
|
||||
|
||||
get insertSocialContacts() {
|
||||
delete this.insertSocialContacts;
|
||||
return this.insertSocialContacts =
|
||||
document.getElementById('insert-social-contacts');
|
||||
},
|
||||
|
||||
|
||||
init: function ct_init() {
|
||||
this.loadButton.addEventListener('click', this.loadContacts.bind(this));
|
||||
this.clearButton.addEventListener('click', this.clearContacts.bind(this));
|
||||
this.getButton.addEventListener('click', this.getContacts.bind(this));
|
||||
this.pickActivityButton.addEventListener('click',
|
||||
this.pickActivity.bind(this));
|
||||
this.newActivityButton.addEventListener('click',
|
||||
this.newActivity.bind(this));
|
||||
this.updateExistingActivityButton.addEventListener('click',
|
||||
this.updateExistingActivity.bind(this));
|
||||
this.updateExistingActivityEmailButton.addEventListener('click',
|
||||
this.updateExistingActivityEmail.bind(this));
|
||||
|
||||
this.updateExistingActivityEmailPhoneButton.addEventListener('click',
|
||||
this.updateExistingActivityEmailPhone.bind(this));
|
||||
|
||||
this.newWithDataActivityButton.addEventListener('click',
|
||||
this.newWithDataActivity.bind(this));
|
||||
|
||||
this.insertSocialContacts.addEventListener('click',
|
||||
this.finsertSocialContacts.bind(this));
|
||||
},
|
||||
|
||||
uninit: function ct_uninit() {
|
||||
this.loadButton.removeEventListener('click', this.loadContacts.bind(this));
|
||||
this.clearButton.removeEventListener('click',
|
||||
this.clearContacts.bind(this));
|
||||
this.getButton.removeEventListener('click', this.getContacts.bind(this));
|
||||
this.newActivityButton.removeEventListener('click',
|
||||
this.newActivity.bind(this));
|
||||
this.updateExistingActivityButton.removeEventListener('click',
|
||||
this.updateExistingActivity.bind(this));
|
||||
},
|
||||
|
||||
clearContacts: function ct_clearContacts() {
|
||||
if (!confirm('This will wipe out ALL of the contacts in the database. ' +
|
||||
'Are you sure?'))
|
||||
return;
|
||||
|
||||
// Ok, we're really doing this.
|
||||
var req = window.navigator.mozContacts.clear();
|
||||
req.onsuccess = function() {
|
||||
alert('Contacts deleted.');
|
||||
};
|
||||
req.onerror = function() {
|
||||
alert('Problem deleting contacts');
|
||||
};
|
||||
},
|
||||
|
||||
getContacts: function ct_getContacts() {
|
||||
var options = {
|
||||
sortBy: 'familyName',
|
||||
sortOrder: 'ascending'
|
||||
};
|
||||
var start = new Date();
|
||||
var req = window.navigator.mozContacts.find(options);
|
||||
req.onsuccess = function() {
|
||||
var duration = new Date() - start;
|
||||
alert('Contacts received: ' + duration + 'msec');
|
||||
};
|
||||
req.onerror = function() {
|
||||
alert('Problem receiving contacts');
|
||||
};
|
||||
},
|
||||
|
||||
setContactId: function ct_setContactId(id) {
|
||||
this.contactId = id;
|
||||
},
|
||||
|
||||
getContactId: function ct_getContactId() {
|
||||
return this.contactId;
|
||||
},
|
||||
|
||||
pickActivity: function ct_pickActivity() {
|
||||
var activity = new MozActivity({
|
||||
name: 'pick',
|
||||
data: {
|
||||
type: 'webcontacts/contact'
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
activity.onsuccess = function() {
|
||||
var number = this.result.number;
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Picked contact with number: ' + number;
|
||||
};
|
||||
|
||||
activity.onerror = function() {
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Activity canceled';
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
newActivity: function ct_newActivity() {
|
||||
var activity = new MozActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'webcontacts/contact'
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
activity.onsuccess = function() {
|
||||
var contact = this.result.contact;
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'New contact' + ' create with id: ' + contact.id;
|
||||
self.setContactId(contact.id);
|
||||
};
|
||||
|
||||
activity.onerror = function() {
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Activity canceled';
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
updateExistingActivityEmail: function ct_updateExistingActivityEmail() {
|
||||
var activity = new MozActivity({
|
||||
name: 'update',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: {
|
||||
'email': 'test@test.com'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
activity.onsuccess = function() {
|
||||
var contact = this.result.contact;
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'updated contact' + ' update with id: ' + contact.id;
|
||||
self.setContactId(contact.id);
|
||||
};
|
||||
|
||||
activity.onerror = function() {
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Activity canceled';
|
||||
};
|
||||
},
|
||||
|
||||
updateExistingActivityEmailPhone: function() {
|
||||
var activity = new MozActivity({
|
||||
name: 'update',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: {
|
||||
'email': 'test@test.com',
|
||||
'tel': '21312132'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
activity.onsuccess = function() {
|
||||
var contact = this.result.contact;
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'updated contact' + ' update with id: ' + contact.id;
|
||||
self.setContactId(contact.id);
|
||||
};
|
||||
|
||||
activity.onerror = function() {
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Activity canceled';
|
||||
};
|
||||
},
|
||||
|
||||
updateExistingActivity: function ct_updateExistingActivity() {
|
||||
var activity = new MozActivity({
|
||||
name: 'update',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: {
|
||||
'tel': '4087469720'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
activity.onsuccess = function() {
|
||||
var contact = this.result.contact;
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'updated contact' + ' update with id: ' + contact.id;
|
||||
self.setContactId(contact.id);
|
||||
};
|
||||
|
||||
activity.onerror = function() {
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Activity canceled';
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
newWithDataActivity: function ct_newActivity() {
|
||||
var activity = new MozActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: {
|
||||
'tel': '555-555-555',
|
||||
'email': 'cool-hunter@telefonica.es',
|
||||
'address': 'San Francisco',
|
||||
'note': 'This is a note',
|
||||
'giveName': 'John',
|
||||
'familyName': 'Orlock',
|
||||
'company': 'Lost Industries'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
activity.onsuccess = function() {
|
||||
var contact = this.result.contact;
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'New contact' + ' create with id: ' + contact.id;
|
||||
self.setContactId(contact.id);
|
||||
};
|
||||
|
||||
activity.onerror = function() {
|
||||
document.getElementById('activities-result').innerHTML =
|
||||
'Activity canceled';
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
loadContacts: function ct_loadContacts() {
|
||||
var req = new XMLHttpRequest();
|
||||
req.overrideMimeType('application/json');
|
||||
req.open('GET', '../../data/fakecontacts/fakecontacts.json', true);
|
||||
req.onreadystatechange = function() {
|
||||
// We will get a 0 status if the app is in app://
|
||||
if (req.readyState === 4 && (req.status === 200 ||
|
||||
req.status === 0)) {
|
||||
var contacts = JSON.parse(req.responseText);
|
||||
this._insertContacts(contacts);
|
||||
}
|
||||
};
|
||||
req.onreadystatechange = req.onreadystatechange.bind(this);
|
||||
|
||||
this.loadButton.disabled = true;
|
||||
req.send(null);
|
||||
},
|
||||
|
||||
_insertContacts: function ct_insertContacts(aContacts) {
|
||||
var self = this;
|
||||
|
||||
var cs = new ContactsSaver(aContacts);
|
||||
cs.start();
|
||||
|
||||
cs.onsuccess = function() {
|
||||
self.loadButton.disabled = false;
|
||||
};
|
||||
cs.onsaved = function(n) {
|
||||
self._setInsertionCount(n, aContacts.length);
|
||||
};
|
||||
cs.onerror = function(c, e) {
|
||||
Components.utils.reportError('Could not add contact with name: ' +
|
||||
c.familyName[0]);
|
||||
};
|
||||
},
|
||||
|
||||
_setInsertionCount: function ct__setInsertionCount(aSoFar, aTotal) {
|
||||
var insertionEl = document.getElementById('insertion-count');
|
||||
insertionEl.textContent = aSoFar + ' / ' + aTotal;
|
||||
},
|
||||
|
||||
_error: function ct__error(c) {
|
||||
Components.utils.reportError('Could not add contact with name: ' +
|
||||
c.familyName[0]);
|
||||
},
|
||||
|
||||
finsertSocialContacts: function ct_finsertSocialContacts() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType('application/json');
|
||||
xhr.open('GET', '../../data/fakesocialcontacts/contacts_social.json', true);
|
||||
xhr.onload = function(e) {
|
||||
// We will get a 0 status if the app is in app://
|
||||
if (xhr.status === 200 || xhr.status === 0) {
|
||||
var cdata = JSON.parse(xhr.responseText);
|
||||
this._insertSocialContacts(cdata.data);
|
||||
}
|
||||
};
|
||||
xhr.onload = xhr.onload.bind(this);
|
||||
|
||||
xhr.send(null);
|
||||
},
|
||||
|
||||
_insertSocialContacts: function ct__insertSocialContacts(contacts) {
|
||||
var cs = new ContactsSaver(contacts);
|
||||
|
||||
cs.start();
|
||||
|
||||
var self = this;
|
||||
|
||||
cs.onsuccess = function() { window.alert('Added!'); };
|
||||
cs.onerror = function(e, c) {
|
||||
self._error(c);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', ContactsTest.init.bind(ContactsTest));
|
||||
window.addEventListener('unload', ContactsTest.uninit.bind(ContactsTest));
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
'use strict';
|
||||
|
||||
function ContactsSaver(data) {
|
||||
this.data = data;
|
||||
var next = 0;
|
||||
var self = this;
|
||||
|
||||
this.start = function() {
|
||||
saveContact(data[0]);
|
||||
};
|
||||
|
||||
function saveContact(cdata) {
|
||||
if (cdata.bday) {
|
||||
cdata.bday = new Date(cdata.bday);
|
||||
}
|
||||
if (cdata.anniversary) {
|
||||
cdata.anniversary = new Date(cdata.anniversary);
|
||||
}
|
||||
var contact = new mozContact(cdata);
|
||||
var req = navigator.mozContacts.save(contact);
|
||||
req.onsuccess = function(e) {
|
||||
if (typeof self.onsaved === 'function') {
|
||||
self.onsaved(next + 1);
|
||||
}
|
||||
continuee();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
if (typeof self.onerror === 'function') {
|
||||
self.onerror(self.data[next], e.target.error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function continuee() {
|
||||
next++;
|
||||
if (next < self.data.length) {
|
||||
saveContact(self.data[next]);
|
||||
}
|
||||
else {
|
||||
// End has been reached
|
||||
if (typeof self.onsuccess === 'function') {
|
||||
self.onsuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
assets/webapps/uitest.gaiamobile.org/js/API/dialnumber.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
var dial = document.querySelector('#dial');
|
||||
if (dial) {
|
||||
dial.onclick = function() {
|
||||
var call = new MozActivity({
|
||||
name: 'dial',
|
||||
data: {
|
||||
number: '+46777888999'
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
84
assets/webapps/uitest.gaiamobile.org/js/API/downloads.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
'use strict';
|
||||
|
||||
var Downloads = function Downloads() {
|
||||
|
||||
var deleteButton, statusButton;
|
||||
|
||||
var download = null;
|
||||
|
||||
var init = function init() {
|
||||
deleteButton = document.getElementById('downloadDelete');
|
||||
deleteButton.addEventListener('click', onDelete);
|
||||
statusButton = document.getElementById('downloadStatus');
|
||||
statusButton.addEventListener('click', onStatus);
|
||||
|
||||
listen();
|
||||
};
|
||||
|
||||
var onDelete = function onDelete(evt) {
|
||||
LazyLoader.load(['/shared/js/download/download_helper.js'],
|
||||
function helperLoaded() {
|
||||
var request = DownloadHelper.remove(download);
|
||||
request.onsuccess = function() {
|
||||
alert('Download removed succesfully');
|
||||
download = null;
|
||||
deleteButton.disabled = true;
|
||||
statusButton.disabled = true;
|
||||
};
|
||||
|
||||
request.onerror = function(msg) {
|
||||
alert('Error : ' + msg.message);
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var onStatus = function onStatus(evt) {
|
||||
if (download === null) {
|
||||
alert('No download initiated');
|
||||
} else {
|
||||
var msg = 'Current download:\n';
|
||||
msg += 'Total bytes: ' + download.totalBytes + '\n';
|
||||
msg += 'Current bytes: ' + download.currentBytes + '\n';
|
||||
msg += 'Url: ' + download.url + '\n';
|
||||
msg += 'Path: ' + download.path + '\n';
|
||||
try {
|
||||
msg += 'Start time: ' + download.startTime + '\n';
|
||||
} catch (e) {
|
||||
msg += 'Start: unknown\n';
|
||||
}
|
||||
msg += 'Id: ' + download.id + '\n';
|
||||
alert('Current download: ' + JSON.stringify(msg));
|
||||
console.log(msg);
|
||||
}
|
||||
};
|
||||
|
||||
var listen = function listen() {
|
||||
navigator.mozDownloadManager.ondownloadstart = downloadStartedListener;
|
||||
};
|
||||
|
||||
var downloadStartedListener = function downloadStartedListener(evt) {
|
||||
download = evt.download;
|
||||
download.onstatechange = downloadChangeListener;
|
||||
|
||||
statusButton.disabled = false;
|
||||
statusButton.textContent = 'Status: ' + download.state;
|
||||
//console.log('State new download: ' + download.state);
|
||||
};
|
||||
|
||||
var downloadChangeListener = function downloadChangeListener(evt) {
|
||||
statusButton.textContent = 'Status: ' + download.state;
|
||||
console.log('State change: ' + download.state);
|
||||
|
||||
if (download.state === 'succeeded') {
|
||||
console.log('Download finished');
|
||||
deleteButton.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
'init': init
|
||||
};
|
||||
}();
|
||||
|
||||
window.addEventListener('load', Downloads.init.bind(Downloads));
|
||||
118
assets/webapps/uitest.gaiamobile.org/js/API/fxa.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* Tests for Firefox Accounts observer API
|
||||
*
|
||||
* navigator.mozId.watch(), .request(), and .logout().
|
||||
*/
|
||||
|
||||
function unpackAssertion(assertion) {
|
||||
var parts = assertion.split('.');
|
||||
return {
|
||||
header: atob(parts[0]),
|
||||
claim: atob(parts[1]),
|
||||
payload: atob(parts[3])
|
||||
};
|
||||
}
|
||||
|
||||
function FXATests() {
|
||||
this._eventNum = 1;
|
||||
}
|
||||
|
||||
FXATests.prototype = {
|
||||
_setupCallbacks: function() {
|
||||
if (this._running) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
try {
|
||||
navigator.mozId.watch({
|
||||
wantIssuer: 'firefox-accounts',
|
||||
|
||||
onlogin: function(assertion) {
|
||||
var unpacked = JSON.stringify(unpackAssertion(assertion), null, 2);
|
||||
self.recordEvent('login', 'login',
|
||||
{assertion: assertion, unpacked: unpacked});
|
||||
},
|
||||
|
||||
onlogout: function() {
|
||||
self.recordEvent('logout', 'logout');
|
||||
},
|
||||
|
||||
onready: function() {
|
||||
self.recordEvent('ready', 'ready');
|
||||
},
|
||||
|
||||
onerror: function(error) {
|
||||
self.recordEvent('error: ' + JSON.stringify(error), 'error');
|
||||
}
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
this.recordEvent('error: ' + JSON.stringify(error), 'error');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind UI components
|
||||
*/
|
||||
_bindEvents: function() {
|
||||
if (this._running) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var testElementHandlers = {
|
||||
'request': function() {
|
||||
navigator.mozId.request({
|
||||
oncancel: function() {
|
||||
self.recordEvent('cancel', 'cancel');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
'logout': function() {
|
||||
navigator.mozId.logout();
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(testElementHandlers).forEach(function(selector) {
|
||||
document.getElementById(selector).addEventListener(
|
||||
'click', testElementHandlers[selector]);
|
||||
});
|
||||
},
|
||||
|
||||
recordEvent: function(message, cklass, params) {
|
||||
var li = document.createElement('li');
|
||||
if (cklass) {
|
||||
li.classList.add(cklass);
|
||||
}
|
||||
|
||||
var events = document.getElementById('event-stream');
|
||||
var html = message;
|
||||
|
||||
if (typeof params === 'object') {
|
||||
Object.keys(params).forEach(function(key) {
|
||||
html += '<div>' + key + ':</div>';
|
||||
html += '<div class="' + key + '">' + params[key] + '</div>';
|
||||
});
|
||||
}
|
||||
|
||||
li.innerHTML = html;
|
||||
events.appendChild(li);
|
||||
this._eventNum += 1;
|
||||
},
|
||||
|
||||
init: function() {
|
||||
this._setupCallbacks();
|
||||
this._bindEvents();
|
||||
this._running = true;
|
||||
|
||||
this.recordEvent('Starting. Wait for "ready" message.');
|
||||
},
|
||||
};
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
var fxaTests = new FXATests();
|
||||
fxaTests.init();
|
||||
}, false);
|
||||
|
||||
22
assets/webapps/uitest.gaiamobile.org/js/API/geolocation.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
function Location(position)//location function is defining with parameter
|
||||
{
|
||||
//Specifies the longitude estimate in decimal degrees.
|
||||
//The value range is [-180.00, +180.00].
|
||||
var latitude = position.coords.latitude;
|
||||
var longitude = position.coords.longitude;
|
||||
//latitude value is defining in label element where id is lati
|
||||
document.getElementById('lati').innerHTML = latitude;
|
||||
document.getElementById('longi').innerHTML = longitude;
|
||||
}
|
||||
|
||||
document.getElementById('submit').addEventListener('click',
|
||||
function findLocation() {
|
||||
//checking browser compatibility
|
||||
if (navigator.geolocation)
|
||||
{
|
||||
//getCurrentPosition method retrieve
|
||||
//the current geographic location of the user
|
||||
navigator.geolocation.getCurrentPosition(Location);
|
||||
}
|
||||
}
|
||||
);
|
||||
220
assets/webapps/uitest.gaiamobile.org/js/API/getusermedia.js
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
'use stricts';
|
||||
|
||||
// This is an indivisual gUM test item, may be audio, video, or both
|
||||
function gUMItem() {
|
||||
var video_status = false;
|
||||
var audio_status = false;
|
||||
var capturing = false;
|
||||
var constrain;
|
||||
|
||||
var media;
|
||||
var video_frame;
|
||||
var video;
|
||||
var snapshots_frames;
|
||||
var audio;
|
||||
var startButtons;
|
||||
var stopButtons;
|
||||
var snapshotButton;
|
||||
var message;
|
||||
|
||||
var snapshots = [];
|
||||
var saved_stream;
|
||||
|
||||
this.init = function(content, i) {
|
||||
media = content.querySelector('[data-type="media"]');
|
||||
video_frame = content.querySelector('[data-type="video-frame"]');
|
||||
video = content.querySelector('video');
|
||||
snapshots_frames = content.querySelector('[data-type="snapshots-frames"]');
|
||||
audio = content.querySelector('audio');
|
||||
startButtons = content.querySelector('[data-type="startbuttons"]');
|
||||
stopButtons = content.querySelector('[data-type="stopbuttons"]');
|
||||
snapshotButton = content.querySelector('[data-type="snapshotButton"]');
|
||||
message = content.querySelector('[data-type="message"]');
|
||||
|
||||
// remove them first, we will add them back when we need them
|
||||
media.removeChild(video_frame);
|
||||
media.removeChild(audio);
|
||||
|
||||
// label button with index
|
||||
content.querySelector('[data-type="startVideoButton"]').textContent += i;
|
||||
content.querySelector('[data-type="startAudioButton"]').textContent += i;
|
||||
content.querySelector('[data-type="startAudioVideoButton"]').
|
||||
textContent += i;
|
||||
|
||||
content.querySelector('[data-type="startVideoButton"]').
|
||||
addEventListener('click', startVideo);
|
||||
content.querySelector('[data-type="startAudioButton"]').
|
||||
addEventListener('click', startAudio);
|
||||
content.querySelector('[data-type="startAudioVideoButton"]').
|
||||
addEventListener('click', startAudioVideo);
|
||||
content.querySelector('[data-type="startDoubleRequest"]').
|
||||
addEventListener('click', startDoubleRequest);
|
||||
content.querySelector('[data-type="stopButton"]').
|
||||
addEventListener('click', stopMedia);
|
||||
content.querySelector('[data-type="pauseButton"]').
|
||||
addEventListener('click', pauseMedia);
|
||||
content.querySelector('[data-type="snapshotButton"]').
|
||||
addEventListener('click', startSnapshot);
|
||||
};
|
||||
|
||||
function startVideo() {
|
||||
startMedia({video: true});
|
||||
}
|
||||
|
||||
function startAudioVideo() {
|
||||
startMedia({video: true, audio: true});
|
||||
}
|
||||
|
||||
function startAudio() {
|
||||
startMedia({audio: true});
|
||||
}
|
||||
|
||||
function startDoubleRequest() {
|
||||
// empty request, just for prompt
|
||||
function empty() {}
|
||||
function success(stream) {
|
||||
message.innerHTML = '<p class="success">Double prompt!</p>';
|
||||
}
|
||||
function error(err) {
|
||||
message.innerHTML = '<p class="error">' + err + '</p>';
|
||||
}
|
||||
window.navigator.mozGetUserMedia({audio: true}, empty, error);
|
||||
window.navigator.mozGetUserMedia({audio: true}, success, error);
|
||||
}
|
||||
|
||||
function startMedia(param) {
|
||||
constrain = param;
|
||||
function success(stream) {
|
||||
message.innerHTML = '<p class="success">Success!</p>';
|
||||
stopButtons.style.display = 'block';
|
||||
startButtons.style.display = 'none';
|
||||
|
||||
if ('video' in constrain) {
|
||||
video_status = true;
|
||||
media.appendChild(video_frame);
|
||||
video.mozSrcObject = stream;
|
||||
video.play();
|
||||
snapshots_frames.innerHTML = '';
|
||||
stopButtons.appendChild(snapshotButton);
|
||||
}
|
||||
if ('audio' in constrain) {
|
||||
audio_status = true;
|
||||
media.appendChild(audio);
|
||||
audio.mozSrcObject = stream;
|
||||
audio.play();
|
||||
}
|
||||
}
|
||||
|
||||
function error(err) {
|
||||
if (video_status)
|
||||
video_status = false;
|
||||
message.innerHTML = '<p class="error">' + err + '</p>';
|
||||
}
|
||||
window.navigator.mozGetUserMedia(param, success, error);
|
||||
}
|
||||
|
||||
function pauseMedia() {
|
||||
if (saved_stream) {
|
||||
if (video_status) {
|
||||
video.mozSrcObject = saved_stream;
|
||||
video.play();
|
||||
} else if (audio_status) {
|
||||
audio.mozSrcObject = saved_stream;
|
||||
audio.play();
|
||||
}
|
||||
saved_stream = null;
|
||||
} else {
|
||||
if (video_status) {
|
||||
video.pause();
|
||||
saved_stream = video.mozSrcObject;
|
||||
video.mozSrcObject = null;
|
||||
} else if (audio_status) {
|
||||
audio.pause();
|
||||
saved_stream = audio.mozSrcObject;
|
||||
audio.mozSrcObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopMedia() {
|
||||
message.innerHTML = '';
|
||||
if (video_status) {
|
||||
video.mozSrcObject.stop();
|
||||
video.mozSrcObject = null;
|
||||
snapshots = [];
|
||||
snapshots_frames.innerHTML = '';
|
||||
media.removeChild(video_frame);
|
||||
|
||||
stopButtons.removeChild(snapshotButton);
|
||||
snapshotButton.textContent = 'Snapshot';
|
||||
|
||||
capturing = false;
|
||||
video_status = false;
|
||||
}
|
||||
if (audio_status) {
|
||||
audio.mozSrcObject.stop();
|
||||
audio.mozSrcObject = null;
|
||||
media.removeChild(audio);
|
||||
|
||||
audio_status = false;
|
||||
}
|
||||
saved_stream = null;
|
||||
|
||||
stopButtons.style.display = 'none';
|
||||
startButtons.style.display = 'block';
|
||||
}
|
||||
|
||||
function startSnapshot() {
|
||||
capturing = !capturing;
|
||||
if (capturing) {
|
||||
captureImage();
|
||||
snapshotButton.textContent = 'Stop Snapshot';
|
||||
} else {
|
||||
snapshotButton.textContent = 'Snapshot';
|
||||
}
|
||||
}
|
||||
|
||||
function captureImage() {
|
||||
if (video_status && capturing) {
|
||||
//dump('Capturing len ' + snapshots.length + '\n');
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = video.videoWidth / 4;
|
||||
canvas.height = video.videoHeight / 4;
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
if (snapshots.unshift(canvas) > 4)
|
||||
snapshots.length = 4;
|
||||
snapshots_frames.innerHTML = '';
|
||||
for (var j = 0; j < snapshots.length; j++) {
|
||||
snapshots_frames.appendChild(snapshots[j]);
|
||||
}
|
||||
|
||||
setTimeout(captureImage, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// You can modify max_instances to test more than one gUM instance
|
||||
function gUMTest() {
|
||||
var max_instances = 2;
|
||||
var gUMItems = [];
|
||||
|
||||
(function init() {
|
||||
var contents = document.getElementById('contents');
|
||||
for (var i = 0; i < max_instances; i++) {
|
||||
gUMItems[i] = new gUMItem;
|
||||
|
||||
// display elements
|
||||
// first copy one from template then modify it.
|
||||
var template = document.getElementById('content_template');
|
||||
var content = template.cloneNode(true);
|
||||
content.id = 'content' + (i + 1);
|
||||
content.classList.remove('hidden');
|
||||
gUMItems[i].init(content, (i + 1));
|
||||
contents.appendChild(content);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
window.addEventListener('load', gUMTest);
|
||||
111
assets/webapps/uitest.gaiamobile.org/js/API/identity-get.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* In these tests, we exercise the native navigator.mozId.get method.
|
||||
*/
|
||||
|
||||
function getUnverifiedOk() {
|
||||
return document.getElementById('unverified-ok').checked;
|
||||
}
|
||||
|
||||
function getIssuerName() {
|
||||
return document.getElementById('issuer-name').value.trim();
|
||||
}
|
||||
|
||||
function unpackAssertion(assertion) {
|
||||
var parts = assertion.split('.');
|
||||
return {
|
||||
header: atob(parts[0]),
|
||||
claim: atob(parts[1]),
|
||||
payload: atob(parts[3])
|
||||
};
|
||||
}
|
||||
|
||||
function IdentityTests() {
|
||||
|
||||
/**
|
||||
* recordEvent: make a note in the event stream
|
||||
*
|
||||
* message is the main text of the note
|
||||
* cklass is the css class to apply to the enclosing li
|
||||
* params is an optional dictionary of strings to append as divs
|
||||
*/
|
||||
this._eventNum = 1;
|
||||
this.recordEvent = function id_recordEvent(message, cklass, params) {
|
||||
var li = document.createElement('li');
|
||||
if (cklass) {
|
||||
li.classList.add(cklass);
|
||||
}
|
||||
|
||||
var events = document.getElementById('event-stream');
|
||||
var html = message;
|
||||
|
||||
if (typeof params === 'object') {
|
||||
Object.keys(params).forEach(function(key) {
|
||||
html += '<div>' + key + ':</div>';
|
||||
html += "<div class='" + key + "'>" + params[key] + '</div>';
|
||||
});
|
||||
}
|
||||
|
||||
li.innerHTML = html;
|
||||
events.appendChild(li);
|
||||
this._eventNum += 1;
|
||||
};
|
||||
|
||||
this.gotAssertion = function id_gotAssertion(assertion) {
|
||||
var result = {assertion: assertion};
|
||||
if (assertion) {
|
||||
result.unpacked = JSON.stringify(unpackAssertion(assertion), null, 2);
|
||||
}
|
||||
this.recordEvent('Assertion', 'assertion', result);
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind UI components
|
||||
*/
|
||||
this._bindEvents = function id__bindEvents() {
|
||||
if (this._running) return;
|
||||
var gotAssertion = this.gotAssertion.bind(this);
|
||||
var self = this;
|
||||
var testElementHandlers = {
|
||||
't-get': function() {
|
||||
navigator.mozId.get(gotAssertion);
|
||||
},
|
||||
|
||||
't-get-allowUnverified': function() {
|
||||
navigator.mozId.get(gotAssertion, {
|
||||
allowUnverified: getUnverifiedOk()
|
||||
});
|
||||
},
|
||||
|
||||
't-get-forceIssuer': function() {
|
||||
navigator.mozId.get(gotAssertion, {
|
||||
forceIssuer: getIssuerName()
|
||||
});
|
||||
},
|
||||
|
||||
't-get-forceIssuer-allowUnverified': function() {
|
||||
navigator.mozId.get(gotAssertion, {
|
||||
allowUnverified: getUnverifiedOk(),
|
||||
forceIssuer: getIssuerName()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(testElementHandlers).forEach(function(selector) {
|
||||
document.getElementById(selector).addEventListener(
|
||||
'click', testElementHandlers[selector]);
|
||||
});
|
||||
};
|
||||
|
||||
this.init = function id_init() {
|
||||
this._bindEvents();
|
||||
this._running = true;
|
||||
|
||||
this.recordEvent('Tests are ready');
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
var identityTests = new IdentityTests();
|
||||
identityTests.init();
|
||||
}, false);
|
||||
|
||||
143
assets/webapps/uitest.gaiamobile.org/js/API/identity.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* The IdentityTests are meant to be exercised from marionette as part of the
|
||||
* gaia-ui-tests suites.
|
||||
*
|
||||
* In these tests, we exercise the native navigator.mozId directly.
|
||||
*/
|
||||
|
||||
function getUnverifiedOk() {
|
||||
return document.getElementById('unverified-ok').checked;
|
||||
}
|
||||
|
||||
function getIssuerName() {
|
||||
return document.getElementById('issuer-name').value.trim();
|
||||
}
|
||||
|
||||
function unpackAssertion(assertion) {
|
||||
var parts = assertion.split('.');
|
||||
return {
|
||||
header: atob(parts[0]),
|
||||
claim: atob(parts[1]),
|
||||
payload: atob(parts[3])
|
||||
};
|
||||
}
|
||||
|
||||
function IdentityTests() {
|
||||
|
||||
/**
|
||||
* recordEvent: make a note in the event stream
|
||||
*
|
||||
* message is the main text of the note
|
||||
* cklass is the css class to apply to the enclosing li
|
||||
* params is an optional dictionary of strings to append as divs
|
||||
*/
|
||||
this._eventNum = 1;
|
||||
this.recordEvent = function id_recordEvent(message, cklass, params) {
|
||||
var li = document.createElement('li');
|
||||
if (cklass) {
|
||||
li.classList.add(cklass);
|
||||
}
|
||||
|
||||
var events = document.getElementById('event-stream');
|
||||
var html = message;
|
||||
|
||||
if (typeof params === 'object') {
|
||||
Object.keys(params).forEach(function(key) {
|
||||
html += '<div>' + key + ':</div>';
|
||||
html += "<div class='" + key + "'>" + params[key] + '</div>';
|
||||
});
|
||||
}
|
||||
|
||||
li.innerHTML = html;
|
||||
events.appendChild(li);
|
||||
this._eventNum += 1;
|
||||
};
|
||||
|
||||
this._setupCallbacks = function id__setupCallbacks() {
|
||||
if (this._running) return;
|
||||
var self = this;
|
||||
try {
|
||||
navigator.mozId.watch({
|
||||
|
||||
onlogin: function(assertion) {
|
||||
var unpacked = JSON.stringify(unpackAssertion(assertion), null, 2);
|
||||
self.recordEvent('login', 'login',
|
||||
{assertion: assertion, unpacked: unpacked});
|
||||
},
|
||||
|
||||
onlogout: function() {
|
||||
self.recordEvent('logout', 'logout');
|
||||
},
|
||||
|
||||
onready: function() {
|
||||
self.recordEvent('ready', 'ready');
|
||||
}
|
||||
|
||||
});
|
||||
} catch (err) {
|
||||
this.recordEvent('Error: ' + err, 'error');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind UI components
|
||||
*/
|
||||
this._bindEvents = function id__bindEvents() {
|
||||
if (this._running) return;
|
||||
var self = this;
|
||||
var testElementHandlers = {
|
||||
't-request': function() {
|
||||
navigator.mozId.request();
|
||||
},
|
||||
|
||||
't-request-withOnCancel': function() {
|
||||
navigator.mozId.request({oncancel: function() {
|
||||
self.recordEvent('cancel', 'cancel');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
't-request-allowUnverified': function() {
|
||||
navigator.mozId.request({
|
||||
allowUnverified: getUnverifiedOk()
|
||||
});
|
||||
},
|
||||
|
||||
't-request-forceIssuer': function() {
|
||||
navigator.mozId.request({
|
||||
forceIssuer: getIssuerName()
|
||||
});
|
||||
},
|
||||
|
||||
't-request-forceIssuer-allowUnverified': function() {
|
||||
navigator.mozId.request({
|
||||
allowUnverified: getUnverifiedOk(),
|
||||
forceIssuer: getIssuerName()
|
||||
});
|
||||
},
|
||||
|
||||
't-logout': function() {
|
||||
navigator.mozId.logout();
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(testElementHandlers).forEach(function(selector) {
|
||||
document.getElementById(selector).addEventListener(
|
||||
'click', testElementHandlers[selector]);
|
||||
});
|
||||
};
|
||||
|
||||
this.init = function id_init() {
|
||||
this._setupCallbacks();
|
||||
this._bindEvents();
|
||||
this._running = true;
|
||||
|
||||
this.recordEvent('Ready to rock');
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
var identityTests = new IdentityTests();
|
||||
identityTests.init();
|
||||
}, false);
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
document.getElementById('go').onclick = function _go() {
|
||||
var a = new MozActivity(
|
||||
{
|
||||
name: 'test-inline',
|
||||
data: {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
a.onsuccess = function() {
|
||||
document.getElementById('result').textContent = this.result.text;
|
||||
};
|
||||
|
||||
a.onerror = function() {
|
||||
document.getElementById('result').textContent = '(canceled)';
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
document.getElementById('go').onclick = function _go() {
|
||||
var a = new MozActivity({ name: 'test', data: {type: 'text'}});
|
||||
a.onsuccess = function() { alert('Success!'); };
|
||||
a.onerror = function() { alert('Failure.'); };
|
||||
};
|
||||
82
assets/webapps/uitest.gaiamobile.org/js/API/openaudio.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
var open = document.getElementById('open');
|
||||
var opensavefile = document.getElementById('opensavefile');
|
||||
var opensaveblob = document.getElementById('opensaveblob');
|
||||
open.hidden = true;
|
||||
opensavefile.hidden = true;
|
||||
opensaveblob.hidden = true;
|
||||
|
||||
open.onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'audio/ogg',
|
||||
blob: localblob,
|
||||
title: 'jonobacon-freesoftwaresong2.ogg'
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
opensavefile.onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'audio/ogg',
|
||||
blob: localblob,
|
||||
allowSave: true,
|
||||
filename: 'downloads/uitest/jonobacon-freesoftwaresong2.ogg'
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
opensaveblob.onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'audio/ogg',
|
||||
blob: remoteblob,
|
||||
allowSave: true,
|
||||
filename: 'downloads/uitest/Back_In_Black-sample.ogg'
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
// The audio we want to open
|
||||
var localblob;
|
||||
var remoteblob;
|
||||
var localURL = '../../data/audio/jonobacon-freesoftwaresong2.ogg';
|
||||
var remoteURL = 'http://upload.wikimedia.org/wikipedia/en/4/45/' +
|
||||
'ACDC_-_Back_In_Black-sample.ogg';
|
||||
|
||||
var localxhr = new XMLHttpRequest();
|
||||
localxhr.open('GET', localURL);
|
||||
localxhr.responseType = 'blob';
|
||||
localxhr.send();
|
||||
localxhr.onload = function() {
|
||||
localblob = new Blob([localxhr.response], { type: 'audio/ogg' });
|
||||
open.hidden = false;
|
||||
opensavefile.hidden = false;
|
||||
};
|
||||
localxhr.onerror = function() {
|
||||
console.log('Error loading test audio', localxhr.error.name);
|
||||
};
|
||||
|
||||
var remotexhr = new XMLHttpRequest();
|
||||
remotexhr.open('GET', remoteURL);
|
||||
remotexhr.responseType = 'blob';
|
||||
remotexhr.send();
|
||||
remotexhr.onload = function() {
|
||||
remoteblob = new Blob([remotexhr.response], { type: 'audio/ogg' });
|
||||
opensaveblob.hidden = false;
|
||||
};
|
||||
remotexhr.onerror = function() {
|
||||
console.log('Error loading test audio from network', remotexhr.error.name);
|
||||
};
|
||||
49
assets/webapps/uitest.gaiamobile.org/js/API/openvideo.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
var open = document.getElementById('open');
|
||||
var opensave = document.getElementById('opensave');
|
||||
open.hidden = true;
|
||||
opensave.hidden = true;
|
||||
|
||||
open.onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'video/3gpp',
|
||||
blob: videoblob,
|
||||
title: 'Stapler'
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
opensave.onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'video/3gpp',
|
||||
blob: videoblob,
|
||||
allowSave: true,
|
||||
filename: 'downloads/uitest/stapler.3gp'
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
// The image we want to view
|
||||
var videoblob;
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '../../data/video/stapler.3gp');
|
||||
xhr.responseType = 'blob';
|
||||
xhr.send();
|
||||
xhr.onload = function() {
|
||||
videoblob = new Blob([xhr.response], { type: 'video/3gpp' });
|
||||
open.hidden = false;
|
||||
opensave.hidden = false;
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
console.log('Error loading test video', xhr.error.name);
|
||||
};
|
||||
56
assets/webapps/uitest.gaiamobile.org/js/API/orientation.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
function orientationTest(evt) {
|
||||
switch (evt.target.id) {
|
||||
case 'lock1':
|
||||
screen.mozLockOrientation(['portrait']);
|
||||
break;
|
||||
case 'lock2':
|
||||
screen.mozLockOrientation(['landscape']);
|
||||
break;
|
||||
case 'lock3':
|
||||
screen.mozLockOrientation(['portrait-primary']);
|
||||
break;
|
||||
case 'lock4':
|
||||
screen.mozLockOrientation(['portrait-secondary']);
|
||||
break;
|
||||
case 'lock5':
|
||||
screen.mozLockOrientation(['landscape-primary']);
|
||||
break;
|
||||
case 'lock6':
|
||||
screen.mozLockOrientation(['landscape-secondary']);
|
||||
break;
|
||||
case 'lock7':
|
||||
screen.mozLockOrientation(['portrait', 'landscape-primary']);
|
||||
break;
|
||||
case 'lock8':
|
||||
screen.mozLockOrientation(['portrait', 'landscape-secondary']);
|
||||
break;
|
||||
case 'lock9':
|
||||
screen.mozLockOrientation(['landscape', 'portrait-primary']);
|
||||
break;
|
||||
case 'lock10':
|
||||
screen.mozLockOrientation(['landscape', 'portrait-secondary']);
|
||||
break;
|
||||
case 'lock11':
|
||||
screen.mozLockOrientation(['portrait-primary', 'landscape-primary']);
|
||||
break;
|
||||
case 'lock12':
|
||||
screen.mozLockOrientation(['portrait-secondary', 'landscape-primary']);
|
||||
break;
|
||||
case 'lock13':
|
||||
screen.mozLockOrientation(['portrait-primary', 'landscape-secondary']);
|
||||
break;
|
||||
case 'lock14':
|
||||
screen.mozLockOrientation(['portrait-secondary', 'landscape-secondary']);
|
||||
break;
|
||||
case 'unlock':
|
||||
screen.mozUnlockOrientation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var buttons = document.getElementsByTagName('button');
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
buttons[i].addEventListener('click', orientationTest);
|
||||
}
|
||||
});
|
||||
63
assets/webapps/uitest.gaiamobile.org/js/API/pay.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
/* vim: set ft=javascript sw=2 ts=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
Mock payment provider JWT
|
||||
|
||||
{ "iss": "323d34dc-b5cf-4822-8e47-6a4515dc74db",
|
||||
"request": {
|
||||
"description": "The forbidden fruit",
|
||||
"id": "af1f960a-3f90-4e2d-a20f-d5170aee49f2",
|
||||
"postbackURL": "https://inapp-pay-test.paas.allizom.org/mozpay/postback",
|
||||
"productData": "localTransID=6fc8f207-da3f-440f-9977-eb613aae0a00",
|
||||
"chargebackURL": "https://inapp-pay-test.paas.allizom.org/mozpay/chargeback",
|
||||
"name": "Virtual Kiwi"
|
||||
},
|
||||
"typ": "tests/payments/pay/v1",
|
||||
"aud": "ferjm.github.io/gaia-mock-payment-provider"
|
||||
}
|
||||
|
||||
*/
|
||||
const mockJWT = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIzMjNkMzRkYy1iNWNmLTQ4MjItOGU0Ny02YTQ1MTVkYzc0ZGIiLCJhdWQiOiJmZXJqbS5naXRodWIuaW8vZ2FpYS1tb2NrLXBheW1lbnQtcHJvdmlkZXIiLCJ0eXAiOiJ0ZXN0cy9wYXltZW50cy9wYXkvdjEiLCJyZXF1ZXN0Ijp7ImlkIjoiYWYxZjk2MGEtM2Y5MC00ZTJkLWEyMGYtZDUxNzBhZWU0OWYyIiwibmFtZSI6IlZpcnR1YWwgS2l3aSIsImRlc2NyaXB0aW9uIjoiVGhlIGZvcmJpZGRlbiBmcnVpdCIsInByb2R1Y3REYXRhIjoibG9jYWxUcmFuc0lEPTZmYzhmMjA3LWRhM2YtNDQwZi05OTc3LWViNjEzYWFlMGEwMCIsInBvc3RiYWNrVVJMIjoiaHR0cHM6Ly9pbmFwcC1wYXktdGVzdC5wYWFzLmFsbGl6b20ub3JnL21venBheS9wb3N0YmFjayIsImNoYXJnZWJhY2tVUkwiOiJodHRwczovL2luYXBwLXBheS10ZXN0LnBhYXMuYWxsaXpvbS5vcmcvbW96cGF5L2NoYXJnZWJhY2sifX0.DlHejmKRlVPLYO-HVAYDzqOcv5eXMsVlpnozcADpnh4';
|
||||
|
||||
// Invalid typ JWT
|
||||
const invalidJWT = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.IntcImF1ZFwiOiBcImNvbm5lY3QucWEtb3BlbnRlbC0wNC5oaS5pbmV0XCIsIFwiaXNzXCI6IFwiMzRYVjM3QkRSQkJGNEtaQ1M5UVVcIiwgXCJyZXF1ZXN0XCI6IHtcIm5hbWVcIjogXCJQaWNlIG9mIENha2VcIiwgXCJwcmljZVwiOiAxMC41LCBcInByaWNlVGllclwiOiAxLCBcInByb2R1Y3RkYXRhXCI6IFwidHJhbnNhY3Rpb25faWQ9MTJcIiwgXCJjdXJyZW5jeUNvZGVcIjogXCJVU0RcIiwgXCJkZXNjcmlwdGlvblwiOiBcIlZpcnR1YWwgY2hvY29sYXRlIGNha2UgdG8gZmlsbCB5b3VyIHZpcnR1YWwgdHVtbXlcIn0sIFwiZXhwXCI6IDEzNDIwMDMwNzA1MTMsIFwiaWF0XCI6IDEzNDIwMDMwNzQxMTMsIFwidHlwXCI6IFwicGF5bWVudHMvaW5hcHAvdjFcIn0i.I5rFwoBtgyTltMJ_11rHOgcto-HdFbYlIOgVOVSlJe0';
|
||||
|
||||
var PayTests = {
|
||||
|
||||
init: function pt_init() {
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
var jwts = {
|
||||
'test-pmpp': [mockJWT],
|
||||
'test-invalid': [invalidJWT],
|
||||
'test-nojwt': undefined,
|
||||
'test-repeated': [mockJWT, mockJWT]
|
||||
};
|
||||
|
||||
['test-pmpp',
|
||||
'test-invalid',
|
||||
'test-nojwt',
|
||||
'test-repeated'].forEach(function(id) {
|
||||
document.getElementById(id).addEventListener('click', function() {
|
||||
PayTests.pay(jwts[id]);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
pay: function pt_pay(JWTs) {
|
||||
var request = navigator.mozPay(JWTs);
|
||||
request.onsuccess = function onsuccess() {
|
||||
document.getElementById('result').innerHTML = 'Payment success';
|
||||
};
|
||||
request.onerror = function onerror() {
|
||||
console.log("Error " + request.error.name);
|
||||
document.getElementById('result').innerHTML = 'Payment error ' +
|
||||
request.error.name;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
PayTests.init();
|
||||
13
assets/webapps/uitest.gaiamobile.org/js/API/pickdownload.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
function pick(type) {
|
||||
var data = {type: type};
|
||||
var a = new MozActivity({ name: 'pick', data: data});
|
||||
a.onsuccess = function(e) {
|
||||
var result = document.getElementById('result');
|
||||
result.textContent = a.result.name + ' ' + a.result.type + ' (' +
|
||||
a.result.size + ')';
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById('b1').onclick = function() {
|
||||
pick('application/*');
|
||||
};
|
||||
25
assets/webapps/uitest.gaiamobile.org/js/API/pickphoto.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
function pick(type, nocrop, width, height) {
|
||||
var data = {type: type};
|
||||
if (nocrop) data.nocrop = true;
|
||||
if (width) data.width = width;
|
||||
if (height) data.height = height;
|
||||
var a = new MozActivity({ name: 'pick', data: data});
|
||||
a.onsuccess = function(e) {
|
||||
console.log('got blob of type', a.result.blob.type);
|
||||
var url = URL.createObjectURL(a.result.blob);
|
||||
var img = document.getElementById('result');
|
||||
img.src = url;
|
||||
img.onload = function() { URL.revokeObjectURL(url); };
|
||||
};
|
||||
a.onerror = function() { alert('Failure picking photo'); };
|
||||
}
|
||||
|
||||
document.getElementById('b1').onclick = function() { pick('image/jpeg'); };
|
||||
document.getElementById('b2').onclick = function() { pick('image/png'); };
|
||||
document.getElementById('b3').onclick = function() {
|
||||
pick(['image/jpeg', 'image/png']);
|
||||
};
|
||||
document.getElementById('b4').onclick = function() { pick('image/*'); };
|
||||
document.getElementById('b5').onclick = function() { pick('image/*', true); };
|
||||
document.getElementById('b6').onclick = function() { pick('image/*', false,
|
||||
100, 100); };
|
||||
32
assets/webapps/uitest.gaiamobile.org/js/API/picktype.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
function pick(type, nocrop, width, height) {
|
||||
var data = {type: type};
|
||||
if (nocrop) data.nocrop = true;
|
||||
if (width) data.width = width;
|
||||
if (height) data.height = height;
|
||||
var a = new MozActivity({ name: 'pick', data: data});
|
||||
a.onsuccess = function(e) {
|
||||
console.log('got blob of type', a.result.blob.type);
|
||||
var url = URL.createObjectURL(a.result.blob);
|
||||
var img = document.getElementById('result');
|
||||
img.src = url;
|
||||
img.onload = function() { URL.revokeObjectURL(url); };
|
||||
};
|
||||
a.onerror = function() { alert('Failure picking'); };
|
||||
}
|
||||
|
||||
document.getElementById('b1').onclick = function() {
|
||||
pick(['video/*', 'image/*']);
|
||||
};
|
||||
document.getElementById('b2').onclick = function() {
|
||||
pick(['image/*', 'video/*']);
|
||||
};
|
||||
document.getElementById('b3').onclick = function() {
|
||||
pick(['video/*', 'image/jpg']);
|
||||
};
|
||||
document.getElementById('b4').onclick = function() {
|
||||
pick(['video/*', 'image/png']);
|
||||
};
|
||||
document.getElementById('b5').onclick = function() {
|
||||
pick(['video/3gpp', 'image/jpg']);
|
||||
};
|
||||
|
||||
18
assets/webapps/uitest.gaiamobile.org/js/API/pickvideo.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
function pick(type, nocrop, width, height) {
|
||||
var data = {type: type};
|
||||
if (nocrop) data.nocrop = true;
|
||||
if (width) data.width = width;
|
||||
if (height) data.height = height;
|
||||
var a = new MozActivity({ name: 'pick', data: data});
|
||||
a.onsuccess = function(e) {
|
||||
console.log('got blob of type', a.result.blob.type);
|
||||
var url = URL.createObjectURL(a.result.blob);
|
||||
var img = document.getElementById('result');
|
||||
img.src = url;
|
||||
img.onload = function() { URL.revokeObjectURL(url); };
|
||||
};
|
||||
a.onerror = function() { alert('Failure picking video'); };
|
||||
}
|
||||
|
||||
document.getElementById('b1').onclick = function() { pick('video/3gpp'); };
|
||||
document.getElementById('b2').onclick = function() { pick('video/*'); };
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
'use strict';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
function log(aMsg) {
|
||||
console.log('-*- mdns.js : ' + aMsg + '\n');
|
||||
}
|
||||
|
||||
if (typeof navigator.mozPresentationDeviceInfo !== 'undefined') {
|
||||
navigator.mozPresentationDeviceInfo.addEventListener('devicechange',
|
||||
function(e) {
|
||||
log('device changed');
|
||||
getAll();
|
||||
});
|
||||
|
||||
document.getElementById('discovery').onclick = function _discovery() {
|
||||
log('discovery');
|
||||
navigator.mozPresentationDeviceInfo.forceDiscovery();
|
||||
getAll();
|
||||
};
|
||||
|
||||
getAll();
|
||||
}
|
||||
|
||||
function appendDevice(device) {
|
||||
var node = document.createElement('li');
|
||||
var textnode = document.createTextNode(device.name);
|
||||
node.appendChild(textnode);
|
||||
document.getElementById('devices').appendChild(node);
|
||||
}
|
||||
|
||||
function clearDevices() {
|
||||
var devNode = document.getElementById('devices');
|
||||
while (devNode.firstChild) {
|
||||
devNode.removeChild(devNode.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function getAll() {
|
||||
log('getAll');
|
||||
navigator.mozPresentationDeviceInfo.getAll().then(function(devices) {
|
||||
log('devices found: ' + devices.length);
|
||||
clearDevices();
|
||||
for (var device of devices) {
|
||||
appendDevice(device);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
13
assets/webapps/uitest.gaiamobile.org/js/API/savebookmark.js
Normal file
14
assets/webapps/uitest.gaiamobile.org/js/API/search.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
var search = document.querySelector('#search');
|
||||
if (search) {
|
||||
search.onclick = function() {
|
||||
var search_area = document.querySelector('#search_area');
|
||||
console.log('Search:' + search_area.value);
|
||||
var searching = new MozActivity({
|
||||
name: 'search',
|
||||
data: {
|
||||
type: 'text/plain',
|
||||
keyword: search_area.value
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
179
assets/webapps/uitest.gaiamobile.org/js/API/secure_element.js
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
'use strict';
|
||||
|
||||
/* globals Utils, AID, APDU */
|
||||
|
||||
var SIMAccessManager = {
|
||||
ERROR_MSG: {
|
||||
NOT_IDLE: 'WRONG SIM ACCESS STATE',
|
||||
ACCESS_FAILED: 'FAILED TO ENABLE SIM ACCESS',
|
||||
APPLETS_FAILED: 'FAILED TO GET APPLETS DATA'
|
||||
},
|
||||
|
||||
SIM_ACCESS_STATES: {
|
||||
DISABLED: 'disabled',
|
||||
IDLE: 'idle',
|
||||
BUSY: 'busy'
|
||||
},
|
||||
|
||||
_simState: null,
|
||||
_seSession: null,
|
||||
|
||||
start: function start() {
|
||||
if(!navigator.seManager) {
|
||||
return Promise.reject('Securel Element API not present');
|
||||
}
|
||||
|
||||
return navigator.seManager.getSEReaders()
|
||||
.then((readers) => {
|
||||
return readers[0].openSession();
|
||||
})
|
||||
.then((session) => {
|
||||
this._seSession = session;
|
||||
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
||||
})
|
||||
.catch(() => {
|
||||
this._simState = this.SIM_ACCESS_STATES.DISABLED;
|
||||
return Promise.reject(this.ERROR_MSG.ACCESS_FAILED);
|
||||
});
|
||||
},
|
||||
|
||||
stop: function stop() {
|
||||
return this._seSession.closeAll()
|
||||
.then(() => {
|
||||
this._seSession = null;
|
||||
this._simState = this.SIM_ACCESS_STATES.DISABLED;
|
||||
});
|
||||
},
|
||||
|
||||
getAppletsData: function getAppletsData() {
|
||||
if(this._simState !== this.SIM_ACCESS_STATES.IDLE) {
|
||||
return Promise.reject(this.ERROR_MSG.NOT_IDLE);
|
||||
}
|
||||
|
||||
this._simState = this.SIM_ACCESS_STATES.BUSY;
|
||||
return this._seSession.openLogicalChannel(AID.CRS)
|
||||
.then((channel) => {
|
||||
return channel.transmit(APDU.CRS.getStatusAll1st);
|
||||
})
|
||||
.then((resp) => {
|
||||
var receivedData = new Uint8Array();
|
||||
|
||||
// we can get different responses, also propriatary
|
||||
// using counter to not allow to try to query the SIM indifinately
|
||||
var count = 0;
|
||||
var condition = (resp) =>
|
||||
(count++ > 10 || resp.sw1 === 0x90 && resp.sw2 === 0x00);
|
||||
var action = (resp) => {
|
||||
receivedData = Utils.joinUint8Arrays(receivedData, resp.data);
|
||||
return resp.channel.transmit(APDU.CRS.getStatusAllNext)
|
||||
};
|
||||
|
||||
return Utils.whilePromise(condition, action, resp)
|
||||
.then((lastResp) => {
|
||||
lastResp.channel.close();
|
||||
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
||||
|
||||
return Utils.joinUint8Arrays(receivedData, lastResp.data);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
return this._cleanup(this.ERROR_MSG.APPLETS_FAILED);
|
||||
});
|
||||
},
|
||||
|
||||
toggleApplet: function toggleApplet(on, aid) {
|
||||
if(this._simState !== this.SIM_ACCESS_STATES.IDLE) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
this._simState = this.SIM_ACCESS_STATES.BUSY;
|
||||
var apdu = on ? APDU.CRS.activateCLF(aid) : APDU.CRS.deactivateCLF(aid);
|
||||
return this._seSession.openLogicalChannel(AID.CRS)
|
||||
.then((channel) => channel.transmit(apdu))
|
||||
.then((resp) => {
|
||||
return resp.channel.close()
|
||||
.then(() => {
|
||||
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
||||
return resp.sw1 === 0x90 && resp.sw2 === 0x00;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_cleanup: function cleanup(message) {
|
||||
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
||||
return Promise.reject(message);
|
||||
}
|
||||
};
|
||||
|
||||
var WalletDemo = {
|
||||
init: function init() {
|
||||
if(!navigator.seManager) {
|
||||
this._updateStatus('Secure Element API not available.');
|
||||
return;
|
||||
}
|
||||
|
||||
SIMAccessManager.start()
|
||||
.then(() => {
|
||||
this._updateStatus('Init SIM access, getting applets from CRS');
|
||||
return this._readDataRefreshUI();
|
||||
})
|
||||
.catch((error) => {
|
||||
this._updateStatus('Init failed.', error);
|
||||
SIMAccessManager.stop();
|
||||
});
|
||||
},
|
||||
|
||||
_readDataRefreshUI: function readDataRefreshUI() {
|
||||
return SIMAccessManager.getAppletsData()
|
||||
.then((appletsData) => {
|
||||
// parsing and filtering to get payment enabled applets
|
||||
var list = Utils.parseAppletsData(appletsData).filter((applet) => {
|
||||
return applet.state === '1F01' || applet.state === '1F00';
|
||||
});
|
||||
this._updateStatus(list.length + ' applets read from SIM');
|
||||
this._updateUI(list);
|
||||
});
|
||||
},
|
||||
|
||||
_updateUI: function updateUI(parsedApplets) {
|
||||
var div = document.querySelector('#applets-view div');
|
||||
div.innerHTML = '';
|
||||
parsedApplets.forEach((applet) => {
|
||||
var button = document.createElement('button');
|
||||
var state = (applet.state === '1F01') ? 'active' : 'not active' ;
|
||||
button.innerHTML = '<p>' + applet.aid + '</p>' +
|
||||
'<p> contactless state: ' + state + '</p>';
|
||||
|
||||
button.addEventListener('click',
|
||||
() => this._toggleApplet(applet.state === '1F00', applet.aid));
|
||||
div.appendChild(button);
|
||||
});
|
||||
},
|
||||
|
||||
_toggleApplet: function toggleApplet(activate, aid) {
|
||||
SIMAccessManager.toggleApplet(activate, aid)
|
||||
.then((success) => {
|
||||
if(success) {
|
||||
this._updateStatus(aid + ' ' + ((activate) ? 'active' : 'not active'));
|
||||
return this._readDataRefreshUI();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this._updateStatus('Failed to toggle applets', error);
|
||||
});
|
||||
},
|
||||
|
||||
_updateStatus: function showStatus(status, error) {
|
||||
console.log(status);
|
||||
var selector = '.success';
|
||||
if(error) {
|
||||
console.log(error);
|
||||
selector = '.error';
|
||||
status += ', error: ' + error;
|
||||
}
|
||||
document.querySelector(selector).textContent = status;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
window.addEventListener('load', () => WalletDemo.init());
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
'use strict';
|
||||
|
||||
/* exported Utils, APDU, AID */
|
||||
|
||||
const Utils = {
|
||||
byteToHexString: function byteToHexString(uint8arr) {
|
||||
if (!uint8arr) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var hexStr = '';
|
||||
for (var i = 0; i < uint8arr.length; i++) {
|
||||
var hex = (uint8arr[i] & 0xff).toString(16);
|
||||
hex = (hex.length === 1) ? '0' + hex : hex;
|
||||
hexStr += hex;
|
||||
}
|
||||
return hexStr.toUpperCase();
|
||||
},
|
||||
|
||||
hexStringToByte: function hexStringToByte(str) {
|
||||
var a = [];
|
||||
for(var i = 0, len = str.length; i < len; i+=2) {
|
||||
a.push(parseInt(str.substr(i,2),16));
|
||||
}
|
||||
return new Uint8Array(a);
|
||||
},
|
||||
|
||||
joinUint8Arrays: function joinUint8Arrays() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var length = args.reduce(function(a, b) { return a + b.length; }, 0);
|
||||
var out = new Uint8Array(length);
|
||||
|
||||
args.reduce(function(previousLen, buffer) {
|
||||
out.set(buffer, previousLen);
|
||||
return previousLen + buffer.length;
|
||||
}, 0);
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
whilePromise: function whilePromise(condition, action, init) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var loop = (value) => {
|
||||
if (condition(value)) {
|
||||
return resolve(value);
|
||||
}
|
||||
|
||||
return action(value).then(loop).catch(reject);
|
||||
};
|
||||
loop(init);
|
||||
});
|
||||
},
|
||||
|
||||
parseAppletsData: function parseAppletsData(data) {
|
||||
var list = [];
|
||||
for(var i = 0, len = data.length; i < len;) {
|
||||
if(data[i] === 0x61) {
|
||||
var end = i+2 + data[i+1];
|
||||
var appletData = data.subarray(i+2, end);
|
||||
var applet = this._parseSingleApplet(appletData);
|
||||
|
||||
if(applet) {
|
||||
list.push(applet);
|
||||
}
|
||||
i = end;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
},
|
||||
|
||||
_parseSingleApplet: function _parseSingleApplet(data) {
|
||||
// checking for AID tag
|
||||
if (data[0] === 0x4F) {
|
||||
var applet = {
|
||||
aid: this.byteToHexString(data.subarray(2, 2 + data[1])),
|
||||
state: this.byteToHexString(data.subarray(data[1] + 5))
|
||||
};
|
||||
return applet;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
CRS AID, TAGS and APDUs based on:
|
||||
GlobalPlatform Card Technology Contactless Services Card Specification v2.2
|
||||
- Amendment C Version 1.1.1
|
||||
*/
|
||||
|
||||
const AID = {
|
||||
CRS: Utils.hexStringToByte('A00000015143525300')
|
||||
};
|
||||
|
||||
const APDU = {
|
||||
CRS: {
|
||||
getStatusAll1st: { cla: 0x80, ins: 0xF2, p1: 0x40, p2: 0x00,
|
||||
data: new Uint8Array([
|
||||
0x4F, 0x00, // applet AID, empty here
|
||||
0x5C, 0x03, // tag list to be returned
|
||||
0x4F, // AID
|
||||
0x9F, 0x70 // applet life cycle state - first byte;
|
||||
// contactless activation state - second byte
|
||||
])
|
||||
},
|
||||
|
||||
getStatusAllNext: { cla: 0x80, ins: 0xF2, p1: 0x40, p2: 0x01,
|
||||
data: new Uint8Array([0x4F, 0x00, 0x5C, 0x03, 0x4F, 0x9F, 0x70])
|
||||
},
|
||||
|
||||
activateCLF: (aid) => {
|
||||
return { cla: 0x80, ins: 0xF0, p1: 0x01, p2: 0x01, data:
|
||||
Utils.joinUint8Arrays([0x4F, 0x0F], Utils.hexStringToByte(aid))
|
||||
};
|
||||
},
|
||||
|
||||
deactivateCLF: (aid) => {
|
||||
return { cla: 0x80, ins: 0xF0, p1: 0x01, p2: 0x00, data:
|
||||
Utils.joinUint8Arrays([0x4F, 0x0F], Utils.hexStringToByte(aid))
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
12
assets/webapps/uitest.gaiamobile.org/js/API/sendsms.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
var sendSMS = document.querySelector('#send-sms');
|
||||
if (sendSMS) {
|
||||
sendSMS.onclick = function() {
|
||||
var sms = new MozActivity({
|
||||
name: 'new', // Possible compose-sms in future versions
|
||||
data: {
|
||||
type: 'websms/sms',
|
||||
number: '+46777888999'
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
31
assets/webapps/uitest.gaiamobile.org/js/API/shareimage.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
var shareImage = document.querySelector("#share-image"),
|
||||
imgToShare = document.querySelector("#image-to-share");
|
||||
if (shareImage && imgToShare) {
|
||||
shareImage.onclick = function () {
|
||||
if(imgToShare.naturalWidth > 0) {
|
||||
// Create dummy canvas
|
||||
var blobCanvas = document.createElement("canvas");
|
||||
blobCanvas.width = imgToShare.width;
|
||||
blobCanvas.height = imgToShare.height;
|
||||
|
||||
// Get context and draw image
|
||||
var blobCanvasContext = blobCanvas.getContext("2d");
|
||||
blobCanvasContext.drawImage(imgToShare, 0, 0);
|
||||
|
||||
// Export to blob and share through a Web Activitiy
|
||||
blobCanvas.toBlob(function (blob) {
|
||||
var sharingImage = new MozActivity({
|
||||
name: "share",
|
||||
data: {
|
||||
type: "image/*",
|
||||
number: 1,
|
||||
blobs: [blob]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
alert("Image failed to load, can't be shared");
|
||||
}
|
||||
}
|
||||
}
|
||||
13
assets/webapps/uitest.gaiamobile.org/js/API/shareurl.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
var share = document.querySelector('#share');
|
||||
if (share) {
|
||||
share.onclick = function() {
|
||||
var sharing = new MozActivity({
|
||||
name: 'share',
|
||||
data: {
|
||||
type: 'url', // Possibly text/html in future versions,
|
||||
number: 1,
|
||||
url: 'http://www.mozilla.org'
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
59
assets/webapps/uitest.gaiamobile.org/js/API/viewphoto.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// The image we want to view
|
||||
var imageblob;
|
||||
|
||||
// Create an image from a canvas
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = 600;
|
||||
canvas.height = 800;
|
||||
var context = canvas.getContext('2d');
|
||||
context.font = '20pt Arial';
|
||||
context.fillStyle = 'red';
|
||||
context.fillText('Hello World', 100, 100);
|
||||
context.strokeStyle = 'blue';
|
||||
context.lineWidth = 4;
|
||||
context.strokeRect(0, 0, 600, 800);
|
||||
|
||||
canvas.toBlob(function(blob) {
|
||||
imageblob = blob;
|
||||
}, 'image/png');
|
||||
|
||||
document.getElementById('view').onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'image/png',
|
||||
blob: imageblob
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
document.getElementById('viewsave').onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'image/png',
|
||||
blob: imageblob,
|
||||
allowSave: true,
|
||||
filename: 'downloads/uitests/helloworldhello.png'
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
|
||||
document.getElementById('viewError').onclick = function() {
|
||||
var a = new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'image/jpeg',
|
||||
blob: new Blob(['empty-image'], {'type': 'image/jpeg'})
|
||||
}
|
||||
});
|
||||
a.onsuccess = function() {
|
||||
console.log('activity returns', JSON.stringify(a.result));
|
||||
};
|
||||
};
|
||||
25
assets/webapps/uitest.gaiamobile.org/js/HW/battery.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
function batteryTest() {
|
||||
var battery = window.navigator.battery;
|
||||
var charging = document.getElementById('charging');
|
||||
var level = document.getElementById('level');
|
||||
var chargingTime = document.getElementById('charging-time');
|
||||
var disChargingTime = document.getElementById('discharging-time');
|
||||
|
||||
function update(evt) {
|
||||
charging.textContent = battery.charging;
|
||||
level.textContent = battery.level * 100;
|
||||
chargingTime.textContent = battery.chargingTime;
|
||||
disChargingTime.textContent = battery.dischargingTime;
|
||||
}
|
||||
|
||||
battery.addEventListener('chargingchange', update);
|
||||
battery.addEventListener('levelchange', update);
|
||||
battery.addEventListener('onchargingtimechange', update);
|
||||
battery.addEventListener('ondischargingtimechange', update);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
window.addEventListener('load', batteryTest);
|
||||
99
assets/webapps/uitest.gaiamobile.org/js/HW/bluetooth.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
'use strict';
|
||||
|
||||
function bluetoothTest() {
|
||||
var bluetooth = window.navigator.mozBluetooth;
|
||||
var settings = window.navigator.mozSettings;
|
||||
var switchButton = document.getElementById('switch');
|
||||
var state = document.getElementById('state');
|
||||
var address = document.getElementById('address');
|
||||
var version = document.getElementById('version');
|
||||
var btHelper = new BluetoothHelper();
|
||||
|
||||
function update() {
|
||||
var req = settings.createLock().get('bluetooth.enabled');
|
||||
req.onsuccess = function() {
|
||||
var isBluetoothEnabled = req.result['bluetooth.enabled'];
|
||||
switchButton.checked = isBluetoothEnabled;
|
||||
if (!isBluetoothEnabled) {
|
||||
state.innerHTML = 'Off';
|
||||
address.innerHTML = 'unknown';
|
||||
}
|
||||
else {
|
||||
state.innerHTML = 'On';
|
||||
if (!btHelper.v2) {
|
||||
btHelper.getAddress(function bt_gotAddress(addr) {
|
||||
if (isBluetoothEnabled) {
|
||||
address.innerHTML = addr;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Click on switch will only update settings and
|
||||
// it takes some time for bluetooth hardware to turns on.
|
||||
// That's why we should listen to 'enabled' event of bluetooth
|
||||
function bluetoothSwitch() {
|
||||
settings.createLock().set({'bluetooth.enabled': switchButton.checked});
|
||||
// set enabled stat for BT APIv2
|
||||
if (switchButton.checked) {
|
||||
btHelper.enable();
|
||||
} else {
|
||||
btHelper.disable();
|
||||
}
|
||||
}
|
||||
|
||||
if (btHelper.v2) {
|
||||
version.textContent = "v2";
|
||||
bluetooth.addEventListener('attributechanged', (evt) => {
|
||||
for (var i in evt.attrs) {
|
||||
switch (evt.attrs[i]) {
|
||||
case 'defaultAdapter':
|
||||
var adapter = bluetooth.defaultAdapter;
|
||||
if (adapter.address) { // for default On case
|
||||
address.innerHTML = adapter.address;
|
||||
}
|
||||
adapter.addEventListener('attributechanged', (evt) => {
|
||||
for (var i in evt.attrs) {
|
||||
switch (evt.attrs[i]) {
|
||||
case 'state':
|
||||
if (adapter.state === 'enabled') {
|
||||
switchButton.checked = true;
|
||||
state.innerHTML = 'On';
|
||||
} else if (adapter.state === 'disabled') {
|
||||
switchButton.checked = false;
|
||||
state.innerHTML = 'Off';
|
||||
address.innerHTML = 'unknown';
|
||||
}
|
||||
break;
|
||||
case 'address':
|
||||
address.innerHTML = adapter.address;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
version.textContent = "v1";
|
||||
bluetooth.addEventListener('adapteradded', update);
|
||||
bluetooth.addEventListener('enabled', update);
|
||||
bluetooth.addEventListener('disabled', update);
|
||||
}
|
||||
|
||||
// init
|
||||
switchButton.addEventListener('click', bluetoothSwitch);
|
||||
|
||||
// Update bluetooth information in the first load
|
||||
update();
|
||||
}
|
||||
|
||||
window.addEventListener('load', bluetoothTest);
|
||||
|
||||
19
assets/webapps/uitest.gaiamobile.org/js/HW/camera.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
var clickHandlers = {
|
||||
'camera': function() {
|
||||
var activity = new MozActivity({
|
||||
name: 'record',
|
||||
data: {
|
||||
type: 'photos'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id]) {
|
||||
clickHandlers[evt.target.id].call(this, evt);
|
||||
}
|
||||
});
|
||||
|
||||
154
assets/webapps/uitest.gaiamobile.org/js/HW/fmradio.js
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
'use strict';
|
||||
|
||||
function FMRadioTest() {
|
||||
var messageText = document.getElementById('message');
|
||||
var frequencyMin = document.getElementById('frequency-min');
|
||||
var frequencyMax = document.getElementById('frequency-max');
|
||||
|
||||
var container = document.getElementById('container');
|
||||
var controlPanel = document.getElementById('controlPanel');
|
||||
|
||||
var frequencyInput = document.getElementById('frequency');
|
||||
var switchBtn = document.getElementById('switch');
|
||||
var seekUpBtn = document.getElementById('seekUp');
|
||||
var seekDownBtn = document.getElementById('seekDown');
|
||||
var seekAndCancelBtn = document.getElementById('seekAndCancel');
|
||||
|
||||
if (!navigator.mozFMRadio) {
|
||||
console.log('mozFMRadio not support');
|
||||
container.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var radio = navigator.mozFMRadio;
|
||||
var prevFreq = radio.frequencyLowerBound;
|
||||
var lock = false;
|
||||
if (radio.enabled) {
|
||||
prevFreq = radio.frequency;
|
||||
}
|
||||
|
||||
switchBtn.textContent = radio.enabled;
|
||||
frequencyMin.textContent = radio.frequencyLowerBound;
|
||||
frequencyMax.textContent = radio.frequencyUpperBound;
|
||||
controlPanel.hidden = !radio.enabled;
|
||||
frequencyInput.value = prevFreq;
|
||||
|
||||
togglePanel();
|
||||
toggleControlPanel();
|
||||
|
||||
radio.addEventListener('antennaavailablechange', togglePanel);
|
||||
radio.addEventListener('frequencychange', frequencyChanged);
|
||||
|
||||
frequencyInput.addEventListener('change', setFrequency);
|
||||
switchBtn.addEventListener('click', onclick_radioSwitch);
|
||||
seekUpBtn.addEventListener('click', seek);
|
||||
seekDownBtn.addEventListener('click', seek);
|
||||
seekAndCancelBtn.addEventListener('click', seekAndCancel);
|
||||
window.addEventListener('unload', uninit);
|
||||
|
||||
function logError() {
|
||||
messageText.textContent = this.error.name;
|
||||
}
|
||||
|
||||
// show/hide panels
|
||||
function togglePanel() {
|
||||
if (radio && radio.antennaAvailable) {
|
||||
container.hidden = false;
|
||||
messageText.textContent = '';
|
||||
}
|
||||
else {
|
||||
container.hidden = true;
|
||||
messageText.textContent = 'Headphone is required for FMRadio';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleControlPanel() {
|
||||
lock = false;
|
||||
controlPanel.hidden = !radio.enabled;
|
||||
if (radio.enabled) {
|
||||
switchBtn.textContent = 'Stop';
|
||||
}
|
||||
else {
|
||||
switchBtn.textContent = 'Start';
|
||||
}
|
||||
switchBtn.disabled = false;
|
||||
}
|
||||
|
||||
function onclick_radioSwitch() {
|
||||
if (lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
// lock button until request is finished
|
||||
lock = true;
|
||||
switchBtn.disabled = true;
|
||||
|
||||
var req;
|
||||
if (!radio.enabled) {
|
||||
messageText.textContent = 'turning on FM, please wait...';
|
||||
req = radio.enable(prevFreq);
|
||||
}
|
||||
else {
|
||||
req = radio.disable();
|
||||
}
|
||||
req.onsuccess = req.onerror = function() {
|
||||
toggleControlPanel();
|
||||
if (this.error) {
|
||||
messageText.textContent = this.error.name;
|
||||
}
|
||||
else {
|
||||
messageText.textContent = '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function frequencyChanged() {
|
||||
console.log('freq changed');
|
||||
frequencyInput.value = radio.frequency;
|
||||
prevFreq = radio.frequency;
|
||||
}
|
||||
|
||||
function setFrequency() {
|
||||
var req = radio.setFrequency(frequencyInput.value);
|
||||
req.onerror = logError;
|
||||
}
|
||||
|
||||
function seek(evt) {
|
||||
var req;
|
||||
if (evt.target.id == 'seekUp') {
|
||||
req = radio.seekUp();
|
||||
}
|
||||
else {
|
||||
req = radio.seekDown();
|
||||
}
|
||||
req.onerror = logError;
|
||||
}
|
||||
|
||||
function seekAndCancel(evt) {
|
||||
var req = radio.seekUp();
|
||||
console.log('seekUP()');
|
||||
req.onsuccess = function() {
|
||||
console.log('seekUP().onsuccess');
|
||||
};
|
||||
req.onerror = function() {
|
||||
console.log('seekUP().onerror:' + this.error.name);
|
||||
};
|
||||
|
||||
var cancel = radio.cancelSeek();
|
||||
console.log('cancel()');
|
||||
cancel.onsuccess = function() {
|
||||
console.log('canelSeek().onsuccess');
|
||||
};
|
||||
cancel.onerror = function() {
|
||||
console.log('cancelSeek().onerror:' + this.error.name);
|
||||
};
|
||||
}
|
||||
|
||||
function uninit() {
|
||||
if (radio.enabled) {
|
||||
radio.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', FMRadioTest);
|
||||
23
assets/webapps/uitest.gaiamobile.org/js/HW/headphone.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
function headphoneTest() {
|
||||
var status_display = document.getElementById('status');
|
||||
var acm = navigator.mozAudioChannelManager;
|
||||
if (acm) {
|
||||
acm.addEventListener('headphoneschange', update.bind(this));
|
||||
}
|
||||
else {
|
||||
status_display.textContent = 'mozAudioChannelManager not found';
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (acm.headphones) {
|
||||
status_display.textContent = 'plugged';
|
||||
}
|
||||
else {
|
||||
status_display.textContent = 'unpluged';
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
window.addEventListener('load', headphoneTest);
|
||||
71
assets/webapps/uitest.gaiamobile.org/js/HW/lcd.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
'use strict';
|
||||
|
||||
var LCDTest = {
|
||||
get fullscreenDiv() {
|
||||
delete this.fullScreenDiv;
|
||||
return this.fullScreenDiv = document.getElementById('fullscreen-div');
|
||||
},
|
||||
firstTimeHint: null,
|
||||
init: function() {
|
||||
document.body.addEventListener('click', this);
|
||||
this.fullscreenDiv.addEventListener('click',
|
||||
this.exitFullscreen.bind(this));
|
||||
this.firstTimeHint = true;
|
||||
},
|
||||
enterFullscreen: function(color) {
|
||||
this.fullscreenDiv.mozRequestFullScreen();
|
||||
this.fullscreenDiv.classList.remove('invisible');
|
||||
this.fullscreenDiv.classList.remove('red');
|
||||
this.fullscreenDiv.classList.remove('green');
|
||||
this.fullscreenDiv.classList.remove('blue');
|
||||
this.fullscreenDiv.classList.remove('white');
|
||||
this.fullscreenDiv.classList.remove('black');
|
||||
this.fullscreenDiv.classList.add(color);
|
||||
if (this.firstTimeHint) {
|
||||
this.firstTimeHint = false;
|
||||
alert('Tap screen again to exit fullscreen mode');
|
||||
}
|
||||
},
|
||||
exitFullscreen: function() {
|
||||
document.mozCancelFullScreen();
|
||||
this.fullscreenDiv.classList.add('invisible');
|
||||
},
|
||||
turnOn: function() {
|
||||
navigator.mozPower.screenEnabled = true;
|
||||
|
||||
// no longer need the lock
|
||||
this.wakeLock.unlock();
|
||||
},
|
||||
// Only handle clicks on button, clicks on div is handled by exitFullscreen
|
||||
handleEvent: function(evt) {
|
||||
switch (evt.target.id) {
|
||||
case 'red':
|
||||
this.enterFullscreen('red');
|
||||
break;
|
||||
case 'green':
|
||||
this.enterFullscreen('green');
|
||||
break;
|
||||
case 'blue':
|
||||
this.enterFullscreen('blue');
|
||||
break;
|
||||
case 'white':
|
||||
this.enterFullscreen('white');
|
||||
break;
|
||||
case 'black':
|
||||
this.enterFullscreen('black');
|
||||
break;
|
||||
case 'off':
|
||||
// We are going to shutdown the screen, this may lead to
|
||||
// the sleep of CPU. Use wake lock to prevent from sleeping
|
||||
// which cause setTimeout stop working.
|
||||
this.wakeLock = window.navigator.requestWakeLock('screen');
|
||||
|
||||
setTimeout(this.turnOn.bind(this), 3000);
|
||||
navigator.mozPower.screenEnabled = false;
|
||||
break;
|
||||
}
|
||||
},
|
||||
wakeLock: null
|
||||
};
|
||||
|
||||
window.addEventListener('load', LCDTest.init.bind(LCDTest));
|
||||
26
assets/webapps/uitest.gaiamobile.org/js/HW/physicalkeys.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
/*
|
||||
var ringer = function callsHandler() {
|
||||
var phoneSoundURL = new SettingsURL();
|
||||
SettingsListener.observe('dialer.ringtone', '', function(value) {
|
||||
ringtonePlayer.pause();
|
||||
ringtonePlayer.src = phoneSoundURL.set(value);
|
||||
});
|
||||
var ringtonePlayer = ringtonePlayer;
|
||||
ringtonePlayer = new Audio();
|
||||
ringtonePlayer.mozAudioChannelType = 'ringer';
|
||||
ringtonePlayer.src = phoneSoundURL.get();
|
||||
ringtonePlayer.loop = false;
|
||||
|
||||
window.addEventListener('home', function (evt) {
|
||||
evt.preventDefault();
|
||||
ringtonePlayer.play();
|
||||
});
|
||||
window.addEventListener('click', function (evt) {
|
||||
evt.stopPropagation();
|
||||
ringtonePlayer.play();
|
||||
});
|
||||
}();
|
||||
|
||||
*/
|
||||
173
assets/webapps/uitest.gaiamobile.org/js/HW/radio.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
'use strict';
|
||||
|
||||
function IMEI(serviceId) {
|
||||
var IMEIText = document.getElementById('IMEI');
|
||||
|
||||
navigator.mozTelephony.dial('*#06#', serviceId).then(function(call) {
|
||||
return call.result.then(function getIMEI(message) {
|
||||
if (message && (message.serviceCode === 'scImei') &&
|
||||
message.statusMessage) {
|
||||
IMEIText.textContent = message.statusMessage;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// SIM card information such as ICCID, MSISDN
|
||||
function iccInfo(mozIcc, parentContainer)
|
||||
{
|
||||
var messageText = parentContainer.querySelector('[data-type="icc-message"]');
|
||||
var ICCIDText = parentContainer.querySelector('[data-type="ICCID"]');
|
||||
var MSISDNText = parentContainer.querySelector('[data-type="MSISDN"]');
|
||||
var container = parentContainer.querySelector('[data-type="icc-container"]');
|
||||
|
||||
mozIcc.addEventListener('cardstatechange', update_icc);
|
||||
update_icc();
|
||||
|
||||
function update_icc() {
|
||||
if (mozIcc.cardState == null ||
|
||||
!mozIcc.iccInfo) {
|
||||
container.hidden = true;
|
||||
messageText.textContent = 'Airplane mode on';
|
||||
}
|
||||
else {
|
||||
ICCIDText.textContent = mozIcc.iccInfo.iccid;
|
||||
MSISDNText.textContent = mozIcc.iccInfo.msisdn || 'Unknown';
|
||||
container.hidden = false;
|
||||
messageText.textContent = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// data & voice network information
|
||||
function mobileConnectionInfo(conn, parentContainer)
|
||||
{
|
||||
conn.addEventListener('datachange', update_data_network);
|
||||
conn.addEventListener('voicechange', update_voice_network);
|
||||
update_data_network();
|
||||
update_voice_network();
|
||||
|
||||
function update_data_network() {
|
||||
var container = parentContainer.
|
||||
querySelector('[data-type="data-container"]');
|
||||
var message = parentContainer.
|
||||
querySelector('[data-type="data-message"]');
|
||||
var result = update_network(conn.data, container, message);
|
||||
}
|
||||
function update_voice_network() {
|
||||
var container = parentContainer.
|
||||
querySelector('[data-type="voice-container"]');
|
||||
var message = parentContainer.
|
||||
querySelector('[data-type="voice-message"]');
|
||||
var result = update_network(conn.voice, container, message);
|
||||
}
|
||||
function update_network(network, container, message) {
|
||||
if (!conn || conn.radioState != 'enabled' ||
|
||||
!network || !network.connected || network.emergencyCallsOnly) {
|
||||
container.hidden = true;
|
||||
message.textContent = 'Disconnect';
|
||||
}
|
||||
else {
|
||||
container.querySelector('[data-type="network"]').textContent =
|
||||
network.network.shortName;
|
||||
container.querySelector('[data-type="LAC"]').textContent =
|
||||
network.cell.gsmCellId;
|
||||
container.querySelector('[data-type="CID"]').textContent =
|
||||
network.cell.gsmLocationAreaCode;
|
||||
container.querySelector('[data-type="strength"]').textContent =
|
||||
network.relSignalStrength;
|
||||
container.querySelector('[data-type="type"]').textContent =
|
||||
network.type;
|
||||
container.querySelector('[data-type="roaming"]').textContent =
|
||||
network.roaming;
|
||||
container.hidden = false;
|
||||
message.textContent = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function radioTest() {
|
||||
var iccIdIndex = [];
|
||||
var icc_num = 0;
|
||||
|
||||
if (('mozIccManager' in navigator) &&
|
||||
navigator.mozIccManager &&
|
||||
('mozMobileConnections' in navigator) &&
|
||||
navigator.mozMobileConnections.length > 0) {
|
||||
var iccManager = navigator.mozIccManager;
|
||||
|
||||
// IMEI should be the same no matter which connection is used
|
||||
IMEI(0);
|
||||
|
||||
var template = document.getElementById('connection-template');
|
||||
template.hidden = true;
|
||||
|
||||
if (iccManager.iccIds.length > 0) {
|
||||
for (var i = 0; i < iccManager.iccIds.length; i++) {
|
||||
addIcc(iccManager.iccIds[i], i);
|
||||
showInfo(iccManager.iccIds[i]);
|
||||
}
|
||||
} else {
|
||||
document.getElementById('global-message').textContent =
|
||||
'SIM card not found, insert SIM and turn off airplane mode';
|
||||
}
|
||||
|
||||
iccManager.addEventListener('iccdetected', function(evt) {
|
||||
document.getElementById('global-message').textContent = '';
|
||||
addIcc(evt.iccId);
|
||||
showInfo(evt.iccId);
|
||||
});
|
||||
iccManager.addEventListener('iccundetected', function(evt) {
|
||||
removeIcc(evt.iccId);
|
||||
});
|
||||
}
|
||||
else {
|
||||
alert('navigator.mozIccManager or navigator.mozMobileConnections ' +
|
||||
'not supported');
|
||||
}
|
||||
|
||||
function showInfo(iccId) {
|
||||
var i = iccIdIndex[iccId];
|
||||
var connectionContainer = document.getElementById('connection#' + i);
|
||||
|
||||
// show ICC#
|
||||
connectionContainer.querySelector('[data-type="SIM-number"]')
|
||||
.textContent = i;
|
||||
|
||||
iccInfo(navigator.mozIccManager.getIccById(iccId), connectionContainer);
|
||||
|
||||
mobileConnectionInfo(navigator.mozMobileConnections[i],
|
||||
connectionContainer);
|
||||
}
|
||||
|
||||
function addIcc(iccId) {
|
||||
var connectionContainer = document.getElementById('connection#' + icc_num);
|
||||
if (!connectionContainer) {
|
||||
// copy connection-template then modify it.
|
||||
connectionContainer = template.cloneNode(true);
|
||||
connectionContainer.id = 'connection#' + icc_num;
|
||||
connectionContainer.hidden = false;
|
||||
|
||||
document.getElementById('content').appendChild(connectionContainer);
|
||||
}
|
||||
|
||||
iccIdIndex[iccId] = icc_num;
|
||||
icc_num++;
|
||||
}
|
||||
|
||||
function removeIcc(iccId) {
|
||||
var i = iccIdIndex[iccId];
|
||||
if (i != null) {
|
||||
iccIdIndex[iccId] = null;
|
||||
// re-organize index
|
||||
for (var key in iccIdIndex) {
|
||||
if (iccIdIndex[key] >= i) {
|
||||
iccIdIndex[key]--;
|
||||
}
|
||||
}
|
||||
icc_num--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', radioTest);
|
||||
17
assets/webapps/uitest.gaiamobile.org/js/HW/ringer.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
var ringtonePlayer = new Audio();
|
||||
|
||||
// Get ringer file path from settings
|
||||
var phoneSoundURL = new SettingsURL();
|
||||
SettingsListener.observe('dialer.ringtone', '', function(value) {
|
||||
ringtonePlayer.pause();
|
||||
ringtonePlayer.src = phoneSoundURL.set(value);
|
||||
});
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (evt.target.id == 'ring') {
|
||||
ringtonePlayer.play();
|
||||
}
|
||||
});
|
||||
|
||||
85
assets/webapps/uitest.gaiamobile.org/js/HW/sdcards.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
'use strict';
|
||||
|
||||
// Whenever the check button is clicked,
|
||||
// we delete previously displayed result (if any) then show new result
|
||||
function checkSDCards() {
|
||||
var sdcards = navigator.getDeviceStorages('sdcard');
|
||||
document.getElementById('numOfCards').textContent = sdcards.length;
|
||||
|
||||
// cardInfo collection
|
||||
var cardsInfo = document.getElementById('cardsInfo');
|
||||
|
||||
// Clear last result
|
||||
while (cardsInfo.firstChild) {
|
||||
cardsInfo.removeChild(cardsInfo.firstChild);
|
||||
}
|
||||
|
||||
// Show new result
|
||||
sdcards.forEach(function showCards(card, index, cards) {
|
||||
// To add a new SD cardInfo,
|
||||
// first copy one from card_template then modify it.
|
||||
var template = document.getElementById('card_template');
|
||||
var cardInfo = template.cloneNode(true);
|
||||
cardInfo.id = 'card' + index;
|
||||
cardInfo.classList.remove('invisible');
|
||||
|
||||
var cardNo =
|
||||
cardInfo.querySelector('#card' + index + ' > .no > span');
|
||||
var cardStatus =
|
||||
cardInfo.querySelector('#card' + index + ' > .status > span');
|
||||
var cardSpace =
|
||||
cardInfo.querySelector('#card' + index + ' > .space');
|
||||
var cardSpaceTotal =
|
||||
cardInfo.querySelector('#card' + index + ' > .space > .totalSpace');
|
||||
var cardSpaceFree =
|
||||
cardInfo.querySelector('#card' + index + ' > .space > .freeSpace');
|
||||
var cardSpaceUsed =
|
||||
cardInfo.querySelector('#card' + index + ' > .space > .usedSpace');
|
||||
cardNo.textContent = index;
|
||||
|
||||
// Show availability and hide other infomation if not available
|
||||
var reqAvailable = card.available();
|
||||
reqAvailable.onsuccess = function() {
|
||||
cardStatus.textContent = this.result;
|
||||
if (this.result == 'shared' || this.result == 'unavailable') {
|
||||
cardSpace.classList.add('invisible');
|
||||
}
|
||||
};
|
||||
reqAvailable.onerror = function() {
|
||||
cardStatus.textContent = 'Unable to get sdcard status' + this.error.name;
|
||||
};
|
||||
|
||||
// Show space info.
|
||||
// If the other space info is already updated, show total space
|
||||
var reqFree = card.freeSpace();
|
||||
reqFree.onsuccess = function() {
|
||||
var spaceGB = this.result / Math.pow(10, 9);
|
||||
cardSpaceFree.textContent = spaceGB;
|
||||
if (cardSpaceTotal.textContent == '') {
|
||||
cardSpaceTotal.textContent = spaceGB;
|
||||
}
|
||||
else {
|
||||
cardSpaceTotal.textContent =
|
||||
parseFloat(cardSpaceTotal.textContent) + spaceGB;
|
||||
}
|
||||
};
|
||||
var reqUsed = card.usedSpace();
|
||||
reqUsed.onsuccess = function() {
|
||||
var spaceGB = this.result / Math.pow(10, 9);
|
||||
cardSpaceUsed.textContent = spaceGB;
|
||||
if (cardSpaceTotal.textContent == '') {
|
||||
cardSpaceTotal.textContent = spaceGB;
|
||||
}
|
||||
else {
|
||||
cardSpaceTotal.textContent =
|
||||
parseFloat(cardSpaceTotal.textContent) + spaceGB;
|
||||
}
|
||||
};
|
||||
|
||||
cardsInfo.appendChild(cardInfo);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
document.getElementById('check').addEventListener('click', checkSDCards);
|
||||
});
|
||||
67
assets/webapps/uitest.gaiamobile.org/js/HW/sensors.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
'use strict';
|
||||
|
||||
function sensorTest() {
|
||||
var interval = document.getElementById('interval');
|
||||
var accelerationX = document.getElementById('acceleration-x');
|
||||
var accelerationY = document.getElementById('acceleration-y');
|
||||
var accelerationZ = document.getElementById('acceleration-z');
|
||||
var accelerationGX = document.getElementById('acceleration-gravity-x');
|
||||
var accelerationGY = document.getElementById('acceleration-gravity-y');
|
||||
var accelerationGZ = document.getElementById('acceleration-gravity-z');
|
||||
var rotAlpha = document.getElementById('rot-rate-alpha');
|
||||
var rotBeta = document.getElementById('rot-rate-beta');
|
||||
var rotGamma = document.getElementById('rot-rate-gamma');
|
||||
var orientationAlpha = document.getElementById('orientation-alpha');
|
||||
var orientationBeta = document.getElementById('orientation-beta');
|
||||
var orientationGamma = document.getElementById('orientation-gamma');
|
||||
var proximityMin = document.getElementById('proximity-min');
|
||||
var proximityMax = document.getElementById('proximity-max');
|
||||
var proximityValue = document.getElementById('proximity-value');
|
||||
var compassCalibration = document.getElementById('compassneedscalibration');
|
||||
|
||||
window.addEventListener('devicemotion', function(e) {
|
||||
interval.textContent = e.interval;
|
||||
|
||||
if (e.acceleration) {
|
||||
accelerationX.textContent = e.acceleration.x;
|
||||
accelerationY.textContent = e.acceleration.y;
|
||||
accelerationZ.textContent = e.acceleration.z;
|
||||
}
|
||||
|
||||
if (e.accelerationIncludingGravity) {
|
||||
accelerationGX.textContent = e.accelerationIncludingGravity.x;
|
||||
accelerationGY.textContent = e.accelerationIncludingGravity.y;
|
||||
accelerationGZ.textContent = e.accelerationIncludingGravity.z;
|
||||
}
|
||||
|
||||
if (e.rotationRate) {
|
||||
rotAlpha.textContent = e.rotationRate.alpha;
|
||||
rotBeta.textContent = e.rotationRate.beta;
|
||||
rotGamma.textContent = e.rotationRate.gamma;
|
||||
}
|
||||
}, false);
|
||||
|
||||
window.addEventListener('deviceorientation', function(e) {
|
||||
if (e) {
|
||||
orientationAlpha.textContent = e.alpha;
|
||||
orientationBeta.textContent = e.beta;
|
||||
orientationGamma.textContent = e.gamma;
|
||||
}
|
||||
}, false);
|
||||
|
||||
window.addEventListener('devicelight', function(event) {
|
||||
document.getElementById('light').textContent = event.value;
|
||||
});
|
||||
|
||||
window.addEventListener('deviceproximity', function(event) {
|
||||
proximityMin.textContent = event.min;
|
||||
proximityMax.textContent = event.max;
|
||||
proximityValue.textContent = event.value;
|
||||
});
|
||||
|
||||
window.addEventListener('compassneedscalibration', function(e) {
|
||||
compassCalibration.textContent = 'true';
|
||||
}, false);
|
||||
}
|
||||
|
||||
window.addEventListener('load', sensorTest);
|
||||
448
assets/webapps/uitest.gaiamobile.org/js/HW/touchscreen.js
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
'use strict';
|
||||
|
||||
// test result indicator
|
||||
var Status = {
|
||||
NOT_FINISHED: 1,
|
||||
FINISHED: 2,
|
||||
ABORT: 3
|
||||
};
|
||||
|
||||
// UTILS
|
||||
var util = {
|
||||
// Use this function to get correct click position on canvas
|
||||
relMouseCoords: function(canvas, evt) {
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
var eventX = evt.clientX - rect.left;
|
||||
var eventY = evt.clientY - rect.top;
|
||||
return {x: eventX, y: eventY};
|
||||
},
|
||||
|
||||
clearScreen: function(ctx) {
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
},
|
||||
|
||||
drawRectFromCoord: function(ctx, x1, y1, x2, y2) {
|
||||
ctx.translate(0.5, 0.5);
|
||||
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
|
||||
ctx.translate(-0.5, -0.5);
|
||||
},
|
||||
|
||||
// Fill rect without overlapping border,
|
||||
fillRectFromCoord: function(ctx, x1, y1, x2, y2) {
|
||||
ctx.fillRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1);
|
||||
},
|
||||
|
||||
drawCircle: function(ctx, x, y, radius, width, color) {
|
||||
ctx.lineWidth = width;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.translate(0.5, 0.5);
|
||||
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
||||
ctx.translate(-0.5, -0.5);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
},
|
||||
|
||||
distance_sqr: function(x1, y1, x2, y2) {
|
||||
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
|
||||
},
|
||||
|
||||
// Return the index less or equal to num
|
||||
binary_search: function(arr, num) {
|
||||
var begin = 0;
|
||||
var end = arr.length - 1;
|
||||
while (begin < end) {
|
||||
var pivot = Math.round((begin + end) / 2);
|
||||
if (num < arr[pivot]) {
|
||||
end = pivot - 1;
|
||||
}
|
||||
else if (num > arr[pivot]) {
|
||||
begin = pivot + 1;
|
||||
}
|
||||
else {
|
||||
return pivot;
|
||||
}
|
||||
}
|
||||
if (num < arr[begin]) {
|
||||
return begin - 1;
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
};
|
||||
|
||||
// TEST 1
|
||||
// Draw 4 circles on the corners and 1 circle in the center
|
||||
var test1 = function(canvas) {
|
||||
var ctx = canvas.getContext('2d');
|
||||
var circle_radius = Math.min(canvas.width, canvas.height) * 0.1;
|
||||
var circles_coord =
|
||||
[[circle_radius, circle_radius],
|
||||
[circle_radius, canvas.height - circle_radius],
|
||||
[canvas.width - circle_radius, circle_radius],
|
||||
[canvas.width - circle_radius, canvas.height - circle_radius],
|
||||
[canvas.width / 2, canvas.height / 2]];
|
||||
var lineWidth = 2;
|
||||
var lineColor = 'red';
|
||||
var lineColorClicked = 'lime';
|
||||
var is_clicked = [];
|
||||
|
||||
var exitIconPositionX;
|
||||
var exitIconPositionY;
|
||||
var exitIconWidth;
|
||||
var exitIconHeight;
|
||||
|
||||
(function init() {
|
||||
util.clearScreen(ctx);
|
||||
|
||||
// Draw not-clicked circle
|
||||
for (var i = 0; i < circles_coord.length; i++) {
|
||||
util.drawCircle(ctx, circles_coord[i][0], circles_coord[i][1],
|
||||
circle_radius, lineWidth, lineColor);
|
||||
is_clicked[i] = false;
|
||||
}
|
||||
// Draw exit icon
|
||||
var image = document.getElementById('icon-exit');
|
||||
exitIconWidth = image.width;
|
||||
exitIconHeight = image.height;
|
||||
// Draw icon below center
|
||||
exitIconPositionX = (canvas.width - exitIconWidth) / 2;
|
||||
exitIconPositionY = canvas.height * 0.65;
|
||||
ctx.drawImage(image, exitIconPositionX, exitIconPositionY);
|
||||
})();
|
||||
|
||||
// Touch test, change circle color if touch point is in the circle.
|
||||
// Test ends if all circles are touched
|
||||
this.test = function(x, y) {
|
||||
// Clicked on exit button
|
||||
if (x > exitIconPositionX && x <= exitIconPositionX + exitIconWidth &&
|
||||
y > exitIconPositionY && y <= exitIconPositionY + exitIconHeight) {
|
||||
return Status.ABORT;
|
||||
}
|
||||
for (var i = 0; i < circles_coord.length; i++) {
|
||||
if (!is_clicked[i] &&
|
||||
util.distance_sqr(x, y, circles_coord[i][0], circles_coord[i][1]) <
|
||||
circle_radius * circle_radius) {
|
||||
// Clear red circle by covering a bigger white circle on it
|
||||
util.drawCircle(ctx, circles_coord[i][0], circles_coord[i][1],
|
||||
circle_radius, lineWidth + 2, 'white');
|
||||
// Draw new circle
|
||||
util.drawCircle(ctx, circles_coord[i][0], circles_coord[i][1],
|
||||
circle_radius, lineWidth, lineColorClicked);
|
||||
|
||||
is_clicked[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < circles_coord.length; i++) {
|
||||
if (!is_clicked[i]) {
|
||||
return Status.NOT_FINISHED;
|
||||
}
|
||||
}
|
||||
// Test over
|
||||
return Status.FINISHED;
|
||||
};
|
||||
};
|
||||
|
||||
// TEST 2
|
||||
// Draw several cubes on the edge of screen
|
||||
var test2 = function(canvas) {
|
||||
var ctx = canvas.getContext('2d');
|
||||
var aspect = canvas.width / canvas.height;
|
||||
var cubes_num_vertical = 20;
|
||||
var cubes_num_horizontal = Math.round(cubes_num_vertical * aspect);
|
||||
var cube_width = canvas.width / cubes_num_horizontal;
|
||||
var cube_height = canvas.height / cubes_num_vertical;
|
||||
|
||||
var cube_coord_x = [];
|
||||
var cube_coord_y = [];
|
||||
var is_clicked_top = [];
|
||||
var is_clicked_bottom = [];
|
||||
var is_clicked_left = [];
|
||||
var is_clicked_right = [];
|
||||
var total_clicked = 0;
|
||||
|
||||
var exitIconPositionX;
|
||||
var exitIconPositionY;
|
||||
var exitIconWidth;
|
||||
var exitIconHeight;
|
||||
|
||||
(function init() {
|
||||
util.clearScreen(ctx);
|
||||
ctx.strokeStyle = 'black';
|
||||
ctx.fillStyle = 'lime';
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
// Because width/height of screen can not be divided evenly
|
||||
// So we precaculate the grid (coordination) of cubes for future use
|
||||
for (var i = 0; i < cubes_num_horizontal; i++) {
|
||||
cube_coord_x[i] = Math.round(cube_width * i);
|
||||
}
|
||||
cube_coord_x[cubes_num_horizontal] = canvas.width - 1;
|
||||
for (var i = 0; i < cubes_num_vertical; i++) {
|
||||
cube_coord_y[i] = Math.round(cube_height * i);
|
||||
}
|
||||
cube_coord_y[cubes_num_vertical] = canvas.height - 1;
|
||||
|
||||
// Draw Top/Bottom/Left/Right cubes
|
||||
for (var i = 0; i < cubes_num_horizontal; i++) {
|
||||
util.drawRectFromCoord(ctx, cube_coord_x[i], cube_coord_y[0],
|
||||
cube_coord_x[i + 1], cube_coord_y[1]);
|
||||
is_clicked_top[i] = false;
|
||||
}
|
||||
for (var i = 0; i < cubes_num_horizontal; i++) {
|
||||
util.drawRectFromCoord(ctx,
|
||||
cube_coord_x[i],
|
||||
cube_coord_y[cubes_num_vertical - 1],
|
||||
cube_coord_x[i + 1],
|
||||
cube_coord_y[cubes_num_vertical]);
|
||||
is_clicked_bottom[i] = false;
|
||||
}
|
||||
for (var i = 0; i < cubes_num_vertical; i++) {
|
||||
util.drawRectFromCoord(ctx,
|
||||
cube_coord_x[0],
|
||||
cube_coord_y[i],
|
||||
cube_coord_x[1],
|
||||
cube_coord_y[i + 1]);
|
||||
is_clicked_left[i] = false;
|
||||
}
|
||||
for (var i = 0; i < cubes_num_vertical; i++) {
|
||||
util.drawRectFromCoord(ctx,
|
||||
cube_coord_x[cubes_num_horizontal - 1],
|
||||
cube_coord_y[i],
|
||||
cube_coord_x[cubes_num_horizontal],
|
||||
cube_coord_y[i + 1]);
|
||||
is_clicked_right[i] = false;
|
||||
}
|
||||
|
||||
// Draw exit icon
|
||||
var image = document.getElementById('icon-exit');
|
||||
exitIconWidth = image.width;
|
||||
exitIconHeight = image.height;
|
||||
exitIconPositionX = (canvas.width - exitIconWidth) / 2;
|
||||
exitIconPositionY = (canvas.height - exitIconHeight) / 2;
|
||||
ctx.drawImage(image, exitIconPositionX, exitIconPositionY);
|
||||
}
|
||||
)();
|
||||
|
||||
// Touch test, fill cubes if touch point is in the cube.
|
||||
// Test ends if all cubes are touched
|
||||
this.test = function(x, y) {
|
||||
// Clicked on exit button
|
||||
if (x > exitIconPositionX && x <= exitIconPositionX + exitIconWidth &&
|
||||
y > exitIconPositionY && y <= exitIconPositionY + exitIconHeight) {
|
||||
return Status.ABORT;
|
||||
}
|
||||
|
||||
if (cube_coord_x[1] <= x && x <= cube_coord_x[cubes_num_horizontal - 1] &&
|
||||
cube_coord_y[1] <= y && y <= cube_coord_y[cubes_num_vertical - 1]) {
|
||||
return Status.NOT_FINISHED;
|
||||
}
|
||||
|
||||
var startXi = util.binary_search(cube_coord_x, x);
|
||||
var startYi = util.binary_search(cube_coord_y, y);
|
||||
util.fillRectFromCoord(ctx,
|
||||
cube_coord_x[startXi],
|
||||
cube_coord_y[startYi],
|
||||
cube_coord_x[startXi + 1],
|
||||
cube_coord_y[startYi + 1]);
|
||||
if (startYi == 0 && !is_clicked_top[startXi]) {
|
||||
is_clicked_top[startXi] = true;
|
||||
}
|
||||
if (startYi == cubes_num_vertical - 1 && !is_clicked_bottom[startXi]) {
|
||||
is_clicked_bottom[startXi] = true;
|
||||
}
|
||||
if (startXi == 0 && !is_clicked_left[startYi]) {
|
||||
is_clicked_left[startYi] = true;
|
||||
}
|
||||
if (startXi == cubes_num_horizontal - 1 && !is_clicked_right[startYi]) {
|
||||
is_clicked_right[startYi] = true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < cubes_num_vertical; i++) {
|
||||
if (!is_clicked_left[i] || !is_clicked_right[i]) {
|
||||
return Status.NOT_FINISHED;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < cubes_num_horizontal; i++) {
|
||||
if (!is_clicked_top[i] || !is_clicked_bottom[i]) {
|
||||
return Status.NOT_FINISHED;
|
||||
}
|
||||
}
|
||||
// Test over
|
||||
return Status.FINISHED;
|
||||
};
|
||||
};
|
||||
|
||||
var test_driver = function() {
|
||||
var current_test = null;
|
||||
var canvas = document.getElementById('canvas');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.style.width = canvas.width + 'px';
|
||||
canvas.style.height = canvas.height + 'px';
|
||||
|
||||
// returns true if no other tests
|
||||
function proceed() {
|
||||
if (current_test == null) {
|
||||
current_test = new test1(canvas);
|
||||
return false;
|
||||
}
|
||||
else if (current_test instanceof test1) {
|
||||
current_test = new test2(canvas);
|
||||
return false;
|
||||
}
|
||||
else if (current_test instanceof test2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
proceed();
|
||||
|
||||
// returns whether all tests are finished
|
||||
this.touch = function(x, y) {
|
||||
var touchResult = current_test.test(x, y);
|
||||
if (touchResult == Status.ABORT) {
|
||||
return Status.ABORT;
|
||||
}
|
||||
// one test finished, go on next test
|
||||
if (touchResult == Status.FINISHED) {
|
||||
var isAllFinished = proceed();
|
||||
if (isAllFinished) {
|
||||
return Status.FINISHED;
|
||||
}
|
||||
}
|
||||
return Status.NOT_FINISHED;
|
||||
};
|
||||
};
|
||||
|
||||
// Handle touch input, Codes modified from
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Events/Touch_events
|
||||
var touchTest = function() {
|
||||
var ongoingTouches = [];
|
||||
var driver = null;
|
||||
var canvas = null;
|
||||
|
||||
function copyTouch(touch) {
|
||||
return { identifier: touch.identifier,
|
||||
pageX: touch.pageX,
|
||||
pageY: touch.pageY };
|
||||
}
|
||||
|
||||
function ongoingTouchIndexById(idToFind) {
|
||||
for (var i = 0; i < ongoingTouches.length; i++) {
|
||||
var id = ongoingTouches[i].identifier;
|
||||
|
||||
if (id == idToFind) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
function handleEvent(evt) {
|
||||
var touches = evt.changedTouches;
|
||||
var result;
|
||||
evt.preventDefault();
|
||||
|
||||
switch (evt.type) {
|
||||
case 'touchstart':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
ongoingTouches.push(copyTouch(touches[i]));
|
||||
var coord = util.relMouseCoords(canvas, touches[i]);
|
||||
result = driver.touch(coord.x, coord.y);
|
||||
}
|
||||
break;
|
||||
case 'touchmove':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
var coord = util.relMouseCoords(canvas, touches[i]);
|
||||
var idx = ongoingTouchIndexById(touches[i].identifier);
|
||||
|
||||
if (idx >= 0) {
|
||||
var coord_last = util.relMouseCoords(canvas, ongoingTouches[idx]);
|
||||
result = driver.touch(coord.x, coord.y);
|
||||
// swap in the new touch record
|
||||
ongoingTouches.splice(idx, 1, copyTouch(touches[i]));
|
||||
} else {
|
||||
console.log('can\'t figure out which touch to continue');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'touchend':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
var coord = util.relMouseCoords(canvas, touches[i]);
|
||||
var idx = ongoingTouchIndexById(touches[i].identifier);
|
||||
|
||||
if (idx >= 0) {
|
||||
ongoingTouches.splice(i, 1); // remove it; we're done
|
||||
} else {
|
||||
console.log('can\'t figure out which touch to end');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'touchcancel':
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
ongoingTouches.splice(i, 1); // remove it; we're done
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// All tests over
|
||||
if (result == Status.FINISHED || result == Status.ABORT) {
|
||||
// Clear remaining touches
|
||||
touches = null;
|
||||
canvas.removeEventListener('touchstart', handleEvent, false);
|
||||
canvas.removeEventListener('touchend', handleEvent, false);
|
||||
canvas.removeEventListener('touchcancel', handleEvent, false);
|
||||
canvas.removeEventListener('touchleave', handleEvent, false);
|
||||
canvas.removeEventListener('touchmove', handleEvent, false);
|
||||
console.log('uninitialized.');
|
||||
|
||||
if (result == Status.ABORT) {
|
||||
alert('Test terminated by user');
|
||||
} else {
|
||||
alert('Test Success!');
|
||||
}
|
||||
document.mozCancelFullScreen();
|
||||
// This is a hack to close touch test, uitest.js will close this iframe
|
||||
window.parent.window.location.hash = '';
|
||||
}
|
||||
}
|
||||
|
||||
this.init = function() {
|
||||
driver = new test_driver();
|
||||
|
||||
if (canvas == null) {
|
||||
canvas = document.getElementsByTagName('canvas')[0];
|
||||
canvas.addEventListener('touchstart', handleEvent, false);
|
||||
canvas.addEventListener('touchend', handleEvent, false);
|
||||
canvas.addEventListener('touchcancel', handleEvent, false);
|
||||
canvas.addEventListener('touchleave', handleEvent, false);
|
||||
canvas.addEventListener('touchmove', handleEvent, false);
|
||||
console.log('initialized.');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// XXX: There is a problem of getting into fullscreen
|
||||
// When we called mozRequestFullscreen, it actually resized two times.
|
||||
// We get almost fullscreen (except status bar) after first event, and truely
|
||||
// fullscreen after second event. So we need to wait for the second event.
|
||||
var resizedTimes = 0;
|
||||
window.addEventListener('resize', function startTest() {
|
||||
if (resizedTimes < 1) {
|
||||
resizedTimes++;
|
||||
}
|
||||
else {
|
||||
window.removeEventListener('resize', startTest);
|
||||
var test = new touchTest;
|
||||
test.init();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
document.getElementById('canvas').classList.remove('invisible');
|
||||
var exitButton = document.getElementById('fullscreen-exit');
|
||||
document.body.mozRequestFullScreen();
|
||||
});
|
||||
54
assets/webapps/uitest.gaiamobile.org/js/HW/vibrator.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
function vibratorTest() {
|
||||
var durationText = document.getElementById('duration-text');
|
||||
var duration = document.getElementById('duration');
|
||||
var timesText = document.getElementById('times-text');
|
||||
var times = document.getElementById('times');
|
||||
|
||||
var clickHandlers = {
|
||||
'off': function() {
|
||||
window.navigator.vibrate(0);
|
||||
},
|
||||
'one-short': function() {
|
||||
window.navigator.vibrate(200);
|
||||
},
|
||||
'two-short': function() {
|
||||
window.navigator.vibrate([200, 100, 200, 100]);
|
||||
},
|
||||
'one-long': function() {
|
||||
window.navigator.vibrate(10000);
|
||||
},
|
||||
'custom-pattern': function() {
|
||||
var timesNum = parseInt(times.value);
|
||||
var pattern = [];
|
||||
for (var i = 0; i < timesNum * 2; i++) {
|
||||
pattern.push(parseInt(duration.value));
|
||||
}
|
||||
window.navigator.vibrate(pattern);
|
||||
}
|
||||
};
|
||||
|
||||
// Update text when sliding bar is changed
|
||||
var changeHandlers = {
|
||||
'duration': function() {
|
||||
durationText.textContent = duration.value;
|
||||
},
|
||||
'times': function() {
|
||||
timesText.textContent = times.value;
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id]) {
|
||||
clickHandlers[evt.target.id].call(this, evt);
|
||||
}
|
||||
});
|
||||
document.body.addEventListener('input', function(evt) {
|
||||
if (changeHandlers[evt.target.id]) {
|
||||
changeHandlers[evt.target.id].call(this, evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('load', vibratorTest);
|
||||
37
assets/webapps/uitest.gaiamobile.org/js/HW/wifi.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
'use strict';
|
||||
|
||||
function wifiTest() {
|
||||
var WiFiMACText = document.getElementById('address');
|
||||
var WiFiStateText = document.getElementById('state');
|
||||
var switchButton = document.getElementById('switch');
|
||||
|
||||
var settings = window.navigator.mozSettings;
|
||||
var wifiManager;
|
||||
function update() {
|
||||
switchButton.checked = wifiManager.enabled;
|
||||
if (wifiManager.enabled) {
|
||||
WiFiStateText.textContent = 'On';
|
||||
WiFiMACText.textContent = wifiManager.macAddress;
|
||||
}
|
||||
else {
|
||||
WiFiStateText.textContent = 'Off';
|
||||
WiFiMACText.textContent = 'Not available';
|
||||
}
|
||||
}
|
||||
if ('mozWifiManager' in navigator) {
|
||||
wifiManager = navigator.mozWifiManager;
|
||||
// navigator.mozWifiManager does not have addEventListener()
|
||||
wifiManager.onenabled = update.bind(this);
|
||||
wifiManager.ondisabled = update.bind(this);
|
||||
update();
|
||||
}
|
||||
// Click on switch will only update settings and
|
||||
// it takes some time for hardware to turns on.
|
||||
// That's why we should listen to enabled/disabled event of wifi
|
||||
function wifiSwitch() {
|
||||
settings.createLock().set({'wifi.enabled': switchButton.checked});
|
||||
}
|
||||
switchButton.addEventListener('click', wifiSwitch);
|
||||
}
|
||||
|
||||
window.addEventListener('load', wifiTest);
|
||||
42
assets/webapps/uitest.gaiamobile.org/js/UI/alert.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
var clickHandlers = {
|
||||
'button1': function() {
|
||||
window.parent.alert('Hello world!');
|
||||
},
|
||||
'button2': function() {
|
||||
window.parent.alert(window.parent.confirm('Hello world?'));
|
||||
},
|
||||
'button3': function() {
|
||||
window.parent.alert(window.parent.prompt('Hello world:', 'initial value'));
|
||||
},
|
||||
'button21': function() {
|
||||
alert('Hello world!');
|
||||
},
|
||||
'button22': function() {
|
||||
alert(confirm('Hello world?'));
|
||||
},
|
||||
'button23': function() {
|
||||
alert(prompt('Hello world:', 'initial value'));
|
||||
},
|
||||
'button31': function() {
|
||||
var msg = 'Hello world!1\n2\n3\n4\n5\n6\n7\n8\n9';
|
||||
msg += '\n10\n11\n12\n13\n14\n15\n16\n17\n18\n';
|
||||
alert(msg);
|
||||
},
|
||||
'button32': function() {
|
||||
var msg = 'Hello world!1\n2\n3\n4\n5\n6\n7\n8\n9';
|
||||
msg += '\n10\n11\n12\n13\n14\n15\n16\n17\n18\n';
|
||||
alert(confirm(msg));
|
||||
},
|
||||
'button33': function() {
|
||||
var msg = 'Hello world!1\n2\n3\n4\n5\n6\n7\n8\n9';
|
||||
msg += '\n10\n11\n12\n13\n14\n15\n16\n17\n18\n';
|
||||
alert(prompt(msg));
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id])
|
||||
clickHandlers[evt.target.id].call(this, evt);
|
||||
});
|
||||
59
assets/webapps/uitest.gaiamobile.org/js/UI/canvas.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
'use strict';
|
||||
|
||||
var CanvasTests = {
|
||||
|
||||
get drawButton() {
|
||||
delete this.drawButton;
|
||||
return this.drawButton = document.getElementById('button-draw');
|
||||
},
|
||||
|
||||
get toggleHiddenButton() {
|
||||
delete this.toggleHiddenButton;
|
||||
return this.toggleHiddenButton =
|
||||
document.getElementById('button-toggle-hidden');
|
||||
},
|
||||
|
||||
get canvas() {
|
||||
delete this.canvas;
|
||||
return this.canvas = document.getElementById('canvas');
|
||||
},
|
||||
|
||||
get hiddenStatus() {
|
||||
delete this.hiddenStatus;
|
||||
return this.hiddenStatus = document.getElementById('hidden-status');
|
||||
},
|
||||
|
||||
init: function ct_init() {
|
||||
this.hiddenStatus.innerHTML =
|
||||
'hidden = ' +
|
||||
this.canvas.classList.contains('hidden');
|
||||
|
||||
this.drawButton.addEventListener('click', this.draw.bind(this));
|
||||
this.toggleHiddenButton.addEventListener('click',
|
||||
this.toggleShowHide.bind(this));
|
||||
},
|
||||
|
||||
draw: function ct_draw() {
|
||||
// var element = document.getElementById('canvas');
|
||||
var context = this.canvas.getContext('2d');
|
||||
var image = new Image();
|
||||
var path = '../../style/images/canardpc.jpg';
|
||||
image.src = path;
|
||||
var self = this;
|
||||
image.onload = function() {
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
context.drawImage(image, 0, 0, w, h, 0, 0, w, h);
|
||||
};
|
||||
},
|
||||
|
||||
toggleShowHide: function ct_toggleShowHide() {
|
||||
this.canvas.classList.toggle('hidden');
|
||||
var status = this.canvas.classList.contains('hidden');
|
||||
this.hiddenStatus.innerHTML = 'hidden = ' + status;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
CanvasTests.init();
|
||||
});
|
||||
24
assets/webapps/uitest.gaiamobile.org/js/UI/contextmenu.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
var clickHandlers = {
|
||||
'reload': function() {
|
||||
window.location.reload();
|
||||
},
|
||||
'comments': function() {
|
||||
window.location = '#comments';
|
||||
alert(window.location.href);
|
||||
},
|
||||
'twitter': function() {
|
||||
window.open('http://twitter.com/intent/tweet?text=' +
|
||||
document.title + ': ' + window.location.href, 'shareWindow');
|
||||
},
|
||||
'facebook': function() {
|
||||
window.open('http://facebook.com/sharer/sharer.php?u=' +
|
||||
window.location.href, 'shareWindow');
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id || evt.target.dataset.fn])
|
||||
clickHandlers[evt.target.id || evt.target.dataset.fn].call(this, evt);
|
||||
});
|
||||
19
assets/webapps/uitest.gaiamobile.org/js/UI/css_dimension.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
function css_dimension_test() {
|
||||
var bar_inch = document.getElementById('bar_inch');
|
||||
var bar_mm = document.getElementById('bar_mm');
|
||||
var bar_px = document.getElementById('bar_px');
|
||||
var bar_mozmm = document.getElementById('bar_mozmm');
|
||||
|
||||
bar_inch.style.width = '1in';
|
||||
bar_inch.style.backgroundColor = '#00FF00';
|
||||
bar_mm.style.width = '25.4mm';
|
||||
bar_mm.style.backgroundColor = '#00DD44';
|
||||
bar_px.style.width = '96px';
|
||||
bar_px.style.backgroundColor = '#00BB88';
|
||||
bar_mozmm.style.width = '25.4mozmm';
|
||||
bar_mozmm.style.backgroundColor = '#0099CC';
|
||||
}
|
||||
|
||||
window.addEventListener('load', css_dimension_test);
|
||||
13
assets/webapps/uitest.gaiamobile.org/js/UI/empty.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
var clickHandlers = {
|
||||
/*
|
||||
'id': function () {
|
||||
alert('event triggered!');
|
||||
}, */
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', function(evt) {
|
||||
if (clickHandlers[evt.target.id || evt.target.dataset.fn])
|
||||
clickHandlers[evt.target.id || evt.target.dataset.fn].call(this, evt);
|
||||
});
|
||||
45
assets/webapps/uitest.gaiamobile.org/js/UI/fullscreen.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
function fullscreenTest() {
|
||||
var fullscreenDiv = document.getElementById('fullscreen-div');
|
||||
|
||||
var testBtnDiv = document.getElementById('testBtn-div');
|
||||
var testBtn = document.getElementById('testBtn');
|
||||
var isFullscreenDiv = false;
|
||||
var isFullscreen = false;
|
||||
var displayMode;
|
||||
|
||||
function clickHandlers(evt) {
|
||||
switch (evt.target.id) {
|
||||
case 'testBtn':
|
||||
if (!isFullscreen) {
|
||||
window.parent.document.getElementById('test-iframe')
|
||||
.mozRequestFullScreen();
|
||||
testBtn.textContent = 'Cancel fullscreen';
|
||||
} else {
|
||||
window.parent.document.mozCancelFullScreen();
|
||||
testBtn.textContent = 'Test';
|
||||
}
|
||||
isFullscreen = !isFullscreen;
|
||||
break;
|
||||
case 'testBtn-div':
|
||||
if (!isFullscreenDiv) {
|
||||
fullscreenDiv.mozRequestFullScreen();
|
||||
testBtnDiv.textContent = 'Cancel fullscreen';
|
||||
} else {
|
||||
document.mozCancelFullScreen();
|
||||
testBtnDiv.textContent = 'Test';
|
||||
|
||||
// Also cancels fullscreen of caused by another button (if any)
|
||||
isFullscreen = false;
|
||||
testBtn.textContent = 'Test';
|
||||
}
|
||||
isFullscreenDiv = !isFullscreenDiv;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener('click', clickHandlers.bind(this));
|
||||
}
|
||||
|
||||
window.addEventListener('load', fullscreenTest);
|
||||
29
assets/webapps/uitest.gaiamobile.org/js/UI/keyboard.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
function keyboardTest() {
|
||||
(function designmode(id) {
|
||||
var iframe = document.querySelector('#' + id);
|
||||
iframe.contentDocument.designMode = 'on';
|
||||
iframe.contentDocument.body.textContent = 'Hola supermercado';
|
||||
})('iframe-designmode');
|
||||
|
||||
(function contentEditable(id) {
|
||||
var iframe = document.querySelector('#' + id);
|
||||
iframe.contentDocument.body.setAttribute('contenteditable', true);
|
||||
iframe.contentDocument.body.textContent = 'Telebancos por aqui';
|
||||
})('iframe-contenteditable');
|
||||
|
||||
var readme = document.getElementById('readme');
|
||||
var readmeBtn = document.getElementById('readme_button');
|
||||
readme.hidden = true;
|
||||
|
||||
readmeBtn.addEventListener('click',
|
||||
function toggle_readme() {
|
||||
readme.hidden = !readme.hidden;
|
||||
if (readme.hidden) {
|
||||
readmeBtn.textContent = 'Show README';
|
||||
} else {
|
||||
readmeBtn.textContent = 'Hide README';
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
window.addEventListener('load', keyboardTest);
|
||||
64
assets/webapps/uitest.gaiamobile.org/js/UI/notification.js
Normal file
19
assets/webapps/uitest.gaiamobile.org/js/UI/popup.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
document.getElementById('close').onclick = function() {
|
||||
window.close();
|
||||
};
|
||||
|
||||
document.getElementById('open').onclick = function() {
|
||||
window.open('/tests_html/UI/popup.html', '', 'dialog');
|
||||
};
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
if (window.innerHeight > 60) {
|
||||
document.body.style.backgroundColor = 'white';
|
||||
document.body.style.fontSize = '20px';
|
||||
document.body.style.color = 'black';
|
||||
} else {
|
||||
document.body.style.backgroundColor = 'green';
|
||||
document.body.style.fontSize = '10px';
|
||||
document.body.style.color = 'white';
|
||||
}
|
||||
});
|
||||
6
assets/webapps/uitest.gaiamobile.org/js/UI/scrollbar.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
window.addEventListener('load', function() {
|
||||
var p = document.getElementById('probe');
|
||||
document.getElementById('report').textContent =
|
||||
'Scroll bar width calculated from Javascript: ' +
|
||||
(p.offsetWidth - p.scrollWidth - 2) + 'px'; // 2 = border width * 2
|
||||
});
|
||||
16
assets/webapps/uitest.gaiamobile.org/js/UI/select.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
function changeTest(evt) {
|
||||
console.log('++select++ onchange: ' + this.selectedIndex);
|
||||
}
|
||||
|
||||
function blurTest(evt) {
|
||||
console.log('++select++ onblur: ' + this.id);
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var elementList = ['singleSel', 'multiSel', 'sizeSel'];
|
||||
elementList.forEach(function(elementId) {
|
||||
console.log('elementId: ' + elementId);
|
||||
document.getElementById(elementId).onchange = changeTest;
|
||||
document.getElementById(elementId).addEventListener('blur', blurTest);
|
||||
});
|
||||
});
|
||||
35
assets/webapps/uitest.gaiamobile.org/js/UI/window_open.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var popup = null;
|
||||
var buttons = document.querySelectorAll('button');
|
||||
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
var button = buttons[i];
|
||||
button.addEventListener('click', function(e) {
|
||||
var target = e.target;
|
||||
var actualWindow = window;
|
||||
if (target.classList.contains('mozbrowser')) {
|
||||
actualWindow = window.parent;
|
||||
}
|
||||
|
||||
switch (target.dataset.action) {
|
||||
case 'open':
|
||||
var param1 = null,
|
||||
param2 = null;
|
||||
var hash = target.dataset.url.split('#');
|
||||
if (hash.length > 1) {
|
||||
param1 = hash[1];
|
||||
param2 = hash[1];
|
||||
}
|
||||
popup = actualWindow.open(target.dataset.url, param1, param2);
|
||||
break;
|
||||
case 'close':
|
||||
popup.close();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
184
assets/webapps/uitest.gaiamobile.org/js/uitest.js
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
'use stricts';
|
||||
|
||||
var UITest = {
|
||||
get UItests() {
|
||||
delete this.UItests;
|
||||
return this.UItests = document.getElementById('UI-tests');
|
||||
},
|
||||
get APItests() {
|
||||
delete this.APItests;
|
||||
return this.APItests = document.getElementById('API-tests');
|
||||
},
|
||||
get HWtests() {
|
||||
delete this.HWtests;
|
||||
return this.HWtests = document.getElementById('HW-tests');
|
||||
},
|
||||
get iframe() {
|
||||
delete this.iframe;
|
||||
return this.iframe = document.getElementById('test-iframe');
|
||||
},
|
||||
get backHeader() {
|
||||
delete this.backHeader;
|
||||
return this.backHeader = document.getElementById('test-panel-header');
|
||||
},
|
||||
get panelTitle() {
|
||||
delete this.panelTitle;
|
||||
return this.panelTitle = document.getElementById('test-panel-title');
|
||||
},
|
||||
get tabs() {
|
||||
delete this.tabs;
|
||||
return this.tabs = document.querySelectorAll('[role="tab"]');
|
||||
},
|
||||
get UITab() {
|
||||
delete this.UITab;
|
||||
return this.UITab = document.getElementById('UI');
|
||||
},
|
||||
get APITab() {
|
||||
delete this.APITab;
|
||||
return this.APITab = document.getElementById('API');
|
||||
},
|
||||
get HWTab() {
|
||||
delete this.HWTab;
|
||||
return this.HWTab = document.getElementById('HW');
|
||||
},
|
||||
currentTab: 'UI',
|
||||
handleNotificationMessage: function(message) {
|
||||
if (!message.clicked) {
|
||||
return;
|
||||
}
|
||||
this.bringSelfToForeground();
|
||||
},
|
||||
bringSelfToForeground: function() {
|
||||
navigator.mozApps.getSelf().onsuccess = function gotSelf(evt) {
|
||||
var app = evt.target.result;
|
||||
app.launch();
|
||||
};
|
||||
},
|
||||
handleActivity: function(activity) {
|
||||
if (activity.source.name != 'internal-system-engineering-mode') {
|
||||
return;
|
||||
}
|
||||
//TODO use mozEngineeringMode API to do something
|
||||
},
|
||||
init: function ut_init() {
|
||||
this.iframe.addEventListener('load', this);
|
||||
this.iframe.addEventListener('unload', this);
|
||||
document.body.addEventListener('transitionend', this);
|
||||
window.addEventListener('keyup', this);
|
||||
window.addEventListener('hashchange', this);
|
||||
this.backHeader.addEventListener('action', this);
|
||||
|
||||
navigator.mozSetMessageHandler('notification', function(msg) {
|
||||
this.handleNotificationMessage(msg);
|
||||
}.bind(this));
|
||||
|
||||
navigator.mozSetMessageHandler('activity', function(activity) {
|
||||
this.handleActivity(activity);
|
||||
}.bind(this));
|
||||
|
||||
var name = this.getNameFromHash();
|
||||
if (name) {
|
||||
this.openTest(name);
|
||||
}
|
||||
else {
|
||||
// if no test is specified, load UI tests list (select UI tab)
|
||||
window.location.hash = 'UI';
|
||||
}
|
||||
},
|
||||
uninit: function ut_uninit() {
|
||||
this.iframe.removeEventListener('load', this);
|
||||
this.iframe.removeEventListener('unload', this);
|
||||
document.body.removeEventListener('transitionend', this);
|
||||
window.removeEventListener('keyup', this);
|
||||
window.removeEventListener('hashchange', this);
|
||||
this.backHeader.removeEventListener('action', this);
|
||||
},
|
||||
getNameFromHash: function ut_getNameFromHash() {
|
||||
return (/\btest=(.+)(&|$)/.exec(window.location.hash) || [])[1];
|
||||
},
|
||||
handleEvent: function ut_handleEvent(ev) {
|
||||
switch (ev.type) {
|
||||
case 'action':
|
||||
if (ev.target != this.backHeader) {
|
||||
return;
|
||||
}
|
||||
if (window.location.hash) {
|
||||
window.location.hash = '';
|
||||
}
|
||||
break;
|
||||
case 'load':
|
||||
this.iframe.contentWindow.addEventListener('keyup', this);
|
||||
break;
|
||||
case 'unload':
|
||||
this.iframe.contentWindow.removeEventListener('keyup', this);
|
||||
break;
|
||||
case 'hashchange':
|
||||
if (window.location.hash == '#UI')
|
||||
{
|
||||
this.UItests.classList.remove('invisible');
|
||||
AccessibilityHelper.setAriaSelected(this.UITab, this.tabs);
|
||||
if (!this.HWtests.classList.contains('invisible'))
|
||||
this.HWtests.classList.add('invisible');
|
||||
if (!this.APItests.classList.contains('invisible'))
|
||||
this.APItests.classList.add('invisible');
|
||||
}
|
||||
else if (window.location.hash == '#API')
|
||||
{
|
||||
this.APItests.classList.remove('invisible');
|
||||
AccessibilityHelper.setAriaSelected(this.APITab, this.tabs);
|
||||
if (!this.UItests.classList.contains('invisible'))
|
||||
this.UItests.classList.add('invisible');
|
||||
if (!this.HWtests.classList.contains('invisible'))
|
||||
this.HWtests.classList.add('invisible');
|
||||
}
|
||||
else if (window.location.hash == '#HW')
|
||||
{
|
||||
this.HWtests.classList.remove('invisible');
|
||||
AccessibilityHelper.setAriaSelected(this.HWTab, this.tabs);
|
||||
if (!this.UItests.classList.contains('invisible'))
|
||||
this.UItests.classList.add('invisible');
|
||||
if (!this.APItests.classList.contains('invisible'))
|
||||
this.APItests.classList.add('invisible');
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = this.getNameFromHash();
|
||||
if (!name) {
|
||||
this.closeTest();
|
||||
return;
|
||||
}
|
||||
this.panelTitle.textContent = name;
|
||||
this.openTest(name);
|
||||
}
|
||||
break;
|
||||
case 'transitionend':
|
||||
var name = this.getNameFromHash();
|
||||
if (!name)
|
||||
this.iframe.src = 'about:blank';
|
||||
break;
|
||||
}
|
||||
},
|
||||
openTest: function ut_openTest(name) {
|
||||
// save tab name from URL
|
||||
// e.g. '#test=UI/empty' => 'UI'
|
||||
this.currentTab = (/=\b(.+)\//.exec(window.location.hash) || [])[1];
|
||||
document.body.classList.add('test');
|
||||
|
||||
var self = this;
|
||||
window.setTimeout(function openTestPage() {
|
||||
self.iframe.src = './tests_html/' + name + '.html';
|
||||
}, 200);
|
||||
},
|
||||
closeTest: function ut_closeTest() {
|
||||
var isOpened = document.body.classList.contains('test');
|
||||
if (!isOpened)
|
||||
return false;
|
||||
document.body.classList.remove('test');
|
||||
|
||||
// select tab after close test iframe
|
||||
window.location.hash = this.currentTab;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', UITest.init.bind(UITest));
|
||||
1
assets/webapps/uitest.gaiamobile.org/manifest.webapp
Executable file
|
|
@ -0,0 +1 @@
|
|||
{"name":"UI tests","launch_path":"/index.html","type":"certified","developer":{"name":"The Gaia Team","url":"https://github.com/mozilla-b2g/gaia"},"messages":[{"notification":"/index.html"}],"activities":{"internal-system-engineering-mode":{"filters":{},"href":"/index.html","disposition":"window"}},"icons":{"84":"/style/icons/ui-test_84.png","126":"/style/icons/ui-test_126.png","142":"/style/icons/ui-test_142.png","189":"/style/icons/ui-test_189.png","284":"/style/icons/ui-test_284.png"},"permissions":{"fmradio":{},"browser":{},"geolocation":{},"wifi-manage":{},"mobilenetwork":{},"mobileconnection":{},"power":{},"backgroundservice":{},"attention":{},"contacts":{"access":"readwrite"},"settings":{"access":"readwrite"},"desktop-notification":{},"device-storage:sdcard":{"access":"readwrite"},"audio-capture":{},"video-capture":{},"bluetooth":{},"downloads":{},"firefox-accounts":{},"engineering-mode":{},"secureelement-manage":{},"presentation-device-manage":{},"moz-extremely-unstable-and-will-change-webcomponents":{}},"origin":"app://uitest.gaiamobile.org","version":"2.5"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
window.COMPONENTS_BASE_URL = '/shared/elements/';
|
||||
1686
assets/webapps/uitest.gaiamobile.org/shared/elements/gaia-header/dist/gaia-header.js
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Generated by grunt-webfont */
|
||||
/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */
|
||||
|
||||
@font-face {
|
||||
font-family: "gaia-icons";
|
||||
src: url("fonts/gaia-icons.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[data-icon]:before,
|
||||
.ligature-icons {
|
||||
font-family: "gaia-icons";
|
||||
content: attr(data-icon);
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
text-transform: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-size: 30px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):before {
|
||||
/* Highlight cases where icons are not localized and still visible to screen
|
||||
reader */
|
||||
background: rgba(255, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):after {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
width: 20rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
content: "Icon is not localized and is still visible to screen reader. Please add data-l10n-id or aria-label for localization or use aria-hidden to hide the icon from screen reader.";
|
||||
z-index: 9999;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
var AccessibilityHelper = {
|
||||
/**
|
||||
* For a set of tab elements, set aria-selected attribute in accordance with
|
||||
* the current selection.
|
||||
* @param {Object} selectedTab a tab to select object.
|
||||
* @param {Array} tabs an array of tabs.
|
||||
*/
|
||||
setAriaSelected: function ah_setAriaSelected(selectedTab, tabs) {
|
||||
// In case tabs is a NodeList, that does not have forEach.
|
||||
Array.prototype.forEach.call(tabs, function setAriaSelectedAttr(tab) {
|
||||
tab.setAttribute('aria-selected',
|
||||
tab === selectedTab ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.AccessibilityHelper = AccessibilityHelper;
|
||||
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
/* exported BluetoothHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var BluetoothHelper = function() {
|
||||
var profiles = {
|
||||
'HFP': 0x111E,
|
||||
'A2DP': 0x110D
|
||||
};
|
||||
|
||||
var _bluetooth = window.navigator.mozBluetooth;
|
||||
var _isReady = false;
|
||||
var _callbacks = [];
|
||||
|
||||
var _adapter = null;
|
||||
var _oldAdapter = null; // The varaible is accessed for v1 only.
|
||||
var _v2 = true;
|
||||
|
||||
var _ready = function(callback) {
|
||||
if (!callback || !_bluetooth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isReady) {
|
||||
callback();
|
||||
} else {
|
||||
_callbacks.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
var _handleRequest = function(request, callback, errorcb) {
|
||||
request.onsuccess = function() {
|
||||
if (callback) {
|
||||
callback(request.result);
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function() {
|
||||
console.log('Error handling bluetooth request');
|
||||
if (errorcb) {
|
||||
errorcb();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// run callbacks when adapter is ready
|
||||
var _processCallbacks = function() {
|
||||
if (_adapter) {
|
||||
_isReady = true;
|
||||
|
||||
_callbacks.forEach(function(callback) {
|
||||
callback();
|
||||
});
|
||||
// clean up the _callback queue
|
||||
_callbacks = [];
|
||||
} else {
|
||||
// We can do nothing without default adapter.
|
||||
console.log('BluetoothHelper(): connot get default adapter yet');
|
||||
}
|
||||
};
|
||||
|
||||
// API v2 get adapter via bluetooth
|
||||
var _fetchAdapterV2 = function() {
|
||||
// need time to get bluetooth adapter at first run
|
||||
_bluetooth.onattributechanged = function onManagerAttributeChanged(evt) {
|
||||
for (var i in evt.attrs) {
|
||||
switch (evt.attrs[i]) {
|
||||
case 'defaultAdapter':
|
||||
console.log('defaultAdapter changed. address:',
|
||||
_bluetooth.defaultAdapter.address);
|
||||
_adapter = _bluetooth.defaultAdapter;
|
||||
_processCallbacks();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_adapter = _bluetooth.defaultAdapter;
|
||||
if (_adapter) {
|
||||
_processCallbacks();
|
||||
}
|
||||
};
|
||||
|
||||
// API v1 get adapter via bluetooth
|
||||
var _fetchAdapter = function() {
|
||||
var req = _bluetooth.getDefaultAdapter();
|
||||
if (req) {
|
||||
req.onsuccess = function() {
|
||||
if (_adapter) {
|
||||
_oldAdapter = _adapter;
|
||||
}
|
||||
|
||||
_isReady = true;
|
||||
_adapter = req.result;
|
||||
|
||||
// Put the callback function of onpairedstatuschanged to the new adapter
|
||||
// because the new adapter won't remember those callback function which
|
||||
// is registered before. In other word, we get a new adpater after
|
||||
// turned on/off Bluetooth. The new adapter have no registered callback.
|
||||
if (_oldAdapter && _oldAdapter.onpairedstatuschanged) {
|
||||
_adapter.onpairedstatuschanged = _oldAdapter.onpairedstatuschanged;
|
||||
}
|
||||
|
||||
_callbacks.forEach(function(callback) {
|
||||
callback();
|
||||
});
|
||||
// clean up the _callback queue
|
||||
_callbacks = [];
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
// We can do nothing without default adapter.
|
||||
console.log('BluetoothHelper(): connot get default adapter!!!');
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var _getAdapter = function() {
|
||||
if (_v2) {
|
||||
_fetchAdapterV2();
|
||||
} else {
|
||||
_fetchAdapter();
|
||||
}
|
||||
};
|
||||
|
||||
var _resetAdapter = function() {
|
||||
if (_adapter) {
|
||||
_oldAdapter = _adapter;
|
||||
}
|
||||
// clean up state and adapter
|
||||
_isReady = false;
|
||||
_adapter = null;
|
||||
};
|
||||
|
||||
// init
|
||||
if (_bluetooth) {
|
||||
// detect API version
|
||||
if (typeof(_bluetooth.onattributechanged) === 'undefined') {
|
||||
_v2 = false;
|
||||
}
|
||||
|
||||
if (_v2) {
|
||||
_bluetooth.onadapteradded = function onAdapterAdded(evt) {
|
||||
_getAdapter();
|
||||
};
|
||||
} else {
|
||||
_bluetooth.addEventListener('enabled', _getAdapter);
|
||||
_bluetooth.addEventListener('adapteradded', _getAdapter);
|
||||
_bluetooth.addEventListener('disabled', _resetAdapter);
|
||||
}
|
||||
_getAdapter();
|
||||
}
|
||||
|
||||
return {
|
||||
profiles: profiles,
|
||||
|
||||
answerWaitingCall: function() {
|
||||
_ready(function() {
|
||||
_adapter.answerWaitingCall();
|
||||
});
|
||||
},
|
||||
|
||||
ignoreWaitingCall: function() {
|
||||
_ready(function() {
|
||||
_adapter.ignoreWaitingCall();
|
||||
});
|
||||
},
|
||||
|
||||
toggleCalls: function() {
|
||||
_ready(function() {
|
||||
_adapter.toggleCalls();
|
||||
});
|
||||
},
|
||||
|
||||
getConnectedDevicesByProfile: function(profileID, cb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.getConnectedDevices(profileID), cb);
|
||||
});
|
||||
},
|
||||
|
||||
connectSco: function(cb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.connectSco(), cb);
|
||||
});
|
||||
},
|
||||
|
||||
disconnectSco: function(cb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.disconnectSco(), cb);
|
||||
});
|
||||
},
|
||||
|
||||
getPairedDevices: function(cb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.getPairedDevices(), cb);
|
||||
});
|
||||
},
|
||||
|
||||
getAddress: function(cb) {
|
||||
if (_v2) {
|
||||
console.log('getAddress function is deprecated');
|
||||
return;
|
||||
}
|
||||
|
||||
_ready(function() {
|
||||
var address = _adapter.address;
|
||||
cb(address);
|
||||
});
|
||||
},
|
||||
|
||||
setPairingConfirmation: function(address, confirmed) {
|
||||
if (_v2) {
|
||||
console.log('setPairingConfirmation API is deprecated');
|
||||
return;
|
||||
}
|
||||
|
||||
_ready(function() {
|
||||
_adapter.setPairingConfirmation(address, confirmed);
|
||||
});
|
||||
},
|
||||
|
||||
setPinCode: function(address, pincode) {
|
||||
if (_v2) {
|
||||
console.log('setPairingConfirmation API is deprecated');
|
||||
return;
|
||||
}
|
||||
|
||||
_ready(function() {
|
||||
_adapter.setPinCode(address, pincode);
|
||||
});
|
||||
},
|
||||
|
||||
setPasskey: function(address, key) {
|
||||
if (_v2) {
|
||||
console.log('setPairingConfirmation API is deprecated');
|
||||
return;
|
||||
}
|
||||
|
||||
_ready(function() {
|
||||
_adapter.setPasskey(address, key);
|
||||
});
|
||||
},
|
||||
|
||||
isScoConnected: function(cb, errorcb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.isScoConnected(), cb, errorcb);
|
||||
});
|
||||
},
|
||||
|
||||
sendMediaMetaData: function(metadata, cb, errorcb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.sendMediaMetaData(metadata), cb, errorcb);
|
||||
});
|
||||
},
|
||||
|
||||
sendMediaPlayStatus: function(metadata, cb, errorcb) {
|
||||
_ready(function() {
|
||||
_handleRequest(_adapter.sendMediaPlayStatus(metadata), cb, errorcb);
|
||||
});
|
||||
},
|
||||
|
||||
set onhfpstatuschanged(callback) {
|
||||
_ready(function() {
|
||||
_adapter.onhfpstatuschanged = callback;
|
||||
});
|
||||
},
|
||||
|
||||
set onscostatuschanged(callback) {
|
||||
_ready(function() {
|
||||
_adapter.onscostatuschanged = callback;
|
||||
});
|
||||
},
|
||||
|
||||
set ona2dpstatuschanged(callback) {
|
||||
_ready(function() {
|
||||
_adapter.ona2dpstatuschanged = callback;
|
||||
});
|
||||
},
|
||||
|
||||
set onrequestmediaplaystatus(callback) {
|
||||
_ready(function() {
|
||||
_adapter.onrequestmediaplaystatus = callback;
|
||||
});
|
||||
},
|
||||
|
||||
set onpairedstatuschanged(callback) {
|
||||
if (_v2) {
|
||||
console.log('onpairedstatuschanged API is deprecated');
|
||||
return;
|
||||
}
|
||||
|
||||
_ready(function() {
|
||||
_adapter.onpairedstatuschanged = callback;
|
||||
});
|
||||
},
|
||||
|
||||
v2: _v2, // expose API version for app reference
|
||||
|
||||
// bypass the enable/disable state if works in APIv1
|
||||
enable: function() {
|
||||
if (_v2) {
|
||||
_ready(function() {
|
||||
_adapter.enable();
|
||||
});
|
||||
} else {
|
||||
console.log('enable is not support in v1 API!');
|
||||
}
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
if (_v2) {
|
||||
_ready(function() {
|
||||
_adapter.disable();
|
||||
});
|
||||
} else {
|
||||
console.log('disable is not support in v1 API!');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/* global mozIntl */
|
||||
|
||||
/**
|
||||
* This lib relies on `l10n.js' to implement localizable date/time strings.
|
||||
*
|
||||
* The proposed `DownloadFormatter' object provides features for formatting
|
||||
* the data retrieved from the new API for Downloads, taking into account
|
||||
* the structure defined by the API itself.
|
||||
*/
|
||||
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var NUMBER_OF_DECIMALS = 2;
|
||||
var BYTE_SCALE = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
function _getFormattedSize(bytes) {
|
||||
if (bytes === undefined || isNaN(bytes)) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
while (bytes >= 1024 && index < BYTE_SCALE.length) {
|
||||
bytes /= 1024;
|
||||
++index;
|
||||
}
|
||||
|
||||
return document.l10n.
|
||||
formatValue('byteUnit-' + BYTE_SCALE[index]).then(unit => {
|
||||
return document.l10n.formatValue('fileSize', {
|
||||
size: bytes.toFixed(NUMBER_OF_DECIMALS),
|
||||
unit: unit
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function _calcPercentage(currently, total) {
|
||||
if (total === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parseInt((100 * currently) / total);
|
||||
}
|
||||
|
||||
var DownloadFormatter = {
|
||||
getFormattedSize: function(bytes) {
|
||||
return _getFormattedSize(bytes);
|
||||
},
|
||||
getPercentage: function(download) {
|
||||
return _calcPercentage(download.currentBytes, download.totalBytes);
|
||||
},
|
||||
getFileName: function(download) {
|
||||
return download.path.split('/').pop(); // filename.ext
|
||||
},
|
||||
getTotalSize: function(download) {
|
||||
var bytes = download.totalBytes;
|
||||
return _getFormattedSize(bytes);
|
||||
},
|
||||
getDownloadedSize: function(download) {
|
||||
var bytes = download.currentBytes;
|
||||
return _getFormattedSize(bytes);
|
||||
},
|
||||
getDate: function(download) {
|
||||
var date;
|
||||
|
||||
try {
|
||||
date = download.startTime;
|
||||
} catch (ex) {
|
||||
date = new Date();
|
||||
console.error(ex);
|
||||
}
|
||||
|
||||
var formatter = mozIntl._gaia.RelativeDate(navigator.languages);
|
||||
return formatter.format(date);
|
||||
},
|
||||
getUUID: function(download) {
|
||||
return download.id || this.getFileName(download);
|
||||
}
|
||||
};
|
||||
|
||||
exports.DownloadFormatter = DownloadFormatter;
|
||||
|
||||
}(this));
|
||||
|
|
@ -0,0 +1,685 @@
|
|||
'use strict';
|
||||
/* exported DownloadHelper */
|
||||
/* global DownloadFormatter */
|
||||
/* global DownloadStore */
|
||||
/* global DownloadUI */
|
||||
/* global LazyLoader */
|
||||
/* global MimeMapper */
|
||||
/* global MozActivity */
|
||||
/* global SettingsListener */
|
||||
|
||||
/*
|
||||
* DownloadHelper.js: Perform some utility functions with DOMDownload
|
||||
* objects.
|
||||
*
|
||||
* - You have to include in you HTML:
|
||||
*
|
||||
* <!-- <script src="shared/js/mime_mapper.js"></script> -->
|
||||
* <!-- <script src="shared/js/download/download_store.js"></script> -->
|
||||
* <script defer src="shared/js/lazy_loader.js"></script>
|
||||
*
|
||||
* - How to use this component.
|
||||
*
|
||||
* For launching a download
|
||||
*
|
||||
* var req = DownloadHelper.open(download);
|
||||
*
|
||||
* req.onsuccess = function req_onsuccess() {
|
||||
* alert('The download was opened so we can remove the notification');
|
||||
* }
|
||||
*
|
||||
* req.onerror = function req_onerror() {
|
||||
* alert('Something wrong!: ' + req.error.message);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
var DownloadHelper = (function() {
|
||||
|
||||
// Exception code constants
|
||||
var CODE = {
|
||||
FILE_NOT_FOUND: 'FILE_NOT_FOUND',
|
||||
DEVICE_STORAGE: 'DEVICE_STORAGE',
|
||||
MIME_TYPE_NOT_SUPPORTED: 'MIME_TYPE_NOT_SUPPORTED',
|
||||
INVALID_STATE: 'INVALID_STATE',
|
||||
NO_SDCARD: 'NO_SDCARD',
|
||||
NO_PROVIDER: 'NO_PROVIDER',
|
||||
UNMOUNTED_SDCARD: 'UNMOUNTED_SDCARD'
|
||||
};
|
||||
|
||||
/*
|
||||
* Request auxiliary object to support asynchronous calls
|
||||
*/
|
||||
var Request = function() {
|
||||
this.done = function(result) {
|
||||
if (typeof this.onsuccess === 'function') {
|
||||
this.result = result;
|
||||
window.setTimeout(function() {
|
||||
this.onsuccess({
|
||||
target: this
|
||||
});
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
this.failed = function(error) {
|
||||
if (typeof this.onerror === 'function') {
|
||||
this.error = error;
|
||||
window.setTimeout(function() {
|
||||
this.onerror({
|
||||
target: this
|
||||
});
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Storage name by default
|
||||
var STORAGE_NAME_BY_DEFAULT = 'sdcard';
|
||||
|
||||
// Storage name settings key
|
||||
var STORAGE_NAME_KEY = 'device.storage.writable.name';
|
||||
|
||||
// Current storage name
|
||||
var storageName = STORAGE_NAME_BY_DEFAULT;
|
||||
|
||||
LazyLoader.load('shared/js/settings_listener.js', function settingsLoaded() {
|
||||
SettingsListener.observe(STORAGE_NAME_KEY, STORAGE_NAME_BY_DEFAULT,
|
||||
function setStorageName(evt) {
|
||||
var settingValue = evt.settingValue;
|
||||
if (settingValue) {
|
||||
storageName = settingValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
* Error auxiliary method
|
||||
*/
|
||||
function sendError(req, message, code) {
|
||||
req.failed({
|
||||
message: message,
|
||||
code: code
|
||||
});
|
||||
|
||||
console.error(message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a blob from the sdcard
|
||||
*
|
||||
* @param{Object} The download object
|
||||
*
|
||||
*/
|
||||
function getBlob(download) {
|
||||
var req = new Request();
|
||||
var storage = getVolume(download.storageName);
|
||||
var storeAvailableReq = storage.available();
|
||||
|
||||
storeAvailableReq.onsuccess = function available_onsuccess(e) {
|
||||
var path = download.storagePath;
|
||||
switch (storeAvailableReq.result) {
|
||||
case 'unavailable':
|
||||
sendError(req, ' Could not open the file: ' + path + ' from ' +
|
||||
download.storageName, CODE.NO_SDCARD);
|
||||
break;
|
||||
|
||||
case 'shared':
|
||||
sendError(req, ' Could not open the file: ' + path + ' from ' +
|
||||
download.storageName, CODE.UNMOUNTED_SDCARD);
|
||||
break;
|
||||
|
||||
default:
|
||||
try {
|
||||
var storeGetReq = storage.get(path);
|
||||
|
||||
storeGetReq.onsuccess = function store_onsuccess() {
|
||||
req.done(storeGetReq.result);
|
||||
};
|
||||
|
||||
storeGetReq.onerror = function store_onerror() {
|
||||
sendError(req, storeGetReq.error.name +
|
||||
' Could not open the file: ' + path + ' from ' +
|
||||
download.storageName, CODE.FILE_NOT_FOUND);
|
||||
};
|
||||
} catch (ex) {
|
||||
sendError(req, 'Error getting the file ' + path + ' from ' +
|
||||
download.storageName, CODE.DEVICE_STORAGE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
storeAvailableReq.onerror = function available_onerror() {
|
||||
sendError(req, 'Error getting storage state ', CODE.DEVICE_STORAGE);
|
||||
};
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
* Base action that implements the commom logic for actions based on the
|
||||
* MozActivity API
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var Action = function Action(params) {
|
||||
this.req = params.request;
|
||||
this.name = params.actionType.activityName;
|
||||
};
|
||||
|
||||
Action.prototype = {
|
||||
/*
|
||||
* It performs the action based on MozActivity
|
||||
*/
|
||||
run: function a_run() {
|
||||
var activity = new MozActivity({
|
||||
name: this.name,
|
||||
data: this.data
|
||||
});
|
||||
|
||||
activity.onsuccess = this._onsuccess.bind(this);
|
||||
activity.onerror = this._onerror.bind(this);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method implements the generic onsuccess callback
|
||||
*/
|
||||
_onsuccess: function a_onsuccess() {
|
||||
this.req.done();
|
||||
},
|
||||
|
||||
/*
|
||||
* This method implements the generic onerror callback
|
||||
*/
|
||||
_onerror: function a_onerror(evt) {
|
||||
this.req.failed({
|
||||
message: evt.target.error.name
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This action opens downloads extending the Action Object
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var OpenAction = function OpenAction(params) {
|
||||
Action.call(this, params);
|
||||
|
||||
this.data = {
|
||||
url: params.download.path,
|
||||
filename: DownloadFormatter.getFileName(params.download),
|
||||
type: params.type,
|
||||
blob: params.blob
|
||||
};
|
||||
};
|
||||
|
||||
OpenAction.prototype = {
|
||||
__proto__: Action.prototype
|
||||
};
|
||||
|
||||
/*
|
||||
* This action shares downloads extending the Action Object
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var ShareAction = function ShareAction(params) {
|
||||
Action.call(this, params);
|
||||
|
||||
this.data = {
|
||||
// 'share' activities do not work with specific mime types
|
||||
type: params.type.split('/')[0] + '/*',
|
||||
blobs: [params.blob],
|
||||
filenames: [DownloadFormatter.getFileName(params.download)]
|
||||
};
|
||||
};
|
||||
|
||||
ShareAction.prototype = {
|
||||
__proto__: Action.prototype,
|
||||
|
||||
/*
|
||||
* It overrides the generic onerror callback for another more suitable
|
||||
*/
|
||||
_onerror: function sa_onerror(evt) {
|
||||
if (evt.target.error.name !== 'NO_PROVIDER') {
|
||||
return;
|
||||
}
|
||||
|
||||
sendError(this.req, 'No provider to share file', CODE.NO_PROVIDER);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This action gets the info for a download
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var InfoAction = function InfoAction(params) {
|
||||
Action.call(this, params);
|
||||
|
||||
this.data = {
|
||||
name: DownloadFormatter.getFileName(params.download),
|
||||
type: params.type,
|
||||
blob: params.blob,
|
||||
size: params.download.totalBytes,
|
||||
path: params.download.path
|
||||
};
|
||||
};
|
||||
|
||||
InfoAction.prototype = {
|
||||
__proto__: Action.prototype,
|
||||
|
||||
/*
|
||||
* It overrides the generic run method
|
||||
*/
|
||||
run: function ia_run() {
|
||||
this.req.done(this.data);
|
||||
}
|
||||
};
|
||||
|
||||
// This is a factory that deals with different <Action> objects
|
||||
var ActionsFactory = {
|
||||
TYPE: {
|
||||
OPEN: {
|
||||
activityName: 'open',
|
||||
actionClass: OpenAction
|
||||
},
|
||||
SHARE: {
|
||||
activityName: 'share',
|
||||
actionClass: ShareAction
|
||||
},
|
||||
INFO: {
|
||||
actionClass: InfoAction
|
||||
},
|
||||
WALLPAPER: {
|
||||
activityName: 'setwallpaper',
|
||||
actionClass: ShareAction
|
||||
},
|
||||
RINGTONE: {
|
||||
activityName: 'setringtone',
|
||||
actionClass: ShareAction
|
||||
}
|
||||
},
|
||||
|
||||
create: function af_create(params) {
|
||||
return new params.actionType.actionClass(params);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the mime type
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*
|
||||
* @returns(String) Mime type
|
||||
*/
|
||||
function getType(download) {
|
||||
var fileName = DownloadFormatter.getFileName(download);
|
||||
var type = MimeMapper.guessTypeFromFileProperties(fileName,
|
||||
download.contentType);
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method allows third-parties to open or share downloads
|
||||
*
|
||||
* @param{Object} Action types: <ActionsFactory.TYPE.OPEN> or
|
||||
* <ActionsFactory.TYPE.SHARE>
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
function runAction(actionType, download) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function launching() {
|
||||
var state = download.state;
|
||||
if (state === 'succeeded' || state === 'finalized') {
|
||||
LazyLoader.load(['shared/js/mime_mapper.js',
|
||||
'shared/js/download/download_formatter.js'],
|
||||
function loaded() {
|
||||
var type = getType(download);
|
||||
|
||||
//
|
||||
// The 'open' action will always launch an activity using the original
|
||||
// content type to allow for third party applications to handle
|
||||
// arbitrary types of content.
|
||||
//
|
||||
// The 'share' action on the other hand only works with known mime
|
||||
// types at this time.
|
||||
//
|
||||
|
||||
if (type.length === 0) {
|
||||
type = download.contentType;
|
||||
|
||||
if (actionType !== ActionsFactory.TYPE.OPEN &&
|
||||
actionType !== ActionsFactory.TYPE.INFO) {
|
||||
sendError(req, 'Mime type not supported: ' + type,
|
||||
CODE.MIME_TYPE_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var blobReq = getBlob(download);
|
||||
|
||||
blobReq.onsuccess = function() {
|
||||
ActionsFactory.create({
|
||||
actionType: actionType,
|
||||
download: download,
|
||||
type: type,
|
||||
blob: blobReq.result,
|
||||
request: req
|
||||
}).run();
|
||||
};
|
||||
|
||||
blobReq.onerror = function() {
|
||||
// Problem getting the blob from the sdcard
|
||||
req.failed(blobReq.error);
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
sendError(req, 'Becareful, the download is not finished!',
|
||||
CODE.INVALID_STATE);
|
||||
}, 0);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method allows clients to remove a download, from the
|
||||
* list and the phone.
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
function remove(download) {
|
||||
var req = new Request();
|
||||
var incompleteDownload = download.state !== 'succeeded';
|
||||
// If is not done, use download manager to remove it,
|
||||
// otherwise, deal with the datastore.
|
||||
setTimeout(function() {
|
||||
if (incompleteDownload) {
|
||||
if (!navigator.mozDownloadManager) {
|
||||
sendError(req, 'DownloadManager not present', CODE.INVALID_STATE);
|
||||
} else {
|
||||
// First we pause the download so that everyone knows it's being
|
||||
// stopped. The Downloads API itself won't stop the download first,
|
||||
// it will simply kill it.
|
||||
// XXXAus: Remove when we fix bug #1090551
|
||||
download.pause().then(
|
||||
function() {
|
||||
navigator.mozDownloadManager.remove(download).then(
|
||||
function success() {
|
||||
req.done(download);
|
||||
},
|
||||
function error() {
|
||||
sendError(req,
|
||||
'DownloadManager doesnt know about this download',
|
||||
CODE.INVALID_STATE);
|
||||
}
|
||||
);
|
||||
},
|
||||
function() {
|
||||
sendError(req,
|
||||
'Failed to pause download before removal',
|
||||
CODE.INVALID_STATE);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
req.done(download);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return incompleteDownload ? req : doRemoveFromPhone(req, download);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the proper delete of the physical file, also
|
||||
* from the datastore if the download has finished.
|
||||
*
|
||||
* @param{Object} This is the Request object
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
function doRemoveFromPhone(deleteRequest, download) {
|
||||
var req = new Request();
|
||||
|
||||
deleteRequest.onsuccess = function() {
|
||||
var storage = getVolume(download.storageName);
|
||||
var storeAvailableReq = storage.available();
|
||||
|
||||
storeAvailableReq.onsuccess = function available_onsuccess(e) {
|
||||
var path = download.path;
|
||||
switch (storeAvailableReq.result) {
|
||||
case 'unavailable':
|
||||
sendError(req, ' Could not delete the file: ' + path + ' from ' +
|
||||
storageName, CODE.NO_SDCARD);
|
||||
break;
|
||||
|
||||
case 'shared':
|
||||
sendError(req, ' Could not delete the file: ' + path + ' from ' +
|
||||
storageName, CODE.UNMOUNTED_SDCARD);
|
||||
break;
|
||||
|
||||
default:
|
||||
var storeDeleteReq = storage.delete(download.storagePath);
|
||||
|
||||
storeDeleteReq.onsuccess = function store_onsuccess() {
|
||||
// Remove from the datastore if status is 'succeeded'
|
||||
// if we find any problem with the datastore, don't send
|
||||
// an error, since the physical remove already happened
|
||||
if (download.state === 'succeeded') {
|
||||
LazyLoader.load(['/shared/js/download/download_store.js'],
|
||||
function() {
|
||||
DownloadStore.remove(download);
|
||||
}
|
||||
);
|
||||
}
|
||||
req.done(storeDeleteReq.result);
|
||||
};
|
||||
|
||||
storeDeleteReq.onerror = function store_onerror() {
|
||||
sendError(req, storeDeleteReq.error.name +
|
||||
' Could not remove the file: ' + download.path + ' from ' +
|
||||
storageName, CODE.FILE_NOT_FOUND);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
storeAvailableReq.onerror = function available_onerror() {
|
||||
sendError(req, 'Error getting storage state ', CODE.DEVICE_STORAGE);
|
||||
};
|
||||
};
|
||||
|
||||
deleteRequest.onerror = function(error) {
|
||||
sendError(req, error.message, error.code);
|
||||
};
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function handlerError(error, download, cb) {
|
||||
LazyLoader.load('shared/js/download/download_ui.js', (function loaded() {
|
||||
var req;
|
||||
var show = DownloadUI.show;
|
||||
|
||||
// Canceled activites are normal and shouldn't be interpreted as errors.
|
||||
// Unfortunately, this isn't reported in a standard way by our
|
||||
// applications (or third party apps for that matter). This is why we
|
||||
// have this lazy filter here that may need to be updated in the future
|
||||
// but hopefully will just get removed.
|
||||
if (error.message &&
|
||||
(error.message.endsWith('canceled') ||
|
||||
error.message.endsWith('cancelled'))) {
|
||||
// Since this isn't actually an error, we invoke the callback with null.
|
||||
cb && cb(null);
|
||||
// and return early.
|
||||
return;
|
||||
}
|
||||
|
||||
switch (error.code) {
|
||||
case CODE.NO_SDCARD:
|
||||
case CODE.UNMOUNTED_SDCARD:
|
||||
case CODE.FILE_NOT_FOUND:
|
||||
case CODE.NO_PROVIDER:
|
||||
req = show(DownloadUI.TYPE[error.code], download, true);
|
||||
req.onconfirm = cb;
|
||||
|
||||
break;
|
||||
|
||||
case CODE.MIME_TYPE_NOT_SUPPORTED:
|
||||
req = show(DownloadUI.TYPE.UNSUPPORTED_FILE_TYPE, download, true);
|
||||
req.onconfirm = function tuftOnConfirm() {
|
||||
showRemoveFileUI(download, cb);
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
req = show(DownloadUI.TYPE.FILE_OPEN_ERROR, download, true);
|
||||
req.onconfirm = function tfoeOnConfirm() {
|
||||
showRemoveFileUI(download, cb);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
// We have to remove the notification if the user cancels
|
||||
req.oncancel = cb;
|
||||
}));
|
||||
}
|
||||
|
||||
function showRemoveFileUI(download, cb) {
|
||||
var req = DownloadUI.show(DownloadUI.TYPE.DELETE, download, true);
|
||||
|
||||
req.oncancel = cb;
|
||||
|
||||
req.onconfirm = function doRemove() {
|
||||
if (typeof cb === 'function') {
|
||||
cb(download);
|
||||
} else {
|
||||
remove(download);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getFreeSpace(cb) {
|
||||
var storage = navigator.getDeviceStorage(storageName);
|
||||
|
||||
if (!storage) {
|
||||
console.error('Cannot get free space size in sdcard');
|
||||
cb(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var req = storage.freeSpace();
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
cb(e.target.result);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
cb(null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the storage for the download based on the volumen
|
||||
* it was saved (storageName)
|
||||
*/
|
||||
function getVolume(volumeName) {
|
||||
// Per API design, all media type return the same volumes.
|
||||
// So we use 'sdcard' here for no reason.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=856782#c10
|
||||
var volumes = navigator.getDeviceStorages('sdcard');
|
||||
if (!volumeName || volumeName === '') {
|
||||
return volumes[0];
|
||||
}
|
||||
for (var i = 0; i < volumes.length; ++i) {
|
||||
if (volumes[i].storageName === volumeName) {
|
||||
return volumes[i];
|
||||
}
|
||||
}
|
||||
return volumes[0];
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* This method allows clients to open a downlaod
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
open: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.OPEN, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method allows clients to share a downlaoded file
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
share: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.SHARE, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method allows clients to set as wallaper a downlaoded file
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
wallpaper: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.WALLPAPER, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method allows clients to set as ringtone a downlaoded file
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
ringtone: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.RINGTONE, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method returns information about a download
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
info: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.INFO, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* Given a download, remove it from the DownloadManager
|
||||
* list, and the file system.
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
remove: remove,
|
||||
|
||||
/*
|
||||
* Returns exception code constants
|
||||
*/
|
||||
get CODE() {
|
||||
return CODE;
|
||||
},
|
||||
|
||||
/*
|
||||
* This method handles different errors when users attemp to open files
|
||||
*
|
||||
* @param{Object} Error object
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*
|
||||
* @param{Function} This function is performed when the flow is finished
|
||||
*/
|
||||
handlerError: handlerError,
|
||||
|
||||
/*
|
||||
* Returns the free memory size in bytes
|
||||
*
|
||||
* @param{Function} This function is performed when the free memory size has
|
||||
* been calculated
|
||||
*/
|
||||
getFreeSpace: getFreeSpace
|
||||
};
|
||||
}());
|
||||
|
|
@ -0,0 +1,451 @@
|
|||
'use strict';
|
||||
/* jshint moz:true */
|
||||
/* global Promise */
|
||||
/* global indexedDB */
|
||||
|
||||
/*
|
||||
* DownloadStore.js: Allows to store download finished ordered by timestamp
|
||||
*
|
||||
* - Adding a new download:
|
||||
*
|
||||
* var req = DownloadStore.add(download);
|
||||
*
|
||||
* req.onsuccess = function() {
|
||||
* console.log('Complete download stored successfully');
|
||||
* };
|
||||
*
|
||||
* req.onerror = function(e) {
|
||||
* console.error(e);
|
||||
* };
|
||||
*
|
||||
* - Getting all downloads:
|
||||
*
|
||||
* var reqStore = DownloadStore.getAll();
|
||||
*
|
||||
* reqStore.onsuccess = function(ev) {
|
||||
* ev.target.result.forEach(function(download) {
|
||||
* // Do something cool with the download
|
||||
* });
|
||||
* };
|
||||
*
|
||||
* reqStore.onerror = function(e) {
|
||||
* console.error(e);
|
||||
* };
|
||||
*
|
||||
* - Removing a existing download:
|
||||
*
|
||||
* var req = DownloadStore.remove(download);
|
||||
*
|
||||
* req.onsuccess = function() {
|
||||
* console.log('Deleted download successfully');
|
||||
* };
|
||||
*
|
||||
* req.onerror = function(e) {
|
||||
* console.error(e);
|
||||
* };
|
||||
*
|
||||
* WARNING:
|
||||
*
|
||||
* The system app should add this to its manifest file
|
||||
*
|
||||
* "datastores-owned": {
|
||||
* "download_store": {
|
||||
* "description": "Stores download finished"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The settings app should add this to its manifest file
|
||||
*
|
||||
* "datastores-access": {
|
||||
* "download_store": {
|
||||
* "description": "Stores download finished"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
/* exported DownloadStore */
|
||||
var DownloadStore = (function() {
|
||||
var datastore;
|
||||
|
||||
// Datastore name declared on the manifest.webapp
|
||||
const DATASTORE_NAME = 'download_store';
|
||||
|
||||
var downloadListDB;
|
||||
var downloadList = [];
|
||||
var lastRevisionId;
|
||||
|
||||
const LIST_DB = 'download_list';
|
||||
const LIST_STORE = 'download_list';
|
||||
|
||||
const LIST_KEY = 0;
|
||||
const REVISION_KEY = 1;
|
||||
|
||||
/*
|
||||
* Request auxiliary object to support asynchronous calls
|
||||
*/
|
||||
var Request = function() {
|
||||
this.done = function(result) {
|
||||
if (typeof this.onsuccess === 'function') {
|
||||
this.result = result;
|
||||
window.setTimeout(function() {
|
||||
this.onsuccess({
|
||||
target: this
|
||||
});
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
this.failed = function(error) {
|
||||
if (typeof this.onerror === 'function') {
|
||||
this.error = error;
|
||||
window.setTimeout(function() {
|
||||
this.onerror({
|
||||
target: this
|
||||
});
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function promisify(request) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
request.onsuccess = (event) => {resolve(event.target.result);};
|
||||
request.onerror = (event) => {reject(event.target.errorCode);};
|
||||
});
|
||||
}
|
||||
|
||||
// Indicates the initialization state
|
||||
var readyState;
|
||||
|
||||
function init(success, fail) {
|
||||
if (readyState === 'initialized') {
|
||||
success();
|
||||
return;
|
||||
}
|
||||
|
||||
if (readyState === 'initializing') {
|
||||
document.addEventListener('ds-initialized', function oninitalized() {
|
||||
document.removeEventListener('ds-initialized', oninitalized);
|
||||
success();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
readyState = 'initializing';
|
||||
|
||||
if (!navigator.getDataStores) {
|
||||
var messageError = 'Data store: DataStore API is not working';
|
||||
console.error(messageError);
|
||||
fail({
|
||||
message: messageError
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.getDataStores(DATASTORE_NAME).then(function(ds) {
|
||||
var messageError = 'Download Store: Cannot get access to the DataStore';
|
||||
if (ds.length < 1) {
|
||||
console.error(messageError);
|
||||
throw ({
|
||||
message: messageError
|
||||
});
|
||||
}
|
||||
|
||||
datastore = ds[0];
|
||||
// Checking the length as the index should be there
|
||||
}).then(function() {
|
||||
return openDownloadListIndexedDB();
|
||||
}).then(function(db) {
|
||||
downloadListDB = db;
|
||||
|
||||
var listStore = downloadListDB.transaction(LIST_STORE, 'readonly')
|
||||
.objectStore(LIST_STORE);
|
||||
|
||||
return Promise.all([
|
||||
promisify(listStore.get(LIST_KEY))
|
||||
.then((value) => {downloadList = value;}),
|
||||
promisify(listStore.get(REVISION_KEY))
|
||||
.then((value) => {lastRevisionId = value;})
|
||||
]);
|
||||
}).then(function() {
|
||||
readyState = 'initialized';
|
||||
|
||||
// Remove the legacy index element, if it exists. See Bug 1180250.
|
||||
const LEGACY_INDEX_ID = 1;
|
||||
if (downloadList && downloadList.includes(LEGACY_INDEX_ID)) {
|
||||
return promisify(get(LEGACY_INDEX_ID)).then((download) => {
|
||||
if ('byTimestamp' in download) {
|
||||
downloadList.splice(downloadList.indexOf(LEGACY_INDEX_ID), 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(function() {
|
||||
notifyOpenSuccess(success);
|
||||
}).catch(function(e) {
|
||||
console.error('Error while opening the Download Store: ', e.message);
|
||||
fail(e);
|
||||
});
|
||||
}
|
||||
|
||||
function openDownloadListIndexedDB() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var request = indexedDB.open(LIST_DB);
|
||||
|
||||
request.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
resolve(db);
|
||||
};
|
||||
|
||||
request.onerror = function(event) {
|
||||
reject(event.target.errorCode);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = function(event) {
|
||||
var db = event.target.result;
|
||||
|
||||
promisify(db.createObjectStore(LIST_STORE)).then(() => {
|
||||
var listStore = db.transaction(LIST_STORE, 'readwrite')
|
||||
.objectStore(LIST_STORE);
|
||||
|
||||
return Promise.all([
|
||||
promisify(listStore.add([], LIST_KEY)),
|
||||
promisify(listStore.add(0, REVISION_KEY))
|
||||
]);
|
||||
}).catch(reject);
|
||||
};
|
||||
|
||||
request.onblocked = function(event) {
|
||||
reject(event.target.errorCode);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function notifyOpenSuccess(cb) {
|
||||
window.setTimeout(cb, 0);
|
||||
document.dispatchEvent(new CustomEvent('ds-initialized'));
|
||||
}
|
||||
|
||||
// These fields will be stored in our datastore
|
||||
var fieldsToPropagate = ['url', 'path', 'totalBytes', 'contentType',
|
||||
'startTime', 'state', 'storageName', 'storagePath'];
|
||||
|
||||
function cookDownload(download) {
|
||||
var ret = Object.create(null);
|
||||
|
||||
fieldsToPropagate.forEach(function(field) {
|
||||
ret[field] = download[field];
|
||||
});
|
||||
|
||||
// This is the timestamp when the download finished
|
||||
ret.finalizeTime = new Date();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function defaultError(req) {
|
||||
return defaultErrorCb.bind(null, req);
|
||||
}
|
||||
|
||||
function defaultErrorCb(request, error) {
|
||||
request.failed(error);
|
||||
}
|
||||
|
||||
function defaultSuccess(req) {
|
||||
return defaultSuccessCb.bind(null, req);
|
||||
}
|
||||
|
||||
function defaultSuccessCb(request, result) {
|
||||
request.done(result);
|
||||
}
|
||||
|
||||
function updateDownloadList() {
|
||||
var cursor = datastore.sync(lastRevisionId);
|
||||
|
||||
function cursorResolve(task) {
|
||||
switch (task.operation) {
|
||||
case 'done':
|
||||
lastRevisionId = task.revisionId;
|
||||
|
||||
var listStore = downloadListDB.transaction(LIST_STORE, 'readwrite')
|
||||
.objectStore(LIST_STORE);
|
||||
|
||||
return Promise.all([
|
||||
promisify(listStore.put(downloadList, LIST_KEY)),
|
||||
promisify(listStore.put(lastRevisionId, REVISION_KEY))
|
||||
]);
|
||||
|
||||
case 'clear':
|
||||
downloadList = [];
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
downloadList.push(task.id);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
// This doesn't matter for us.
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
var i = downloadList.indexOf(task.id);
|
||||
if (i >= 0) {
|
||||
downloadList.splice(i, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return cursor.next().then(cursorResolve);
|
||||
}
|
||||
|
||||
return cursor.next().then(cursorResolve);
|
||||
}
|
||||
|
||||
function doAdd(download, req) {
|
||||
var downloadCooked = cookDownload(download);
|
||||
// Adding our cooked download
|
||||
datastore.add(downloadCooked).then(function(id) {
|
||||
// Enriched object with the id provided by the datastore
|
||||
downloadCooked.id = id;
|
||||
datastore.put(downloadCooked, id)
|
||||
.then(function() { req.done(downloadCooked); },
|
||||
defaultError(req));
|
||||
}, defaultError(req));
|
||||
}
|
||||
|
||||
function add(download) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
init(doAdd.bind(null, download, req), req.failed.bind(req));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function doGet(id, req) {
|
||||
datastore.get(id).then(function(download) { req.done(download); },
|
||||
defaultError(req));
|
||||
}
|
||||
|
||||
function get(id) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
init(doGet.bind(null, id, req), req.failed.bind(req));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function doGetAll(req) {
|
||||
updateDownloadList().then(function() {
|
||||
datastore.get.apply(datastore, downloadList).then(defaultSuccess(req),
|
||||
defaultError(req));
|
||||
}, defaultError(req));
|
||||
}
|
||||
|
||||
function getAll() {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
init(doGetAll.bind(null, req), req.failed.bind(req));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function doRemove(id, req) {
|
||||
// Removing the download object from datastore
|
||||
datastore.remove(id).then(function(success) {
|
||||
if (success) {
|
||||
defaultSuccess(req);
|
||||
} else {
|
||||
req.failed({
|
||||
message: 'The download with id: ' + id + 'does not exist'
|
||||
});
|
||||
}
|
||||
}, defaultError(req));
|
||||
}
|
||||
|
||||
function remove(download) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
init(doRemove.bind(null, download.id, req), req.failed.bind(req));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function doAddListener(listener) {
|
||||
datastore.addEventListener('change', listener);
|
||||
}
|
||||
|
||||
function addListener(listener) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
init(doAddListener.bind(null, listener), req.failed.bind(req));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function doRemoveListener(listener) {
|
||||
datastore.removeEventListener('change', listener);
|
||||
}
|
||||
|
||||
function removeListener(listener) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
init(doRemoveListener.bind(null, listener), req.failed.bind(req));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* This method returns the download with the specified id.
|
||||
*/
|
||||
get: get,
|
||||
|
||||
/*
|
||||
* This method returns an array of complete downloads
|
||||
*/
|
||||
getAll: getAll,
|
||||
|
||||
/*
|
||||
* It adds a new download object and returns the new datastore
|
||||
* download object
|
||||
*
|
||||
* @param{Object} Download object provided by the API
|
||||
*
|
||||
*/
|
||||
add: add,
|
||||
|
||||
/*
|
||||
* It removes a download object
|
||||
*
|
||||
* @param{Object} The download object to be removed from the datastore
|
||||
*
|
||||
*/
|
||||
remove: remove,
|
||||
|
||||
/*
|
||||
* Add a listener that will receive datastore change events.
|
||||
*
|
||||
* @param{Function} The listener to be added.
|
||||
*/
|
||||
addListener: addListener,
|
||||
|
||||
/*
|
||||
* Remove a listener previously added via addListener.
|
||||
*
|
||||
* @param{Function} The listener to be removed.
|
||||
*/
|
||||
removeListener: removeListener
|
||||
};
|
||||
}());
|
||||
154
assets/webapps/uitest.gaiamobile.org/shared/js/lazy_loader.js
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* exported LazyLoader */
|
||||
/* globals HtmlImports, Promise */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This contains a simple LazyLoader implementation
|
||||
* To use:
|
||||
*
|
||||
* LazyLoader.load(
|
||||
* ['/path/to/file.js', '/path/to/file.css', 'domNode'], callback
|
||||
* );
|
||||
*/
|
||||
var LazyLoader = (function() {
|
||||
|
||||
function LazyLoader() {
|
||||
this._loaded = {};
|
||||
this._isLoading = {};
|
||||
}
|
||||
|
||||
LazyLoader.prototype = {
|
||||
|
||||
_js: function(file, callback) {
|
||||
var script = document.createElement('script');
|
||||
script.src = file;
|
||||
// until bug 916255 lands async is the default so
|
||||
// we must disable it so scripts load in the order they where
|
||||
// required.
|
||||
script.async = false;
|
||||
script.addEventListener('load', callback);
|
||||
document.head.appendChild(script);
|
||||
this._isLoading[file] = script;
|
||||
},
|
||||
|
||||
_css: function(file, callback) {
|
||||
var style = document.createElement('link');
|
||||
style.type = 'text/css';
|
||||
style.rel = 'stylesheet';
|
||||
style.href = file;
|
||||
document.head.appendChild(style);
|
||||
callback();
|
||||
},
|
||||
|
||||
_html: function(domNode, callback) {
|
||||
|
||||
// The next few lines are for loading html imports in DEBUG mode
|
||||
if (domNode.getAttribute('is')) {
|
||||
this.load(['/shared/js/html_imports.js'], function() {
|
||||
HtmlImports.populate(callback);
|
||||
}.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < domNode.childNodes.length; i++) {
|
||||
if (domNode.childNodes[i].nodeType == document.COMMENT_NODE) {
|
||||
domNode.innerHTML = domNode.childNodes[i].nodeValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
window.dispatchEvent(new CustomEvent('lazyload', {
|
||||
detail: domNode
|
||||
}));
|
||||
|
||||
callback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves content of JSON file.
|
||||
*
|
||||
* @param {String} file Path to JSON file
|
||||
* @param {Boolean} mozSystem If xhr should use mozSystem permissions
|
||||
* @return {Promise} A promise that resolves to the JSON content
|
||||
* or null in case of invalid path. Rejects if an error occurs.
|
||||
*/
|
||||
getJSON: function(file, mozSystem) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var xhr;
|
||||
if (mozSystem) {
|
||||
xhr = new XMLHttpRequest({mozSystem: true});
|
||||
} else {
|
||||
xhr = new XMLHttpRequest();
|
||||
}
|
||||
xhr.open('GET', file, true);
|
||||
xhr.responseType = 'json';
|
||||
|
||||
xhr.onerror = function(error) {
|
||||
reject(error);
|
||||
};
|
||||
xhr.onload = function() {
|
||||
if (xhr.response !== null) {
|
||||
resolve(xhr.response);
|
||||
} else {
|
||||
reject(new Error('No valid JSON object was found (' +
|
||||
xhr.status + ' ' + xhr.statusText + ')'));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
});
|
||||
},
|
||||
|
||||
load: function(files, callback) {
|
||||
var deferred = {};
|
||||
deferred.promise = new Promise(resolve => {
|
||||
deferred.resolve = resolve;
|
||||
});
|
||||
|
||||
if (!Array.isArray(files)) {
|
||||
files = [files];
|
||||
}
|
||||
|
||||
var loadsRemaining = files.length, self = this;
|
||||
function perFileCallback(file) {
|
||||
if (self._isLoading[file]) {
|
||||
delete self._isLoading[file];
|
||||
}
|
||||
self._loaded[file] = true;
|
||||
|
||||
if (--loadsRemaining === 0) {
|
||||
deferred.resolve();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
|
||||
if (this._loaded[file.id || file]) {
|
||||
perFileCallback(file);
|
||||
} else if (this._isLoading[file]) {
|
||||
this._isLoading[file].addEventListener(
|
||||
'load', perFileCallback.bind(null, file));
|
||||
} else {
|
||||
var method, idx;
|
||||
if (typeof file === 'string') {
|
||||
method = file.match(/\.([^.]+)$/)[1];
|
||||
idx = file;
|
||||
} else {
|
||||
method = 'html';
|
||||
idx = file.id;
|
||||
}
|
||||
|
||||
this['_' + method](file, perFileCallback.bind(null, idx));
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
return new LazyLoader();
|
||||
}());
|
||||
135
assets/webapps/uitest.gaiamobile.org/shared/js/mime_mapper.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* exported MimeMapper */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* MimeMapper helps gaia apps to decide the mapping of mimetype and extension.
|
||||
* The use cases often happen when apps need to know about the exact
|
||||
* mimetypes or extensions, such as to delegate the open web activity, we must
|
||||
* have suitable mimetypes or extensions to request the right activity
|
||||
*
|
||||
* The mapping is basically created according to:
|
||||
* http://en.wikipedia.org/wiki/Internet_media_type
|
||||
*
|
||||
* The supported formats are considered base on the deviceStorage properties:
|
||||
* http://dxr.mozilla.org/mozilla-central/toolkit/content/
|
||||
* devicestorage.properties
|
||||
*
|
||||
*/
|
||||
|
||||
var MimeMapper = {
|
||||
// This list only contains the extensions we currently supported
|
||||
// We should make it more complete for further usages
|
||||
_typeToExtensionMap: {
|
||||
// Image
|
||||
'image/jpeg': 'jpg',
|
||||
'image/png': 'png',
|
||||
'image/gif': 'gif',
|
||||
'image/bmp': 'bmp',
|
||||
// Audio
|
||||
'audio/mpeg': 'mp3',
|
||||
'audio/mp4': 'm4a',
|
||||
'audio/ogg': 'ogg',
|
||||
'audio/webm': 'webm',
|
||||
'audio/3gpp': '3gp',
|
||||
'audio/amr': 'amr',
|
||||
// Video
|
||||
'video/mp4': 'mp4',
|
||||
'video/mpeg': 'mpg',
|
||||
'video/ogg': 'ogg',
|
||||
'video/webm': 'webm',
|
||||
'video/3gpp': '3gp',
|
||||
'video/3gpp2': '3g2',
|
||||
// Application
|
||||
// If we want to support some types, like pdf, just add
|
||||
'application/pdf': 'pdf',
|
||||
'application/vcard': 'vcf',
|
||||
// Text
|
||||
'text/vcard': 'vcf',
|
||||
'text/x-vcard': 'vcf'
|
||||
},
|
||||
|
||||
// This list only contains the mimetypes we currently supported
|
||||
// We should make it more complete for further usages
|
||||
_extensionToTypeMap: {
|
||||
// Image
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'jpe': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'gif': 'image/gif',
|
||||
'bmp': 'image/bmp',
|
||||
// Audio
|
||||
'mp3': 'audio/mpeg',
|
||||
'm4a': 'audio/mp4',
|
||||
'm4b': 'audio/mp4',
|
||||
'm4p': 'audio/mp4',
|
||||
'm4r': 'audio/mp4',
|
||||
'aac': 'audio/aac',
|
||||
'opus': 'audio/ogg',
|
||||
'amr': 'audio/amr',
|
||||
// Video
|
||||
'mp4': 'video/mp4',
|
||||
'mpeg': 'video/mpeg',
|
||||
'mpg': 'video/mpeg',
|
||||
'ogv': 'video/ogg',
|
||||
'ogx': 'video/ogg',
|
||||
'webm': 'video/webm',
|
||||
'3gp': 'video/3gpp',
|
||||
'3g2': 'video/3gpp2',
|
||||
'ogg': 'video/ogg',
|
||||
// Application
|
||||
// If we want to support some extensions, like pdf, just add
|
||||
'pdf': 'application/pdf',
|
||||
// Text
|
||||
'vcf': 'text/vcard'
|
||||
},
|
||||
_parseExtension: function(filename) {
|
||||
var array = filename.split('.');
|
||||
return array.length > 1 ? array.pop() : '';
|
||||
},
|
||||
|
||||
isSupportedType: function(mimetype) {
|
||||
return (mimetype in this._typeToExtensionMap);
|
||||
},
|
||||
|
||||
isSupportedExtension: function(extension) {
|
||||
return (extension in this._extensionToTypeMap);
|
||||
},
|
||||
|
||||
isFilenameMatchesType: function(filename, mimetype) {
|
||||
var extension = this._parseExtension(filename);
|
||||
var guessedType = this.guessTypeFromExtension(extension);
|
||||
return (guessedType == mimetype);
|
||||
},
|
||||
|
||||
guessExtensionFromType: function(mimetype) {
|
||||
return this._typeToExtensionMap[mimetype];
|
||||
},
|
||||
|
||||
guessTypeFromExtension: function(extension) {
|
||||
return this._extensionToTypeMap[extension];
|
||||
},
|
||||
|
||||
// If mimetype is not in the supported list, we will try to
|
||||
// predict the possible valid mimetype based on extension.
|
||||
guessTypeFromFileProperties: function(filename, mimetype) {
|
||||
var extension = this._parseExtension(filename);
|
||||
var type = this.isSupportedType(mimetype) ?
|
||||
mimetype : this.guessTypeFromExtension(extension);
|
||||
return type || '';
|
||||
},
|
||||
|
||||
// if mimetype is not supported, preserve the original extension
|
||||
// and add the predict result as new extension.
|
||||
// If both filename and mimetype are not supported, return the original
|
||||
// filename.
|
||||
ensureFilenameMatchesType: function(filename, mimetype) {
|
||||
if (!this.isFilenameMatchesType(filename, mimetype)) {
|
||||
var guessedExt = this.guessExtensionFromType(mimetype);
|
||||
if (guessedExt) {
|
||||
filename += '.' + guessedExt;
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/* exported SettingsListener */
|
||||
'use strict';
|
||||
|
||||
var SettingsListener = {
|
||||
/* lock stores here */
|
||||
_lock: null,
|
||||
|
||||
/* keep record of observers in order to remove them in the future */
|
||||
_observers: [],
|
||||
|
||||
/**
|
||||
* getSettingsLock: create a lock or retrieve one that we saved.
|
||||
* mozSettings.createLock() is expensive and lock should be reused
|
||||
* whenever possible.
|
||||
*/
|
||||
getSettingsLock: function sl_getSettingsLock() {
|
||||
// If there is a lock present we return that
|
||||
if (this._lock && !this._lock.closed) {
|
||||
return this._lock;
|
||||
}
|
||||
|
||||
// If there isn't we return one.
|
||||
var settings = window.navigator.mozSettings;
|
||||
|
||||
return (this._lock = settings.createLock());
|
||||
},
|
||||
|
||||
observe: function sl_observe(name, defaultValue, callback) {
|
||||
var settings = window.navigator.mozSettings;
|
||||
if (!settings) {
|
||||
window.setTimeout(function() { callback(defaultValue); });
|
||||
return;
|
||||
}
|
||||
|
||||
var req;
|
||||
try {
|
||||
req = this.getSettingsLock().get(name);
|
||||
} catch (e) {
|
||||
// It is possible (but rare) for getSettingsLock() to return
|
||||
// a SettingsLock object that is no longer valid.
|
||||
// Until https://bugzilla.mozilla.org/show_bug.cgi?id=793239
|
||||
// is fixed, we just catch the resulting exception and try
|
||||
// again with a fresh lock
|
||||
console.warn('Stale lock in settings_listener.js.',
|
||||
'See https://bugzilla.mozilla.org/show_bug.cgi?id=793239');
|
||||
this._lock = null;
|
||||
req = this.getSettingsLock().get(name);
|
||||
}
|
||||
|
||||
req.addEventListener('success', (function onsuccess() {
|
||||
callback(typeof(req.result[name]) != 'undefined' ?
|
||||
req.result[name] : defaultValue);
|
||||
}));
|
||||
|
||||
var settingChanged = function settingChanged(evt) {
|
||||
callback(evt.settingValue);
|
||||
};
|
||||
settings.addObserver(name, settingChanged);
|
||||
this._observers.push({
|
||||
name: name,
|
||||
callback: callback,
|
||||
observer: settingChanged
|
||||
});
|
||||
},
|
||||
|
||||
unobserve: function sl_unobserve(name, callback) {
|
||||
var settings = window.navigator.mozSettings;
|
||||
var that = this;
|
||||
this._observers.forEach(function(value, index) {
|
||||
if (value.name === name && value.callback === callback) {
|
||||
settings.removeObserver(name, value.observer);
|
||||
that._observers.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* exported SettingsURL */
|
||||
'use strict';
|
||||
|
||||
// This library helps with managing settings that can either send back
|
||||
// string uris or blobs that need to be turned into blob: uris.
|
||||
// Usage is as follows:
|
||||
// var blobURL = new SettingsURL();
|
||||
// element.src = blobURL.set(settingValue);
|
||||
|
||||
function SettingsURL() {
|
||||
this._url = null;
|
||||
this._isBlob = false;
|
||||
}
|
||||
|
||||
SettingsURL.prototype = {
|
||||
set: function(value) {
|
||||
if (this._isBlob) {
|
||||
window.URL.revokeObjectURL(this._url);
|
||||
}
|
||||
if (value instanceof Blob) {
|
||||
this._isBlob = true;
|
||||
this._url = window.URL.createObjectURL(value);
|
||||
} else {
|
||||
this._isBlob = false;
|
||||
this._url = value;
|
||||
}
|
||||
return this._url;
|
||||
},
|
||||
|
||||
get: function() {
|
||||
return this._url;
|
||||
}
|
||||
};
|
||||
428
assets/webapps/uitest.gaiamobile.org/shared/style/buttons.css
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
/* ----------------------------------
|
||||
* Buttons
|
||||
* ---------------------------------- */
|
||||
|
||||
.button::-moz-focus-inner,
|
||||
.bb-button::-moz-focus-inner,
|
||||
button::-moz-focus-inner {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button,
|
||||
.bb-button,
|
||||
.button {
|
||||
font-family: sans-serif;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
margin: 0 0 1rem;
|
||||
padding: 0 1.2rem;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background: #d8d8d8;
|
||||
border: none;
|
||||
border-radius: 2rem;
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
line-height: 4rem;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Recommend */
|
||||
button.recommend,
|
||||
.bb-button.recommend {
|
||||
background-color: #00caf2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Danger */
|
||||
button.danger,
|
||||
.danger.bb-button {
|
||||
background-color: #e51e1e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Pressed */
|
||||
button:active,
|
||||
.bb-button:active,
|
||||
.button:active {
|
||||
background-color: #b2f2ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
button[data-icon],
|
||||
.bb-button[data-icon],
|
||||
.button[data-icon] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
button[data-icon]:before,
|
||||
.bb-button[data-icon]:before,
|
||||
.button[data-icon]:before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -1.5rem;
|
||||
offset-inline-end: 1.5rem;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
button[data-icon]:active:before,
|
||||
.bb-button[data-icon]:active:before,
|
||||
.button[data-icon]:active:before {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Pressed with dark skin */
|
||||
.skin-dark button:active,
|
||||
.skin-dark .bb-button:active,
|
||||
.skin-dark .button:active {
|
||||
background-color: #00aacc;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
button[disabled],
|
||||
.bb-button[aria-disabled="true"],
|
||||
.button[aria-disabled="true"] {
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
color: #c7c7c7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
button[disabled].recommend,
|
||||
.bb-button[aria-disabled="true"].recommend,
|
||||
.button[aria-disabled="true"].recommend {
|
||||
background-color: #97cbd5;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
button[disabled].danger,
|
||||
.bb-button[aria-disabled="true"].danger,
|
||||
.button[aria-disabled="true"].danger {
|
||||
background-color: #eb7474;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
/* Disabled with dark background */
|
||||
.skin-dark button[disabled],
|
||||
.skin-dark .button[aria-disabled="true"],
|
||||
.skin-dark .bb-button[aria-disabled="true"] {
|
||||
background-color: #565656;
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
.skin-dark button[disabled].recommend,
|
||||
.skin-dark .button[aria-disabled="true"].recommend,
|
||||
.skin-dark .bb-button[aria-disabled="true"].recommend {
|
||||
background-color: #006579;
|
||||
}
|
||||
|
||||
.skin-dark button[disabled].danger,
|
||||
.skin-dark .button[aria-disabled="true"].danger,
|
||||
.skin-dark .bb-button[aria-disabled="true"].danger {
|
||||
background-color: #730f0f;
|
||||
}
|
||||
|
||||
button[disabled]::-moz-selection,
|
||||
.button[aria-disabled="true"]::-moz-selection,
|
||||
.bb-button[aria-disabled="true"]::-moz-selection {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
/* ----------------------------------
|
||||
* Buttons inside lists
|
||||
* ---------------------------------- */
|
||||
|
||||
li button,
|
||||
li .bb-button,
|
||||
li .button {
|
||||
position: relative;
|
||||
border: 0.1rem solid #c7c7c7;
|
||||
background: rgba(0,0,0,0.05);
|
||||
margin: 0 0 1rem;
|
||||
overflow: hidden;
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
height: auto;
|
||||
min-height: 4rem;
|
||||
text-overflow: initial;
|
||||
white-space: normal;
|
||||
line-height: 2rem;
|
||||
padding: 1rem 1.2rem;
|
||||
}
|
||||
html[dir="ltr"] li button,
|
||||
html[dir="ltr"] li .bb-button,
|
||||
html[dir="ltr"] li .button {
|
||||
text-align: left;
|
||||
}
|
||||
html[dir="rtl"] li button,
|
||||
html[dir="rtl"] li .bb-button,
|
||||
html[dir="rtl"] li .button {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Press */
|
||||
li .bb-button:active:after,
|
||||
li .button:active:after,
|
||||
li button:active:after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
li button:disabled,
|
||||
li .bb-button[aria-disabled="true"],
|
||||
li .button[aria-disabled="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
li button.icon,
|
||||
li .bb-button.icon,
|
||||
li .button.icon {
|
||||
padding-inline-end: 4rem;
|
||||
}
|
||||
|
||||
li button.icon:before,
|
||||
li .bb-button.icon:before,
|
||||
li .button.icon:before {
|
||||
content: "";
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
offset-inline-end: 0.5rem;
|
||||
margin-top: -1.5rem;
|
||||
background: transparent no-repeat center center / 100% auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
li button[data-icon]:before,
|
||||
li .bb-button[data-icon]:before,
|
||||
li .button[data-icon]:before {
|
||||
offset-inline-end: 1rem;
|
||||
}
|
||||
|
||||
li button.icon-view:active,
|
||||
li .bb-button.icon-view:active,
|
||||
li .button.icon-view:active {
|
||||
background-color: #b2f2ff;
|
||||
}
|
||||
|
||||
li button[data-icon="expand"]:before,
|
||||
li .bb-button[data-icon="expand"]:before,
|
||||
li .button[data-icon="expand"]:before {
|
||||
color: #00aac5;
|
||||
top: unset;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
li button[data-icon="expand"][disabled]:before,
|
||||
li .bb-button[data-icon="expand"][aria-disabled="true"]:before,
|
||||
li .button[data-icon="expand"][aria-disabled="true"]:before {
|
||||
color: #A9A9A9;
|
||||
}
|
||||
|
||||
li button[data-icon="forward"]:before,
|
||||
li .bb-button[data-icon="forward"]:before,
|
||||
li .button[data-icon="forward"]:before {
|
||||
color: #686f72;
|
||||
}
|
||||
|
||||
/* Getting more space for text, as icon is shorter */
|
||||
li button.icon-dialog,
|
||||
li .bb-button.icon-dialog,
|
||||
li .button.icon-dialog {
|
||||
padding-inline-end: 3rem;
|
||||
}
|
||||
|
||||
li button.icon-dialog:before,
|
||||
li .bb-button.icon-dialog:before,
|
||||
li .button.icon-dialog:before {
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: auto;
|
||||
offset-inline-end: 0.8rem;
|
||||
bottom: 0.5rem;
|
||||
border-top: 0.7rem solid transparent;
|
||||
border-bottom: 0.7rem solid transparent;
|
||||
border-inline-start: 0.7rem solid #00aac5;
|
||||
z-index: 1;
|
||||
}
|
||||
html[dir="ltr"] li button.icon-dialog:before,
|
||||
html[dir="ltr"] li .bb-button.icon-dialog:before,
|
||||
html[dir="ltr"] li .button.icon-dialog:before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
html[dir="rtl"] li button.icon-dialog:before,
|
||||
html[dir="rtl"] li .bb-button.icon-dialog:before,
|
||||
html[dir="rtl"] li .button.icon-dialog:before {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
li button.icon-dialog:disabled:before,
|
||||
li .bb-button[aria-disabled="true"].icon-dialog:before,
|
||||
li .button[aria-disabled="true"].icon-dialog:before {
|
||||
border-inline-start: 0.7rem solid #a9a9a9;
|
||||
}
|
||||
|
||||
/* ----------------------------------
|
||||
* Theme: Dark
|
||||
* ---------------------------------- */
|
||||
|
||||
.skin-dark li button,
|
||||
.skin-dark li .bb-button,
|
||||
.skin-dark li .button {
|
||||
background-color: #454545;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.skin-dark li button:active,
|
||||
.skin-dark li .button:active,
|
||||
.skin-dark li .bb-button:active {
|
||||
background-color: #00aacc;
|
||||
}
|
||||
|
||||
.skin-dark li button.icon-view:active,
|
||||
.skin-dark li .button.icon-view:active,
|
||||
.skin-dark li .bb-button.icon-view:active {
|
||||
background-color: #00aacc;
|
||||
}
|
||||
|
||||
.skin-dark li button.icon-dialog:active:before,
|
||||
.skin-dark li .bb-button.icon-dialog:active:before,
|
||||
.skin-dark li .button.icon-dialog:active:before {
|
||||
border-inline-start: 0.7rem solid #b2f2ff;
|
||||
}
|
||||
|
||||
.skin-dark li button:disabled,
|
||||
.skin-dark li .bb-button[aria-disabled="true"],
|
||||
.skin-dark li .button[aria-disabled="true"] {
|
||||
background-color: #454545;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Inputs inside of .button */
|
||||
.button input,
|
||||
body[role="application"] .bb-button input,
|
||||
body[role="application"] .button input {
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* select */
|
||||
select {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
font-style: italic;
|
||||
font-size: 1.6rem;
|
||||
color: #333;
|
||||
margin: 0 0 1rem;
|
||||
padding: 0;
|
||||
padding-inline-start: 1.1rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
height: 4rem;
|
||||
border: 0.1rem solid #c7c7c7;
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.skin-dark select {
|
||||
background-color: rgba(87, 87, 87, 0.8);
|
||||
border: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* remove dotted outline */
|
||||
select:-moz-focusring {
|
||||
color: transparent;
|
||||
text-shadow: 0 0 0 #333;
|
||||
}
|
||||
|
||||
.skin-dark select:-moz-focusring {
|
||||
text-shadow: 0 0 0 #fff;
|
||||
}
|
||||
|
||||
/* Hides dropdown arrow until bug #649849 is fixed */
|
||||
.bb-button.icon select,
|
||||
.button.icon select,
|
||||
.bb-button[data-icon] select,
|
||||
.button[data-icon] select {
|
||||
position: absolute;
|
||||
top: -0.6rem;
|
||||
offset-inline-start: 0;
|
||||
width: calc(100% + 3rem);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-inline-end: 5rem;
|
||||
text-indent: 0;
|
||||
font-weight: 400;
|
||||
font-size: 1.7rem;
|
||||
color: rgba(0,0,0,0);
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
/**
|
||||
* The select element uses the text color to determine the color of the outline.
|
||||
* We put a text shadow with needed offset and no blur, so that replaces the text.
|
||||
*/
|
||||
html[dir="ltr"] .bb-button.icon select,
|
||||
html[dir="ltr"] .button.icon select,
|
||||
html[dir="ltr"] .bb-button[data-icon] select,
|
||||
html[dir="ltr"] .button[data-icon] select,
|
||||
html[dir="ltr"] .bb-button.icon-dialog select,
|
||||
html[dir="ltr"] .button.icon-dialog select {
|
||||
text-shadow: 1rem 0.5rem 0 #333;
|
||||
}
|
||||
html[dir="rtl"] .bb-button.icon select,
|
||||
html[dir="rtl"] .button.icon select,
|
||||
html[dir="rtl"] .bb-button[data-icon] select,
|
||||
html[dir="rtl"] .button[data-icon] select,
|
||||
html[dir="rtl"] .bb-button.icon-dialog select,
|
||||
html[dir="rtl"] .button.icon-dialog select {
|
||||
text-shadow: -1rem 0.5rem 0 #333;
|
||||
}
|
||||
|
||||
.bb-button.icon select option,
|
||||
.button.icon select option,
|
||||
.bb-button[data-icon] select option,
|
||||
.button[data-icon] select option {
|
||||
color: #000;
|
||||
border: 0;
|
||||
padding: 0.6rem 1.3rem;
|
||||
text-shadow: 0 0 0 rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.bb-button.icon-dialog,
|
||||
.button.icon-dialog {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .skin-dark .bb-button.icon select,
|
||||
html[dir="ltr"] .skin-dark .button.icon select,
|
||||
html[dir="ltr"] .skin-dark .bb-button[data-icon] select,
|
||||
html[dir="ltr"] .skin-dark .button[data-icon] select,
|
||||
html[dir="ltr"] .skin-dark .bb-button.icon-dialog select,
|
||||
html[dir="ltr"] .skin-dark .button.icon-dialog select {
|
||||
text-shadow: 1rem 0.5rem 0 #fff;
|
||||
}
|
||||
html[dir="rtl"] .skin-dark .bb-button.icon select,
|
||||
html[dir="rtl"] .skin-dark .button.icon select,
|
||||
html[dir="rtl"] .skin-dark .bb-button[data-icon] select,
|
||||
html[dir="rtl"] .skin-dark .button[data-icon] select,
|
||||
html[dir="rtl"] .skin-dark .bb-button.icon-dialog select,
|
||||
html[dir="rtl"] .skin-dark .button.icon-dialog select {
|
||||
text-shadow: -1rem 0.5rem 0 #fff;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><path fill="#667174" fill-opacity=".7" d="m 18.2995,19.66 -5.056,-5.057 5.056,-5.056 c -0.31,-0.516 -0.62,-1.032 -0.93,-1.34 l -5.672,5.673 v 1.652 l 5.675,5.676 c 0.31,-0.515 0.722,-0.928 0.93,-1.547 z"/></svg>
|
||||
|
After Width: | Height: | Size: 294 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><path fill="#667174" fill-opacity=".7" d="m 11.698,19.66 5.056,-5.057 -5.056,-5.056 c 0.31,-0.516 0.62,-1.032 0.93,-1.34 L 18.3,13.88 v 1.652 l -5.675,5.676 c -0.31,-0.515 -0.722,-0.928 -0.93,-1.547 z"/></svg>
|
||||
|
After Width: | Height: | Size: 292 B |
|
|
@ -0,0 +1,280 @@
|
|||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Buttons</title>
|
||||
<link rel="stylesheet" href="../buttons.css">
|
||||
<link rel="stylesheet" href="../../elements/gaia-icons/gaia-icons-embedded.css">
|
||||
<link rel="stylesheet" href="../../elements/gaia-icons/bidi-helper.css">
|
||||
|
||||
<!--
|
||||
- These <style> and <script> elements are only used for the example code,
|
||||
- they are not needed for the real case.
|
||||
-->
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
label.bb-docs {
|
||||
font-family: sans-serif;
|
||||
font-size: 1.2rem;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
h2.bb-docs {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 300;
|
||||
color: #666;
|
||||
margin: -0.1rem 0 0;
|
||||
background-color: #f5f5f5;
|
||||
padding: 0.4rem 3rem;
|
||||
border: solid 0.1rem #e8e8e8;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.bb-docs {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
div.bb-docs.skin-organic {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
div.bb-docs.skin-dark {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
body[role="application"] > [role="region"] {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[data-icon]:before {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
document.getElementById('dir').checked = false;
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body role="application">
|
||||
<p dir="ltr">
|
||||
<label class="bb-docs">
|
||||
<input id="dir" type="checkbox" checked="false"
|
||||
onchange="document.documentElement.dir = this.checked ? 'rtl' : 'ltr'">
|
||||
RTL mode
|
||||
</label>
|
||||
</p>
|
||||
<h2 class="bb-docs">Special buttons</h2>
|
||||
<div class="bb-docs skin-organic">
|
||||
<button>Default</button>
|
||||
<button class="recommend">Recommend</button>
|
||||
<button class="danger">Danger</button>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Buttons with icons</h2>
|
||||
<div class="bb-docs skin-organic">
|
||||
<button data-icon="forward" aria-label="forward" data-l10n-id="">Default</button>
|
||||
<button data-icon="forward" aria-label="forward" data-l10n-id="" class="recommend">Recommend</button>
|
||||
<button data-icon="forward" aria-label="forward" data-l10n-id="" class="danger">Danger</button>
|
||||
<button data-icon="forward-light" aria-label="forward-light" data-l10n-id="">Default</button>
|
||||
<button data-icon="back" aria-label="back" data-l10n-id="">Default</button>
|
||||
<button data-icon="back-light" aria-label="back-light" data-l10n-id="">Default</button>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Disabled</h2>
|
||||
<div class="bb-docs skin-organic">
|
||||
<button disabled>Default</button>
|
||||
<a href="#" role="button" aria-disabled="true" class="recommend bb-button">Recommend</a>
|
||||
<button disabled class="danger">Danger</button>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Skin dark</h2>
|
||||
<div class="bb-docs skin-dark">
|
||||
<button>Default</button>
|
||||
<button class="recommend">Recommend</button>
|
||||
<button class="danger">Danger</button>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Disabled</h2>
|
||||
<div class="bb-docs skin-dark">
|
||||
<button disabled>Default</button>
|
||||
<a href="#" role="button" aria-disabled="true" class="recommend bb-button">Recommend</a>
|
||||
<button disabled class="danger">Danger</button>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">List buttons</h2>
|
||||
<div class="bb-docs skin-organic">
|
||||
<ul>
|
||||
<li>
|
||||
<button>Default</button>
|
||||
</li>
|
||||
<li>
|
||||
<button disabled>Disabled</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon icon-view">
|
||||
Icon view button
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon icon-dialog">
|
||||
Icon dialog button
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<span class="button icon icon-dialog">
|
||||
<select>
|
||||
<option selected>Icon dialog with options</option>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
<option>option 3</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon icon-dialog" disabled>
|
||||
Icon dialog button disabled
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button data-icon="forward" aria-label="forward" data-l10n-id="">
|
||||
Side navigation
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button disabled data-icon="forward" aria-label="forward" data-l10n-id="">
|
||||
Side navigation
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-icon="expand" class="bb-button" aria-label="expand" data-l10n-id="">
|
||||
Tuesday Sept. 18, 2012
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" aria-disabled="true" data-icon="expand" class="bb-button" aria-label="expand" data-l10n-id="">
|
||||
Tuesday Sept. 18, 2012
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Skin Dark</h2>
|
||||
<div class="bb-docs skin-dark">
|
||||
<ul>
|
||||
<li>
|
||||
<button>Default</button>
|
||||
</li>
|
||||
<li>
|
||||
<button disabled>Disabled</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon icon-dialog">
|
||||
Dark icon dialog button
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button data-icon="forward" aria-label="forward" data-l10n-id="">
|
||||
Side navigation
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button disabled data-icon="forward" aria-label="forward" data-l10n-id="">
|
||||
Side navigation
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-icon="expand" class="bb-button" aria-label="expand" data-l10n-id="">
|
||||
Tuesday Sept. 18, 2012
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" aria-disabled="true" data-icon="expand" class="bb-button" aria-label="expand" data-l10n-id="">
|
||||
Tuesday Sept. 18, 2012
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Select</h2>
|
||||
<div class="bb-docs">
|
||||
<select>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Fake select</h2>
|
||||
<div class="bb-docs">
|
||||
<ul>
|
||||
<li>
|
||||
<span class="bb-button" data-icon="expand" aria-label="expand" data-l10n-id="">
|
||||
<select>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Disabled select</h2>
|
||||
<div class="bb-docs">
|
||||
<ul>
|
||||
<li>
|
||||
<span aria-disabled="true" class="bb-button" data-icon="expand" aria-label="expand" data-l10n-id="">
|
||||
<select>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Skin dark</h2>
|
||||
<div class="bb-docs skin-dark">
|
||||
<select>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h2 class="bb-docs">Fake select</h2>
|
||||
<div class="bb-docs skin-dark">
|
||||
<ul>
|
||||
<li>
|
||||
<span class="bb-button" data-icon="expand" aria-label="expand" data-l10n-id="">
|
||||
<select>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span aria-disabled="true" class="bb-button" data-icon="expand" aria-label="expand" data-l10n-id="">
|
||||
<select>
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,575 @@
|
|||
/* ----------------------------------
|
||||
* Input areas
|
||||
* ---------------------------------- */
|
||||
|
||||
/**
|
||||
* BiDi notes:
|
||||
*
|
||||
* - as the <input>, <textarea> and [contentEditable] elements are filled by
|
||||
* the user, they need a `unicode-bidi: -moz-plaintext` attribute for their
|
||||
* content to be displayed properly (e.g. LTR content in an RTL document,
|
||||
* or vice-versa);
|
||||
*
|
||||
* - consequence: *-inline-start/end properties can still be used on such
|
||||
* elements content direction doesn't impact element's direction
|
||||
*
|
||||
* - however, the one-line inputs must still be aligned along with the
|
||||
* *document* direction: to the left in LTR, to the right in RTL
|
||||
* -- no matter what the *content* direction is.
|
||||
*/
|
||||
|
||||
html[dir="ltr"] input { text-align: left; }
|
||||
html[dir="rtl"] input { text-align: right; }
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
input[type="search"],
|
||||
input[type="url"],
|
||||
input[type="number"],
|
||||
textarea {
|
||||
font-family: sans-serif;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
line-height: 4rem;
|
||||
resize: none;
|
||||
padding: 0 1.5rem;
|
||||
font-size: 1.6rem;
|
||||
border: 0.1rem solid #c7c7c7;
|
||||
border-radius: 0;
|
||||
box-shadow: none; /* override the box-shadow from the system (performance issue) */
|
||||
color: #333;
|
||||
background: #fff;
|
||||
margin: 0 0 1rem;
|
||||
unicode-bidi: -moz-plaintext;
|
||||
}
|
||||
|
||||
label:active {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 10rem;
|
||||
max-height: 10rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
input::-moz-placeholder,
|
||||
textarea::-moz-placeholder {
|
||||
color: #a9a9a9;
|
||||
opacity: 1;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
input[disabled],
|
||||
textarea[disabled] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.skin-dark input[type="text"],
|
||||
.skin-dark input[type="password"],
|
||||
.skin-dark input[type="email"],
|
||||
.skin-dark input[type="tel"],
|
||||
.skin-dark input[type="search"],
|
||||
.skin-dark input[type="url"],
|
||||
.skin-dark input[type="number"],
|
||||
.skin-dark textarea {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
border-color: #454545;
|
||||
}
|
||||
|
||||
form p {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
form p input + button[type="reset"],
|
||||
form p textarea + button[type="reset"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
padding: 0;
|
||||
border: none;
|
||||
font-size: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
background: url(input_areas/images/clear.png) no-repeat 50% 50% / 2.4rem auto;
|
||||
}
|
||||
html[dir="ltr"] form p input + button[type="reset"],
|
||||
html[dir="ltr"] form p textarea + button[type="reset"] {
|
||||
right: -0.3rem;
|
||||
}
|
||||
html[dir="rtl"] form p input + button[type="reset"],
|
||||
html[dir="rtl"] form p textarea + button[type="reset"] {
|
||||
left: -0.3rem;
|
||||
}
|
||||
|
||||
.skin-dark p input + button[type="reset"],
|
||||
.skin-dark p textarea + button[type="reset"] {
|
||||
background-image: url(input_areas/images/clear_dark.png);
|
||||
}
|
||||
|
||||
/* To avoid colission with BB butons */
|
||||
li input + button[type="reset"]:after,
|
||||
li textarea + button[type="reset"]:after {
|
||||
background: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
html[dir="ltr"] form p input:focus { padding-right: 3rem; }
|
||||
html[dir="rtl"] form p input:focus { padding-left: 3rem; }
|
||||
|
||||
form p input:focus + button[type="reset"],
|
||||
form p textarea:focus + button[type="reset"] {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
/* Fieldset */
|
||||
fieldset {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin: 1.5rem 0 0;
|
||||
padding: 0;
|
||||
font-size: 2rem;
|
||||
line-height: 1em;
|
||||
background: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
fieldset legend,
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime"],
|
||||
input[type="datetime-local"] {
|
||||
border: 0.1rem solid #c7c7c7;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
font-style: italic;
|
||||
font-size: 1.6rem;
|
||||
margin: 0 0 1.5rem;
|
||||
padding: 0 1.5rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
height: 4rem;
|
||||
color: #333;
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
html[dir="ltr"] fieldset legend,
|
||||
html[dir="ltr"] input[type="date"],
|
||||
html[dir="ltr"] input[type="time"],
|
||||
html[dir="ltr"] input[type="datetime"],
|
||||
html[dir="ltr"] input[type="datetime-local"] {
|
||||
background-image: url(input_areas/images/dialog.svg);
|
||||
background-position: right 1rem bottom 1rem;
|
||||
}
|
||||
html[dir="rtl"] fieldset legend,
|
||||
html[dir="rtl"] input[type="date"],
|
||||
html[dir="rtl"] input[type="time"],
|
||||
html[dir="rtl"] input[type="datetime"],
|
||||
html[dir="rtl"] input[type="datetime-local"] {
|
||||
background-image: url(input_areas/images/dialog_rtl.svg);
|
||||
background-position: left 1rem bottom 1rem;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
margin: 0 0 1rem;
|
||||
padding: 1rem 1.5rem 0;
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
fieldset[disabled] {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.skin-dark fieldset legend,
|
||||
.skin-dark input[type="date"],
|
||||
.skin-dark input[type="time"],
|
||||
.skin-dark input[type="datetime"],
|
||||
.skin-dark input[type="datetime-local"] {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
border-color: #454545;
|
||||
}
|
||||
|
||||
fieldset[disabled] legend,
|
||||
input[type="date"][disabled],
|
||||
input[type="time"][disabled],
|
||||
input[type="datetime"][disabled],
|
||||
input[type="datetime-local"][disabled] {
|
||||
background-color: transparent;
|
||||
}
|
||||
html[dir="ltr"] fieldset[disabled] legend,
|
||||
html[dir="ltr"] input[type="date"][disabled],
|
||||
html[dir="ltr"] input[type="time"][disabled],
|
||||
html[dir="ltr"] input[type="datetime"][disabled],
|
||||
html[dir="ltr"] input[type="datetime-local"][disabled] {
|
||||
background-image: url(input_areas/images/dialog_disabled.svg);
|
||||
}
|
||||
html[dir="rtl"] fieldset[disabled] legend,
|
||||
html[dir="rtl"] input[type="date"][disabled],
|
||||
html[dir="rtl"] input[type="time"][disabled],
|
||||
html[dir="rtl"] input[type="datetime"][disabled],
|
||||
html[dir="rtl"] input[type="datetime-local"][disabled] {
|
||||
background-image: url(input_areas/images/dialog_disabled_rtl.svg);
|
||||
}
|
||||
|
||||
input[type="date"]:focus,
|
||||
input[type="time"]:focus,
|
||||
input[type="datetime"]:focus,
|
||||
input[type="datetime-local"]:focus {
|
||||
box-shadow: none;
|
||||
border-bottom-color: #c7c7c7;
|
||||
}
|
||||
|
||||
.skin-dark input[type="date"]:focus,
|
||||
.skin-dark input[type="time"]:focus,
|
||||
.skin-dark input[type="datetime"]:focus,
|
||||
.skin-dark input[type="datetime-local"]:focus {
|
||||
box-shadow: none;
|
||||
border-bottom-color: #454545;
|
||||
}
|
||||
|
||||
input[type="date"]:active,
|
||||
input[type="time"]:active,
|
||||
input[type="datetime"]:active,
|
||||
input[type="datetime-local"]:active {
|
||||
background-color: #b2f2ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.skin-dark input[type="date"]:active,
|
||||
.skin-dark input[type="time"]:active,
|
||||
.skin-dark input[type="datetime"]:active,
|
||||
.skin-dark input[type="datetime-local"]:active {
|
||||
background-color: #00aacc;
|
||||
background-image: url(input_areas/images/dialog_active.svg);
|
||||
}
|
||||
|
||||
/* fix for required inputs with wrong or empty value e.g. [type=email] */
|
||||
input:invalid,
|
||||
textarea:invalid,
|
||||
.skin-dark input:invalid,
|
||||
.skin-dark textarea:invalid {
|
||||
color: #b90000;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
.skin-dark input:focus,
|
||||
.skin-dark textarea:focus {
|
||||
box-shadow: inset 0 -0.1rem 0 #00caf2;
|
||||
border-bottom-color: #00caf2;
|
||||
}
|
||||
|
||||
input:invalid:focus,
|
||||
textarea:invalid:focus,
|
||||
.skin-dark input:invalid:focus,
|
||||
.skin-dark textarea:invalid:focus {
|
||||
box-shadow: inset 0 -0.1rem 0 #820000;
|
||||
border-bottom-color: #820000;
|
||||
}
|
||||
|
||||
input[type="range"]:focus,
|
||||
.skin-dark input[type="range"]:focus {
|
||||
box-shadow: none;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
/* Tidy (search/submit) */
|
||||
form[role="search"] {
|
||||
position: relative;
|
||||
height: 3.7rem;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
form[role="search"].skin-dark {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
form[role="search"] p {
|
||||
padding: 0 1.5rem;
|
||||
padding-inline-start: 3rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
form[role="search"] p input,
|
||||
form[role="search"] p textarea {
|
||||
height: 3.7rem;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
form[role="search"] p textarea {
|
||||
padding: 1rem 0 0;
|
||||
}
|
||||
|
||||
form[role="search"] p input::-moz-placeholder {
|
||||
background-image: url(input_areas/images/search.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 3rem;
|
||||
}
|
||||
html[dir="ltr"] form[role="search"] p input::-moz-placeholder {
|
||||
background-position: right -0.5rem center;
|
||||
}
|
||||
html[dir="rtl"] form[role="search"] p input::-moz-placeholder {
|
||||
background-position: left -0.5rem center;
|
||||
}
|
||||
|
||||
form[role="search"].skin-dark p input::-moz-placeholder {
|
||||
background-image: url(input_areas/images/search_dark.svg);
|
||||
}
|
||||
|
||||
form[role="search"] p input:focus::-moz-placeholder {
|
||||
background: none;
|
||||
}
|
||||
|
||||
form[role="search"] p input:invalid,
|
||||
form[role="search"] p textarea:invalid,
|
||||
form[role="search"] p input:focus,
|
||||
form[role="search"] p textarea:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
form[role="search"].skin-dark p input,
|
||||
form[role="search"].skin-dark p textarea {
|
||||
color: #fff;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
form[role="search"] button[type="submit"] {
|
||||
min-width: 6rem;
|
||||
height: 3.7rem;
|
||||
padding: 0 1.5rem;
|
||||
border: none;
|
||||
color: #00aac5;
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
line-height: 3.7rem;
|
||||
width: auto;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
background-image: none;
|
||||
background-color: unset;
|
||||
font-style: italic;
|
||||
overflow: visible;
|
||||
}
|
||||
html[dir="ltr"] form[role="search"] button[type="submit"] { float: right; }
|
||||
html[dir="rtl"] form[role="search"] button[type="submit"] { float: left; }
|
||||
|
||||
form[role="search"] button[type="submit"]:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
offset-inline-start: -0.1rem;
|
||||
top: 0.7rem;
|
||||
bottom: 0.7rem;
|
||||
width: 0.1rem;
|
||||
background: #c7c7c7;
|
||||
}
|
||||
|
||||
form[role="search"] button[type="submit"].icon {
|
||||
font-size: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 3rem;
|
||||
}
|
||||
|
||||
form[role="search"] p input + button[type="reset"]{
|
||||
height: 3.7rem;
|
||||
offset-inline-end: 0.5rem;
|
||||
}
|
||||
|
||||
form[role="search"].skin-dark button[type="submit"]:after {
|
||||
background-color: #575757;
|
||||
}
|
||||
|
||||
form[role="search"] button[type="submit"] + p > textarea {
|
||||
height: 3.7rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
form[role="search"] button[type="submit"] + p button {
|
||||
height: 3.7rem;
|
||||
}
|
||||
|
||||
form[role="search"].full button[type="submit"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form[role="search"] button.icon:active,
|
||||
form[role="search"] button[type="submit"]:active {
|
||||
background-color: #b2f2ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
form[role="search"].skin-dark button.icon:active,
|
||||
form[role="search"].skin-dark button[type="submit"]:active {
|
||||
background: #00aacc;
|
||||
}
|
||||
|
||||
form[role="search"] button[type="submit"][disabled] {
|
||||
color: #adadad;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
form button::-moz-focus-inner {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* .bb-editable - e.g email & SMS recipients */
|
||||
.bb-editable [contenteditable] {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
max-width: calc(100% - 1.4rem);
|
||||
overflow: hidden;
|
||||
padding: 0 1.5rem;
|
||||
margin: 0 0 0.6rem;
|
||||
line-height: 2.8rem;
|
||||
border-radius: 1.4rem;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 1.6rem;
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .bb-editable [contenteditable].icon {
|
||||
padding-right: 3.7rem;
|
||||
}
|
||||
html[dir="rtl"] .bb-editable [contenteditable].icon {
|
||||
padding-left: 3.7rem;
|
||||
}
|
||||
|
||||
.bb-editable [contenteditable="false"].invalid {
|
||||
background-color: #fedcdc;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
html[dir="ltr"] .bb-editable [contenteditable="false"].invalid { padding-left: 3.3rem; }
|
||||
html[dir="rtl"] .bb-editable [contenteditable="false"].invalid { padding-right: 3.3rem; }
|
||||
|
||||
.bb-editable [contenteditable="false"].invalid:before {
|
||||
content: "!";
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
display: block;
|
||||
width: 1.7rem;
|
||||
height: 1.6rem;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
line-height: 1.6rem;
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
text-indent: -0.1rem;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background-color: #b80404;
|
||||
}
|
||||
html[dir="ltr"] .bb-editable [contenteditable="false"].invalid:before { left: 0.8rem; }
|
||||
html[dir="rtl"] .bb-editable [contenteditable="false"].invalid:before { right: 0.8rem; }
|
||||
|
||||
.bb-editable [contenteditable="false"].invalid:hover:before,
|
||||
.bb-editable [contenteditable="false"].invalid:active:before {
|
||||
color: #b2f2ff;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.bb-editable [contenteditable]:hover,
|
||||
.bb-editable [contenteditable]:active,
|
||||
.bb-editable [contenteditable].invalid:hover,
|
||||
.bb-editable [contenteditable].invalid:active {
|
||||
color: #fff;
|
||||
background-color: #b2f2ff;
|
||||
}
|
||||
|
||||
.bb-editable [contenteditable].icon:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0.7rem;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 3rem;
|
||||
}
|
||||
html[dir="ltr"] .bb-editable [contenteditable].icon:after {
|
||||
right: 0;
|
||||
background-position: -0.7rem -3.8rem;
|
||||
}
|
||||
html[dir="rtl"] .bb-editable [contenteditable].icon:after {
|
||||
left: 0;
|
||||
background-position: 0.7rem -3.8rem;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .bb-editable [contenteditable].icon:hover:after,
|
||||
html[dir="ltr"] .bb-editable [contenteditable].icon:active:after {
|
||||
background-position: -0.7rem -0.8rem;
|
||||
}
|
||||
html[dir="rtl"] .bb-editable [contenteditable].icon:hover:after,
|
||||
html[dir="rtl"] .bb-editable [contenteditable].icon:active:after {
|
||||
background-position: 0.7rem -0.8rem;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .bb-editable [contenteditable].invalid.icon {
|
||||
padding-right: 3.7rem;
|
||||
}
|
||||
html[dir="rtl"] .bb-editable [contenteditable].invalid.icon {
|
||||
padding-left: 3.7rem;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .bb-editable [contenteditable].invalid.icon:after {
|
||||
background-position: -0.7rem -6.8rem;
|
||||
}
|
||||
html[dir="rtl"] .bb-editable [contenteditable].invalid.icon:after {
|
||||
background-position: 0.7rem -6.8rem;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .bb-editable [contenteditable].invalid.icon:hover:after,
|
||||
html[dir="ltr"] .bb-editable [contenteditable].invalid.icon:active:after {
|
||||
background-position: -0.7rem -0.8rem;
|
||||
}
|
||||
html[dir="rtl"] .bb-editable [contenteditable].invalid.icon:hover:after,
|
||||
html[dir="rtl"] .bb-editable [contenteditable].invalid.icon:active:after {
|
||||
background-position: 0.7rem -0.8rem;
|
||||
}
|
||||
|
||||
.bb-editable [contenteditable="true"],
|
||||
.bb-editable [contenteditable="true"]:hover,
|
||||
.skin-comms .bb-editable [contenteditable="true"]:hover {
|
||||
padding: 0 0.2rem;
|
||||
white-space: normal;
|
||||
font-size: 1.8rem;
|
||||
font-style: normal;
|
||||
color: #000;
|
||||
background: none repeat scroll 0 0 transparent;
|
||||
}
|
||||
|
||||
.skin-comms .bb-editable [contenteditable]:hover,
|
||||
.skin-comms .bb-editable [contenteditable]:active {
|
||||
color: #fff;
|
||||
background-color: #29bbb5;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9"><path fill="#00AAC5" enable-background="new" d="M9 0L9 9 0 9"/></svg>
|
||||
|
After Width: | Height: | Size: 148 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9"><path fill="#B2F2FF" d="M9 0v9h-9"/></svg>
|
||||
|
After Width: | Height: | Size: 121 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9"><path opacity=".5" fill="#A9A9A9" d="M9 0L9 9 0 9"/></svg>
|
||||
|
After Width: | Height: | Size: 137 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9"><path opacity=".5" fill="#A9A9A9" enable-background="new" d="M0 0L0 9 9 9"/></svg>
|
||||
|
After Width: | Height: | Size: 161 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9"><path fill="#00AAC5" enable-background="new" d="M0 0L0 9 9 9"/></svg>
|
||||
|
After Width: | Height: | Size: 148 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><path fill-rule="evenodd" clip-rule="evenodd" fill="#424242" d="M23.385 21.494c.266.266.057.903-.465 1.426s-1.16.729-1.426.465l-4.041-4.039c-1.01.656-2.21 1.047-3.505 1.047-3.561 0-6.448-2.885-6.448-6.446 0-3.56 2.887-6.446 6.448-6.446 3.562 0 6.449 2.887 6.449 6.446 0 1.297-.393 2.499-1.051 3.509l4.041 4.039h-.002zm-9.437-12.459c-2.712 0-4.914 2.198-4.914 4.912s2.202 4.913 4.914 4.913c2.714 0 4.914-2.199 4.914-4.913-.001-2.714-2.2-4.912-4.914-4.912z"/></svg>
|
||||
|
After Width: | Height: | Size: 546 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><path fill-rule="evenodd" clip-rule="evenodd" fill="#8F9091" d="M23.385 21.494c.266.266.057.903-.465 1.426s-1.16.729-1.426.465l-4.041-4.039c-1.01.656-2.21 1.047-3.505 1.047-3.561 0-6.448-2.885-6.448-6.446 0-3.56 2.887-6.446 6.448-6.446 3.562 0 6.449 2.887 6.449 6.446 0 1.297-.393 2.499-1.051 3.509l4.041 4.039h-.002zm-9.437-12.459c-2.712 0-4.914 2.198-4.914 4.912s2.202 4.913 4.914 4.913c2.714 0 4.914-2.199 4.914-4.913-.001-2.714-2.2-4.912-4.914-4.912z"/></svg>
|
||||
|
After Width: | Height: | Size: 546 B |