Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View 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);
});

View file

@ -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);
});

View 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'
}
});
};
}

View 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));

View file

@ -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();
}
}
}
}

View file

@ -0,0 +1,11 @@
var dial = document.querySelector('#dial');
if (dial) {
dial.onclick = function() {
var call = new MozActivity({
name: 'dial',
data: {
number: '+46777888999'
}
});
};
}

View 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));

View 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);

View 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);
}
}
);

View 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);

View 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);

View 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);

View file

@ -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)';
};
};

View file

@ -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.'); };
};

View 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);
};

View 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);
};

View 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);
}
});

View 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();

View 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/*');
};

View 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); };

View 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']);
};

View 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/*'); };

View file

@ -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);
}
});
}
});

File diff suppressed because one or more lines are too long

View 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
}
});
};
}

View 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());

View file

@ -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))
};
},
}
};

View 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'
}
});
};
}

View 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");
}
}
}

View 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'
}
});
};
}

View 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));
};
};