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,24 @@
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<title>Provider 1</title>
<script defer src="/js/google.js"></script>
<script defer src="/js/app.js"></script>
</head>
<body>
<section>
<header>
<h1>Google contacts provider</h1>
</header>
<article>
This test app will allow you to log in into your google account and store your contacts into an 'contacts' datastore.
</article>
</section>
<section id="info"></section>
<section id="commands">
<button id="fillDS">Fill DataStore</button>
<button id="resetDS">Clean DataStore</button>
</section>
</body>
</html>

View file

@ -0,0 +1,87 @@
/* global Google */
'use strict';
var App = function App() {
var store = null;
var info = null;
var fillButton, resetButton;
var DS_NAME = 'contacts';
var init = function init() {
info = document.getElementById('info');
fillButton = document.getElementById('fillDS');
resetButton = document.getElementById('resetDS');
fillButton.addEventListener('click', handleEvent);
resetButton.addEventListener('click', handleEvent);
initDS();
};
function handleEvent(evt) {
var btn = evt.target.id;
switch (btn) {
case 'fillDS':
fillButton.disabled = true;
Google.initLogin(store, function(t) {
Google.importContacts(function() {
fillButton.disabled = false;
store.getLength().then(function(count) {
info.textContent = count + ' elements';
});
});
});
break;
case 'resetDS':
store.clear().then(function() {
store.getLength().then(function(count) {
info.textContent = count + ' elements';
});
});
break;
default:
break;
}
}
function initDS() {
function storeError() {
info.textContent = 'Error getting store';
}
if (!navigator.getDataStores) {
info.textContent = 'NO DataStore API!';
return;
}
navigator.getDataStores(DS_NAME).then(function(ds) {
if (!ds || ds.length < 1) {
storeError();
return;
}
ds.forEach(function onDs(datastore) {
if (datastore.owner.indexOf('provider1')) {
store = datastore;
console.log('Got store ' + store.owner);
}
});
store.getLength().then(function(count) {
info.textContent = count + ' elements';
});
}, function() {
storeError();
});
}
return {
init: init
};
}();
App.init();

View file

@ -0,0 +1,343 @@
'use strict';
var googleAuth = 'https://accounts.google.com/o/oauth2/auth?scope=' +
'https://www.google.com/m8/feeds/&response_type=token&redirect_uri=' +
'http://postmessageitor.eu01.aws.af.cm/&approval_prompt=force&' +
'client_id=206115911344.apps.googleusercontent.com';
var Google = function Google() {
var authWindow;
var accessToken;
var datastore;
var initLogin = function initLogin(ds, cb) {
accessToken = localStorage.accessToken;
datastore = ds;
if (accessToken) {
cb(accessToken);
return;
}
authWindow = window.open(googleAuth);
window.addEventListener('message', function(evt) {
authWindow.close();
accessToken = evt.data.access_token;
localStorage.accessToken = accessToken;
if (typeof cb === 'function') {
cb(accessToken);
}
});
};
var getContacts = function getContacts(cb) {
var parser = ContactsParser(accessToken, function() {
var contacts = parser.getContacts();
cb(contacts);
});
parser.fetchContacts();
};
var importContacts = function importContacts(cb) {
this.getContacts(function(contacts) {
doImportContacts(contacts, cb);
});
};
function doImportContacts(contacts, cb) {
if (!contacts || contacts.length === 0) {
if (typeof cb === 'function') {
cb();
}
return;
}
var contact = prepareForDS(contacts[0]);
function continuee() {
var rest = contacts.slice(1);
if (rest && rest.length > -1) {
doImportContacts(rest, cb);
}
}
datastore.add(contact).then(function(id) {
console.add('Added contact with id ' + id);
continuee();
}, function() {
console.add('Error saving contact ');
continuee();
});
}
function prepareForDS(contact) {
// Initialy dont do any extra processing
return contact;
}
return {
initLogin: initLogin,
getContacts: getContacts,
importContacts: importContacts
};
}();
var ContactsParser = function contacts(t, cb) {
var token = t;
var contacts = [];
var contactsToImport = [];
var GOOGLE_URL = 'https://www.google.com/m8/feeds/contacts/default/full?max-results=10000';
var GROUPS_END_POINT = 'https://www.google.com/m8/feeds/groups/default/full/';
var GD_NAMESPACE = 'http://schemas.google.com/g/2005';
var CATEGORY = 'gmail';
var URN_IDENTIFIER = 'urn:service:gmail:uid:';
var fetchContacts = function fetchContact() {
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
xhr.open('GET', GROUPS_END_POINT, true);
xhr.setRequestHeader('Authorization', 'OAuth ' + token);
xhr.setRequestHeader('Gdata-version', '3.0');
xhr.addEventListener('load', function dataLoaded(e) {
if (xhr.status == 200 || xhr.status == 0) {
var feed = xhr.responseXML.getElementsByTagName('feed')[0];
if (feed === null) {
getContactsByGroup();
return;
}
var sgc = feed.querySelector('systemGroup[id="Contacts"]');
if (sgc !== null) {
var id = sgc.parentNode.querySelector('id').textContent;
getContactsByGroup(id);
} else {
getContactsByGroup();
}
} else {
getContactsByGroup();
}
});
xhr.send(null);
};
var getContactsByGroup = function getContactsByGroup(groupId) {
var url = GOOGLE_URL;
if (groupId) {
url += '&group=' + groupId;
}
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
xhr.open('GET', url, true);
xhr.setRequestHeader('Authorization', 'OAuth ' + token);
xhr.setRequestHeader('Gdata-version', '3.0');
xhr.addEventListener('load', function dataLoaded(e) {
if (xhr.status == 200 || xhr.status == 0) {
parseContacts(xhr.responseXML);
cb(contacts);
} else {
// TODO : Handle error
console.error('----->> Pete');
delete localStorage.accessToken;
}
});
xhr.send(null);
};
var parseContacts = function parseContacts(responseXML) {
contacts = [];
var contactsEntries = responseXML.querySelectorAll('entry');
for (var i = 0; i < contactsEntries.length; i++) {
var entry = contactsEntries[i];
contacts.push(gContactToJson(entry));
}
};
var getValueForNode = function getValueForNode(doc, name, def) {
var defaultValue = def || '';
if (doc == null || name == null) {
return defaultValue;
}
var node = doc.querySelector(name);
if (node && node.textContent) {
return node.textContent;
}
return defaultValue;
};
var gContactToJson = function gContactToJson(googleContact) {
var output = {};
// This field will be needed for indexing within the
// import process, not for the api
output.uid = getUid(googleContact);
output.name = getValueForNode(googleContact, 'title');
// Store the photo url, not in the contact itself
var photoUrl = googleContact.querySelector('link[type="image/*"]');
if (photoUrl) {
photoUrl = photoUrl.getAttribute('href');
} else {
// No image link
photoUrl = '';
}
output.photoUrl = photoUrl;
var name = googleContact.querySelector('name');
if (name) {
var contactName = getValueForNode(name, 'givenName');
if (contactName) {
output.givenName = [contactName];
}
var contactFamilyName = getValueForNode(name, 'familyName');
if (contactFamilyName) {
output.familyName = [contactFamilyName];
}
var contactSuffix = getValueForNode(name, 'additionalName');
if (contactSuffix) {
output.additionalName = [contactSuffix];
}
}
output.email = parseEmails(googleContact);
output.adr = parseAddress(googleContact);
output.tel = parsePhones(googleContact);
var org = googleContact.querySelector('organization');
if (org) {
output.org = [getValueForNode(org, 'orgName')];
output.jobTitle = [getValueForNode(org, 'orgTitle')];
}
var bday = googleContact.querySelector('birthday');
if (bday) {
output.bday = new Date(bday.getAttribute('when'));
}
var content = googleContact.querySelector('content');
if (content) {
output.note = [content.textContent];
}
output.category = [CATEGORY];
output.url = [{
type: ['source'],
value: getContactURI(output)
}];
return output;
};
var getContactURI = function getContactURI(contact) {
return URN_IDENTIFIER + contact.uid;
};
// This will be a full url like:
// http://www.google.com/m8/feeds/contacts/<email>/base/<contact_id>
// for a specific contact node
var getUid = function getUid(contact) {
return contact.querySelector('id').textContent;
};
// Returns an array with the possible emails found in a contact
// as a ContactField format
var parseEmails = function parseEmails(googleContact) {
var DEFAULT_EMAIL_TYPE = 'personal';
var emails = [];
var fields = googleContact.getElementsByTagNameNS(GD_NAMESPACE,
'email');
if (fields && fields.length > 0) {
for (var i = 0; i < fields.length; i++) {
var emailField = fields.item(i);
// Type format: rel="http://schemas.google.com/g/2005#home"
var type = emailField.getAttribute('rel') || DEFAULT_EMAIL_TYPE;
if (type.indexOf('#') > -1) {
type = type.substr(type.indexOf('#') + 1);
}
emails.push({
'type': type,
'value': emailField.getAttribute('address')
});
}
}
return emails;
};
// Given a google contact returns an array of ContactAddress
var parseAddress = function parseAddress(googleContact) {
var addresses = [];
var fields = googleContact.getElementsByTagNameNS(GD_NAMESPACE,
'structuredPostalAddress');
if (fields && fields.length > 0) {
for (var i = 0; i < fields.length; i++) {
var field = fields.item(i);
var address = {};
address.streetAddress = getValueForNode(field, 'street');
address.locality = getValueForNode(field, 'city');
address.region = getValueForNode(field, 'region');
address.postalCode = getValueForNode(field, 'postcode');
address.countryName = getValueForNode(field, 'country');
addresses.push(address);
}
}
return addresses;
};
// Given a google contact this function returns an array of
// ContactField with the pones stored for that contact
var parsePhones = function parsePhones(googleContact) {
var DEFAULT_PHONE_TYPE = 'personal';
var phones = [];
var fields = googleContact.getElementsByTagNameNS(GD_NAMESPACE,
'phoneNumber');
if (fields && fields.length > 0) {
for (var i = 0; i < fields.length; i++) {
var field = fields.item(i);
// Type format: rel="http://schemas.google.com/g/2005#home"
var type = field.getAttribute('rel') || DEFAULT_PHONE_TYPE;
if (type.indexOf('#') > -1) {
type = type.substr(type.indexOf('#') + 1);
}
phones.push({
'type': type,
'value': field.textContent
});
}
}
return phones;
};
// All contacts
var getContacts = function getContacts() {
return contacts;
};
var cancelImport = function cancelImport() {
};
return {
'parseContacts': parseContacts,
'fetchContacts': fetchContacts,
'getContacts': getContacts,
'cancelImport': cancelImport
};
};

View file

@ -0,0 +1,36 @@
'use strict';
var Redirect = function Redirect() {
var ORIGIN = document.location.protocol + '//' +
document.location.host;
var init = function init() {
var hash = document.location.hash.substring(1);
var parameters = {};
var dataStart = hash.indexOf('access_token');
if (dataStart !== -1) {
var elements = hash.split('&');
elements.forEach(function(p) {
var values = p.split('=');
parameters[values[0]] = values[1];
});
console.log('-----> Post message:');
console.log(JSON.stringify(parameters));
window.opener.postMessage(parameters, ORIGIN);
window.close();
}
};
return {
'init': init
};
}();
Redirect.init();

View file

@ -0,0 +1,27 @@
{
"name": "Google Contacts DS",
"type": "certified",
"launch_path": "/index.html",
"datastores-owned": {
"contacts": {
"access": "readwrite",
"description": "Provider 1 contacts"
}
},
"redirects": [
{"from": "http://postmessageitor.eu01.aws.af.cm/",
"to": "/redirect.html"}
],
"permissions": {
"systemXHR": {
"description": "This permission is require to contact Google serves"
}
},
"icons": {
"84": "/style/icons/default_84.png",
"126": "/style/icons/default_126.png",
"142": "/style/icons/default_142.png",
"189": "/style/icons/default_189.png",
"284": "/style/icons/default_284.png"
}
}

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OAuth Redirection</title>
<script defer src="js/redirect.js"></script>
</head>
<body>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,24 @@
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<title>Provider 2</title>
<script defer src="/js/linkedin.js"></script>
<script defer src="/js/app.js"></script>
</head>
<body>
<section>
<header>
<h1>Linked In contacts provider</h1>
</header>
<article>
This test app will allow you to log in into your linked in account and store your contacts into an 'contacts' datastore.
</article>
</section>
<section id="info"></section>
<section id="commands">
<button id="fillDS">Fill DataStore</button>
<button id="resetDS">Clean DataStore</button>
</section>
</body>
</html>

View file

@ -0,0 +1,85 @@
'use strict';
var App = function App() {
var store = null;
var info = null;
var fillButton, resetButton;
var DS_NAME = 'contacts';
var init = function init() {
info = document.getElementById('info');
fillButton = document.getElementById('fillDS');
resetButton = document.getElementById('resetDS');
fillButton.addEventListener('click', handleEvent);
resetButton.addEventListener('click', handleEvent);
initDS();
};
function handleEvent(evt) {
var btn = evt.target.id;
switch (btn) {
case 'fillDS':
fillButton.disabled = true;
LinkedIn.initLogin(store, function() {
LinkedIn.importContacts(function() {
store.getLength().then(function(count) {
info.textContent = count + ' elements';
});
fillButton.disabled = false;
});
});
break;
case 'resetDS':
store.clear().then(function() {
store.getLength().then(function(count) {
info.textContent = count + ' elements';
});
});
break;
default:
break;
}
}
function initDS() {
function storeError() {
info.textContent = 'Error getting store';
}
if (!navigator.getDataStores) {
info.textContent = 'NO DataStore API!';
return;
}
navigator.getDataStores(DS_NAME).then(function(ds) {
if (!ds || ds.length < 1) {
storeError();
return;
}
ds.forEach(function onDs(datastore) {
if (datastore.owner.indexOf('provider2')) {
store = datastore;
console.log('Got store ' + store.owner);
}
});
store.getLength().then(function(count) {
info.textContent = count + ' elements';
});
}, function() {
storeError();
});
}
return {
init: init
};
}();
App.init();

View file

@ -0,0 +1,177 @@
'use strict';
var accessURI = 'https://www.linkedin.com/uas/oauth2/authorization?response_type=code' +
'&client_id=773tsorjqd8pqw' +
'&scope=r_fullprofile%20r_emailaddress%20r_network' +
'&state=' + (new Date()) +
'&redirect_uri=http://127.0.0.1/li';
var LinkedIn = function LinkedIn() {
var accessToken = null;
var datastore = null;
var initLogin = function(ds, cb) {
datastore = ds;
accessToken = localStorage.accessToken;
if (accessToken) {
cb(accessToken);
return;
}
var authWindow = window.open(accessURI);
window.addEventListener('message', function(evt) {
authWindow.close();
var code = evt.data.code;
exchangeAuthCode(code, cb);
});
};
var getContacts = function getContacts(cb) {
getLinkedinPeople(cb);
};
var importContacts = function importContacts(cb) {
getContacts(function(contacts) {
// Save contacts to local ds
doImportContacts(contacts, cb);
});
};
function doImportContacts(contacts, cb) {
if (!contacts || contacts.length === 0) {
if (typeof cb === 'function') {
cb();
}
return;
}
var contact = prepareForDS(contacts[0]);
function continuee() {
var rest = contacts.slice(1);
if (rest && rest.length > -1) {
doImportContacts(rest, cb);
}
}
datastore.add(contact).then(function(id) {
console.add('Added contact with id ' + id);
continuee();
}, function() {
console.add('Error saving contact ');
continuee();
});
}
function prepareForDS(contact) {
// Initialy dont do any extra processing
return contact;
}
function getLinkedinPeople(cb) {
var url = 'https://api.linkedin.com/v1/people/~/connections?oauth2_access_token=' + accessToken;
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
xhr.open('GET', url, true);
xhr.addEventListener('load', function dataLoaded(e) {
if (xhr.status === 200 || xhr.status === 0) {
parseContacts(xhr.responseXML, cb);
}
});
xhr.send();
}
function parseContacts(xml, cb) {
var contactsElements = xml.querySelectorAll('person');
contactsElements = Array.prototype.slice.call(contactsElements);
parseIterative(contactsElements, [], cb);
}
function parseIterative(unparsedList, parsedList, cb) {
if (!unparsedList || unparsedList.length === 0) {
cb(parsedList);
return;
}
var unparsed = unparsedList[0];
var rest = unparsedList.slice(1);
parseContact(unparsed, function(contact) {
parsedList.push(contact);
parseIterative(rest, parsedList, cb);
});
}
function parseContact(raw, cb) {
var contact = {};
contact.uid = raw.querySelector('id').textContent;
contact.givenName = [];
contact.givenName[0] = raw.querySelector('first-name') ?
raw.querySelector('first-name').textContent || '' : '';
contact.familyName = [];
contact.familyName[0] = raw.querySelector('last-name') ?
raw.querySelector('last-name').textContent || '' : '';
contact.name = [contact.givenName[0] + ' ' + contact.familyName[0]];
contact.org = [];
contact.org[0] = raw.querySelector('headline') ?
raw.querySelector('headline').textContent || '' : '';
// Parse photo here
var photoUrl = raw.querySelector('picture-url');
if (photoUrl) {
photoUrl = photoUrl.textContent;
var xhr = new XMLHttpRequest({mozSystem: true});
xhr.open('GET', photoUrl, true);
xhr.responseType = 'blob';
xhr.addEventListener('load', function onImageLoaded(e) {
if (xhr.status === 0 || xhr.status === 200) {
contact.photo = [xhr.response];
}
cb(contact);
});
xhr.send();
} else {
cb(contact);
}
}
function exchangeAuthCode(code, cb) {
var url = 'https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code' +
'&code=' + code +
'&redirect_uri=http://127.0.0.1/li' +
'&client_id=773tsorjqd8pqw' +
'&client_secret=oX6W3js86FIfNtZH';
var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
xhr.open('POST', url, true);
xhr.addEventListener('load', function dataLoaded(e) {
if (xhr.status === 200 || xhr.status === 0) {
var response = xhr.response;
if (typeof response === 'string') {
response = JSON.parse(response);
}
accessToken = response.access_token;
localStorage.accessToken = accessToken;
cb(accessToken);
} else {
console.error('Could not get an access token from url ' + url);
}
});
xhr.send();
}
return {
initLogin: initLogin,
getContacts: getContacts,
importContacts: importContacts
};
}();

View file

@ -0,0 +1,36 @@
'use strict';
var Redirect = function Redirect() {
var ORIGIN = document.location.protocol + '//' +
document.location.host;
var init = function init() {
var parameters = {};
var dataStart = document.location.href.indexOf('?');
var params = document.location.href.substring(dataStart + 1);
if (dataStart !== -1) {
var elements = params.split('&');
elements.forEach(function(p) {
var values = p.split('=');
parameters[values[0]] = values[1];
});
console.log('-----> Post message:');
console.log(JSON.stringify(parameters));
window.opener.postMessage(parameters, ORIGIN);
window.close();
}
};
return {
'init': init
};
}();
Redirect.init();

View file

@ -0,0 +1,27 @@
{
"name": "LI Contacts DS",
"type": "certified",
"launch_path": "/index.html",
"datastores-owned": {
"contacts": {
"access": "readwrite",
"description": "Provider 2 contacts"
}
},
"redirects": [
{"from": "http://127.0.0.1/li",
"to": "/redirect.html"}
],
"permissions": {
"systemXHR": {
"description": "This permission is require to contact Linked In serves"
}
},
"icons": {
"84": "/style/icons/default_84.png",
"126": "/style/icons/default_126.png",
"142": "/style/icons/default_142.png",
"189": "/style/icons/default_189.png",
"284": "/style/icons/default_284.png"
}
}

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OAuth Redirection</title>
<script defer src="js/redirect.js"></script>
</head>
<body>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,27 @@
Contacts Manager
================
History
-------
Traditionally we have been using and abusing mozContacts[1], each certified app that uses contacts has been putting their requirements (indexes, search options, etc.) directly on the API, making it a bit unmaintable.
Also this API has been designed as a mapping with the vcard format, which is great to define a single contact but lacks of all the fields necessary to track modern requirements (contacts merging, data visibility, searching, etc.)
Despite of that in the certified Contacts application we have been adding more and more functionality to try to fix this (the merging is added, different sources of contacts are available just as input), but the requirements continue growing and we move the problem from the API to the app.
Future
------
In a no so distant future, now with current technologies like Datastore, that enable us to share information across domains we can bypass the mozContacts restrictions.
We want to create an app that aggregates and manipulates contacts data sources, not just mozContacts, but also any other possible provider of contacts. As well as consumers will use this data and create local copies with simplified, optimized and specific view of that data per app.
Key points
----------
- Remember a contact is not just an entry: it could be a merged contact, we do have logic to pick information and prioritizing the view of a contact from different entries.
- Datasources should be pluggable: removing a contacts data source would clean all the contacts coming from it, in case of merged contacts being able to unmerge the changes.
- Security and access: By contract some contacts data sources wont be available to everyone or the user may decide to not to grant access from and to different apps.
- Each app will need to have their specific view: an example of this, contacts needs to make the contact list superfast, contacts app should create a storage from this global one with the requirements needed by contacts app.
Links
-----
[1] https://wiki.mozilla.org/WebAPI/ContactsAPI

View file

@ -0,0 +1,36 @@
{
"name": "ContactsManager",
"description": "Contacts Provider App",
"version": "1.5",
"type": "certified",
"role": "system",
"developer": {
"name": "Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"permissions": {
"contacts":{ "access": "readwrite" },
"alarms": {},
"systemXHR": {},
"phonenumberservice": {}
},
"locales": {
"en-US": {
"name": "Contacts Provider",
"description": "Contacts Provider App"
}
},
"default_locale": "en-US",
"messages": [
{ "alarm": "/periodic_sync.html" }
],
"connections": {
"contacts-sync": {
"description": "Contacts DataBase synchronization",
"rules": {
"manifestURLs": ["*"]
},
"handler_path": "/on_demand_sync.html"
}
}
}

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<title> Contacts Database On Demand Sync </title>
<script defer src="/shared/js/simple_phone_matcher.js"></script>
<script defer src="/shared/js/fb/fb_tel_index.js"></script>
<script defer src="/shared/js/async_storage.js"></script>
<script defer src="js/gcdsops.js"></script>
<script defer src="js/sync.js"></script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<title> Contacts Database Periodic Sync </title>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<title>Device Storage Test</title>
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css">
<link rel="stylesheet" type="text/css" href="shared/style/headers.css">
<link rel="stylesheet" type="text/css" href="style/ds-test.css">
<script src="js/ds-test.js"></script>
</head>
<body role="application">
<section role="region">
<header>
<h1 data-l10n-id="dstest-title">Device Storage Test</h1>
</header>
</section>
<section role="region">
<table>
<tr>
<td align="right">Command:</td>
<td>
<select class="fake-select" id="ds.cmd" name="ds.cmd">
<option value="getDeviceStorage">GetDeviceStorage</option>
<option value="getDeviceStorages">GetDeviceStorages</option>
<option value="free-space">Free Space</option>
<option value="used-space">Used Space</option>
<option value="available">Available</option>
<option value="enumerate">Enumerate</option>
<option value="enumerate-dcim">Enumerate DCIM</option>
<option value="enumerate-filesizes">Enum File Sizes</option>
<option value="enumerate-all">Enumerate ALL</option>
<option value="add-unnamed-file">Add & Del Unnamed File</option>
<option value="add-file">Add File</option>
<option value="del-file">Del File</option>
<option value="mod-file">Mod File</option>
<option value="show-file">Show File</option>
<option value="add-listener">Add Listener</option>
<option value="remove-listener">Remove Listener</option>
<option value="test" selected>Test</option>
</select>
</td>
</tr>
<tr>
<td align="right">Storage Type:</td>
<td>
<select class="fake-select" id="ds.type" name="ds.type">
<option value="sdcard" selected>sdcard</option>
<option value="pictures">pictures</option>
<option value="music">music</option>
<option value="videos">videos</option>
<option value="apps">apps</option>
</select>
</td>
</tr>
</table>
<br>
</section>
<section role="region">
<div class="bb-docs">
<button id="doit">Do It</button>
<button id="clear-log">Clear Log</button>
</div>
</section
<section role="region">
<header>
<h1 data-l10n-id="dstest-debug-log" id="h1-debuglog" data-hide="debuglog">Debug Log</h1>
</header>
<div class="bb-docs" id="debuglog">
<span id="log"></span>
</div>
</section>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,38 @@
{
"name": "Device Storage Test",
"launch_path": "/index.html",
"description": "Test app for Device Storage",
"type": "certified",
"fullscreen": "false",
"developer": {
"name": "dhylands",
"url": "https://github.com/dhylands/ds-test"
},
"permissions": {
"systemXHR":{},
"device-storage:apps":{ "access": "readwrite" },
"device-storage:sdcard":{ "access": "readwrite" },
"device-storage:music":{ "access": "readwrite" },
"device-storage:pictures":{ "access": "readwrite" },
"device-storage:videos":{ "access": "readwrite" },
"settings":{ "access": "readonly" },
"webapps-manage":{}
},
"activities": {
"share": {
"filters": {
"type": "image/*"
},
"href": "/index.html",
"returnValue": true,
"disposition": "window"
}
},
"icons": {
"84": "/style/icons/default_84.png",
"126": "/style/icons/default_126.png",
"142": "/style/icons/default_142.png",
"189": "/style/icons/default_189.png",
"284": "/style/icons/default_284.png"
}
}

View file

@ -0,0 +1,22 @@
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-size: 10px;
font-family: sans-serif;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
td {
font-size: 18px;
}
select {
font-size: 18px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

107
gaia/dev_apps/geoloc/index.html Executable file
View file

@ -0,0 +1,107 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8" />
<title>Geolocation Debug</title>
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css" />
<link rel="stylesheet" type="text/css" href="shared/style/headers.css" />
<link rel="stylesheet" type="text/css" href="shared/style/input_areas.css" />
<link rel="stylesheet" type="text/css" href="shared/elements/gaia-theme/gaia-theme.css /">
<link rel="stylesheet" type="text/css" href="style/geoloc.css" />
<script type="application/javascript" src="js/geoloc.js"></script>
<!-- localization -->
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="en-US">
<link rel="localization" href="locales/geoloc.{locale}.properties">
<link rel="localization" href="/shared/locales/date/date.{locale}.properties">
<script src="shared/js/l10n.js" type="text/javascript" charset="utf-8"></script>
<script defer src="shared/js/component_utils.js"></script>
<script defer src="shared/elements/gaia_switch/script.js"></script>
</head>
<body role="application">
<section role="region">
<header>
<h1 data-l10n-id="geolocation-tracker">Geolocation tracker</h1>
</header>
</section>
<section role="region">
<div class="bb-docs">
<button id="btnGet" data-l10n-id="geoloc-get">Get</button>
<button id="btnStart" data-l10n-id="geoloc-start">Start</button>
<button id="btnStop" data-l10n-id="geoloc-stop">Stop</button>
<button id="btnClear" data-l10n-id="geoloc-clear">Clear</button>
<button id="btnSave" data-l10n-id="geoloc-save">Save as a file</button>
</div>
</section>
<section role="region">
<header>
<h1 data-l10n-id="geoloc-parameters" id="h1-parameters" data-hide="parameters" class="bb-docs">Parameters</h1>
</header>
<div class="bb-docs" id="parameters">
<fieldset>
<legend data-l10n-id="geoloc-timeout">Timeout</legend>
<section>
<input type="number" id="geoloc-timeout" value="0" />
</section>
</fieldset>
<fieldset>
<legend data-l10n-id="geoloc-maxage">Max. age</legend>
<section>
<input type="number" id="geoloc-maxage" value="0" />
</section>
</fieldset>
<span class="desc" data-l10n-id="geoloc-msecs">Timeout and Max. age are specified in miliseconds.</span>
<ul>
<li>
<gaia-switch id="geoloc-highaccuracy" checked>
<label data-l10n-id="geoloc-highaccuracy">High accuracy</label>
</gaia-switch>
</li>
<li>
<gaia-switch id="geoloc-debug" checked>
<label data-l10n-id="geoloc-debug">Show debug traces</label>
</gaia-switch>
</li>
<li>
<gaia-switch id="geoloc-keep" checked>
<label data-l10n-id="geoloc-keep">Keep locations</label>
</gaia-switch>
</li>
</ul>
</div>
</section>
<section role="region">
<header>
<h1 data-l10n-id="geoloc-status" id="h1-status" data-hide="status">Status</h1>
</header>
<div class="bb-docs" id="status">
<fieldset>
<legend data-l10n-id="gps-fixes">Fixes</legend>
<section>
<input type="text" disabled="disabled" id="nbr-gps-fixes" placeholder="0" />
</section>
</fieldset>
<fieldset>
<legend data-l10n-id="last-fix">Last</legend>
<section>
<input type="text" disabled="disabled" data-l10n-id="date-last-fix" id="date-last-fix" placeholder="None" />
</section>
</fieldset>
</div>
</section>
<section role="region">
<header>
<h1 data-l10n-id="geoloc-debug-log" id="h1-debuglog" data-hide="debuglog">Debug log</h1>
</header>
<div class="bb-docs" id="debuglog">
<span id="log"></span>
</div>
</section>
</body>
</html>

325
gaia/dev_apps/geoloc/js/geoloc.js Executable file
View file

@ -0,0 +1,325 @@
var running = false;
var watchId = undefined;
var sep = "<br />************<br />";
var _ = undefined;
var locations = undefined;
function successWatch(position) {
var debug = document.getElementById('geoloc-debug').checked;
var s = document.querySelector('#log');
if (!debug) {
s.innerHTML = "";
}
if (keep()) {
locations.push(position);
}
s.innerHTML = sep + s.innerHTML;
for (var i in position.coords) {
var e = position.coords[i];
if (typeof e == 'number') {
s.innerHTML = i + ":" + position.coords[i] + "<br />" + s.innerHTML;
}
}
var link = '<a href="http://www.openstreetmap.org/?mlat=' + position.coords.latitude + '&mlon=' + position.coords.longitude + '&zoom=16">Show on map</a><br />';
s.innerHTML = link + s.innerHTML;
var nbfixes = document.getElementById('nbr-gps-fixes');
nbfixes.value = parseInt(nbfixes.value) + 1;
var datefix = document.getElementById('date-last-fix');
var cdate = new Date();
var localeFormat = cdate.toLocaleString(navigator.languages, {
month: 'numeric',
day: 'numeric',
year: 'numeric'
});
datefix.value = localeFormat;
}
function errorWatch(msg) {
var s = document.querySelector('#log');
if (typeof msg == "object") {
if (msg.code == 1) {
msg = "Permission denied";
}
if (msg.code == 2) {
msg = "Position unavailable";
}
if (msg.code == 3) {
msg = "Timed out";
}
}
s.innerHTML = sep + msg + sep + s.innerHTML;
}
function successGet(position) {
successWatch(position);
enable('btnGet');
enable('btnStart');
enable('btnClear');
enable('btnSave');
}
function errorGet(msg) {
errorWatch(msg);
enable('btnGet');
enable('btnStart');
enable('btnClear');
}
function getOptions() {
if (navigator.geolocation) {
locations = new Array();
document.getElementById('nbr-gps-fixes').value = "0";
document.getElementById('date-last-fix').value = "";
show('status');
if (document.getElementById('geoloc-debug').checked) {
show('debuglog');
}
running = true;
return {
enableHighAccuracy: document.getElementById('geoloc-highaccuracy').checked ? true : false,
timeout: parseInt(document.getElementById('geoloc-timeout').value),
maximumAge: parseInt(document.getElementById('geoloc-maxage').value)
};
}
errorWatch('not supported');
return null;
}
function getGeoloc() {
errorWatch("getting current position");
options = getOptions();
if (options) {
navigator.geolocation.getCurrentPosition(successGet, errorGet, options);
disable('btnGet');
disable('btnStart');
disable('btnStop');
disable('btnClear');
disable('btnSave');
}
}
function startGeoloc() {
errorWatch("Starting watchPosition");
options = getOptions();
if (options) {
watchId = navigator.geolocation.watchPosition(successWatch, errorWatch, options);
enable('btnStop');
enable('btnClear');
disable('btnGet');
disable('btnStart');
disable('btnSave');
}
}
function stopGeoloc() {
if (watchId != undefined) {
errorWatch("Stopping watchPosition");
navigator.geolocation.clearWatch(watchId);
disable('btnStop');
enable('btnGet');
enable('btnClear');
enable('btnStart');
if (keep()) {
enable('btnSave');
}
running = false;
}
}
function clearGeoloc() {
var s = document.querySelector('#log');
s.innerHTML = "";
}
function locationsToGPX() {
var xmldoc = document.implementation.createDocument("", "", null);
var doc = xmldoc.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx');
doc.setAttribute('creator', 'FirefoxOS geotracker');
doc.setAttribute('version', '1.1');
xmldoc.appendChild(doc);
var time = xmldoc.createElement('time');
var dtf = Intl.DateTimeFormat(navigator.languages, {
year: 'numeric',
moth: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZoneName: 'short'
});
var ts = xmldoc.createTextNode(dtf.format(new Date()));
time.appendChild(ts);
doc.appendChild(time);
var createSimpleNode = function(name, value) {
var e = xmldoc.createElement(name);
e.appendChild(xmldoc.createTextNode(value));
return e;
};
for (var ei in locations) {
var e = locations[ei];
var x = xmldoc.createElement('wpt');
x.setAttribute('lat', e.coords.latitude);
x.setAttribute('lon', e.coords.longitude);
var ele = createSimpleNode('ele', e.coords.altitude);
x.appendChild(ele);
var time = createSimpleNode('time', dtf.format(new Date(e.timestamp)));
x.appendChild(time);
var ext = xmldoc.createElement('extensions');
x.appendChild(ext);
var accuracy = createSimpleNode('accuracy', e.coords.accuracy);
ext.appendChild(accuracy);
var altitudeAccuracy = createSimpleNode('altitudeAccuracy', e.coords.altitudeAccuracy);
ext.appendChild(altitudeAccuracy);
if (!isNaN(e.coords.speed)) {
var speed = createSimpleNode('speed', e.coords.speed);
ext.appendChild(speed);
}
if (!isNaN(e.coords.heading)) {
var heading = createSimpleNode('heading', e.coords.heading);
ext.appendChild(heading);
}
doc.appendChild(x);
}
return xmldoc;
}
function locationsToFile(format) {
var xmldoc = undefined;
if (format == "gpx") {
xmldoc = locationsToGPX();
}
var oSerializer = new XMLSerializer();
return '<?xml version="1.0" ?>'
+ oSerializer.serializeToString(xmldoc);
}
function saveGeoloc() {
var date = (new Date()).toLocaleFormat("%Y%m%d%H%M%S");
var file = locationsToFile("gpx");
var blob = new Blob([file], {type: 'application/gpx+xml'});
var fname = "geoloc/geoloc-" + date + ".gpx";
var storage = navigator.getDeviceStorage('sdcard');
if (!storage) {
console.log("No storage available!");
console.log(file);
return;
}
var save = storage.addNamed(blob, fname);
save.onsuccess = function() {
alert("Successfully saved " + fname);
};
save.onerror = function() {
alert("Error while saving to " + fname + ": " + this.error.name);
};
}
function enable(id) {
var e = document.getElementById(id);
if (e) {
e.disabled = false;
}
}
function disable(id) {
var e = document.getElementById(id);
if (e) {
e.disabled = true;
}
}
function show(id) {
var e = document.getElementById(id);
if (e) {
e.style.display = "block";
}
}
function hide(id) {
var e = document.getElementById(id);
if (e) {
e.style.display = "none";
}
}
function keep() {
var e = document.getElementById('geoloc-keep');
if (e) {
return e.checked;
}
return false;
}
window.addEventListener('DOMContentLoaded', function() {
_ = navigator.mozL10n.get;
disable('btnStop');
disable('btnClear');
disable('btnSave');
hide('parameters');
hide('status');
hide('debuglog');
var keep = document.getElementById('geoloc-keep');
keep.addEventListener('change', function(ev) {
if (running) {
return;
}
if (ev.target.checked) {
enable('btnSave');
} else {
disable('btnSave');
}
});
document.getElementById('geoloc-debug').addEventListener('change', function(ev) {
if (ev.target.checked) {
show('debuglog');
} else {
hide('debuglog');
}
});
var showHideDiv = function(ev, tgt) {
var clicktarget = document.getElementById(tgt);
clicktarget.addEventListener(ev, function(ev) {
var target = ev.target.dataset['hide'];
var el = document.getElementById(target);
if (el) {
if (el.style.display == "" || el.style.display == "block") {
hide(target);
} else {
show(target);
}
}
});
};
showHideDiv('click', 'h1-parameters');
showHideDiv('click', 'h1-status');
showHideDiv('click', 'h1-debuglog');
document.getElementById('btnGet').addEventListener('click', getGeoloc);
document.getElementById('btnStart').addEventListener('click', startGeoloc);
document.getElementById('btnStop').addEventListener('click', stopGeoloc);
document.getElementById('btnClear').addEventListener('click', clearGeoloc);
document.getElementById('btnSave').addEventListener('click', saveGeoloc);
});

View file

@ -0,0 +1,20 @@
geolocation-tracker = Geolocation tracker
geoloc-get = Get current
geoloc-start = Start
geoloc-stop = Stop
geoloc-clear = Clear
geoloc-debug = Show debug traces
geoloc-keep = Keep locations
geoloc-save = Save as a file
geoloc-parameters = Parameters
geoloc-status = Status
geoloc-debug-log = Debug log
geoloc-timeout = Timeout
geoloc-maxage = Max. age
geoloc-highaccuracy = High accuracy
geoloc-msecs = Timeout and max. age are specified in milliseconds.
gps-fixes = Fixes
last-fix = Last
date-last-fix.placeholder = None

View file

@ -0,0 +1,41 @@
{
"name": "Geoloc",
"description": "Geolocation Tracker",
"type": "certified",
"launch_path": "/index.html",
"developer": {
"name": "lissyx",
"url": "https://github.com/lissyx/gaia-geoloc"
},
"permissions": {
"geolocation":{},
"device-storage:sdcard":{ "access": "readonly" }
},
"locales": {
"ar": {
"name": "Geoloc",
"description": "Geolocation Tracker"
},
"en-US": {
"name": "Geoloc",
"description": "Geolocation Tracker"
},
"fr": {
"name": "Geoloc",
"description": "Enregistrement de géolocalisation"
},
"zh-TW": {
"name": "Geoloc",
"description": "Geolocation Tracker"
}
},
"icons": {
"84": "/style/icons/default_84.png",
"126": "/style/icons/default_126.png",
"142": "/style/icons/default_142.png",
"189": "/style/icons/default_189.png",
"284": "/style/icons/default_284.png"
},
"default_locale": "en-US",
"orientation": "portrait-primary"
}

View file

@ -0,0 +1,27 @@
html, body {
margin: 0;
padding: 0;
background-color: #fff;
}
body {
background: none;
}
li {
list-style: none;
margin-left: -2em;
text-align: right;
}
li span {
text-align: left;
}
fieldset legend {
text-transform: none;
}
span.desc {
padding: 1em;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,10 @@
<html>
<head>
<title>In-App Payments Tester</title>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<iframe id="app" src="http://inapp-pay-test.paas.allizom.org"></iframe>
</body>
</html>

View file

@ -0,0 +1,18 @@
{
"name": "In-app Payment Tester",
"description": "Utility app to test in-app payments.",
"version": "1.0",
"launch_path": "/index.html",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"default_locale": "en",
"icons": {
"84": "/style/icons/default_84.png",
"126": "/style/icons/default_126.png",
"142": "/style/icons/default_142.png",
"189": "/style/icons/default_189.png",
"284": "/style/icons/default_284.png"
}
}

View file

@ -0,0 +1,10 @@
#app {
position: fixed;
border: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="en-US">
<script defer src="shared/js/intl/l20n.js"></script>
<link rel="localization" href="locales/example.{locale}.l20n">
<title>L20n</title>
<style>
html, body {
width: 100%;
height: 100%;
background-color: #dff;
overflow: hidden;
}
</style>
</head>
<body>
<h1 data-l10n-id="hello"></h1>
<p data-l10n-id="name">
<input type="text">
<input type="submit">
</p>
<div data-l10n-id="unreadEmails" data-l10n-args='{"emailCount": 5}'/>
</body>
</html>

View file

@ -0,0 +1 @@
// App script goes here.

View file

@ -0,0 +1,8 @@
<hello "Hello, <em>world</em>!">
<name 'Type <input placeholder="your name"> and <input value="send">'>
<unreadEmails[@cldr.plural($emailCount)] {
one: "You have one unread email",
other: "You have {{ $emailCount }} unread emails"
}>

View file

@ -0,0 +1,17 @@
{
"name": "L20n Example App",
"description": "Example app using l20n file format",
"launch_path": "/index.html",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"default_locale": "en-US",
"icons": {
"84": "/style/icons/default_84.png",
"126": "/style/icons/default_126.png",
"142": "/style/icons/default_142.png",
"189": "/style/icons/default_189.png",
"284": "/style/icons/default_284.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

View file

@ -0,0 +1,9 @@
{
"external": true,
"etag": "\"82c95e39e46ee556037d41177df63766f6c8229b6ad7ee67ec56db17ce29ef8e\"",
"manifestURL": "https://marketplace.firefox.com/app/bee10fbb-7829-4d99-8a9f-a6d0a905138e/manifest.webapp",
"packageEtag": "\"e5dcfbb758ef085adab393464d92ccbd0de7bfc0fab94e0e7d3dc2c1aa07f39f\"",
"installOrigin": "https://marketplace.firefox.com",
"removable": true,
"type": "privileged"
}

View file

@ -0,0 +1,17 @@
{
"name": "Dev",
"description": "Firefox Marketplace Dev",
"developer": {
"name": "Mozilla",
"url": "http://mozilla.org"
},
"icons": {
"60": "/60.png",
"90": "/90.png",
"120": "/120.png",
"128": "/128.png",
"256": "/256.png"
},
"launch_path": "/index.html",
"package_path": "/application.zip"
}

Binary file not shown.

View file

@ -0,0 +1,9 @@
{
"external": true,
"etag": "\"2fe3dfe988d1bce334a4697ef2fcf4a736fb539ed1dd141b74d72d9d50abbfa2\"",
"manifestURL": "https://marketplace.firefox.com/app/3cf61cd6-7ab6-4329-83dc-275b47c8c873/manifest.webapp",
"packageEtag": "\"a9f9981c467a42a53e4009590e9c3123d9ac8b56b4587d31856fddafce054c72\"",
"installOrigin": "https://marketplace.firefox.com",
"removable": true,
"type": "privileged"
}

View file

@ -0,0 +1,17 @@
{
"name": "Stage",
"description": "Firefox Marketplace Stage",
"developer": {
"name": "Mozilla",
"url": "http://mozilla.org"
},
"icons": {
"60": "/60.png",
"90": "/90.png",
"120": "/120.png",
"128": "/128.png",
"256": "/256.png"
},
"launch_path": "/index.html",
"package_path": "/application.zip"
}

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<title>Template</title>
<style>
#canvases {
position: absolute;
top: 50%;
left: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
}
</style>
<script src="membuster.js"></script>
</head>
<body>
<div>
<button id="sys">Bust system memory</button>
<button id="gfx">Bust graphics memory</button>
</div>
<div><span id="log"></span></div>
<div id="canvases"></div>
</body>
</html>

View file

@ -0,0 +1,63 @@
{
"name": "Membuster",
"description": "Busts your memory",
"launch_path": "/index.html",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"locales": {
"ar": {
"name": "Membuster",
"description": "Busts your memory"
},
"de": {
"name": "Membuster",
"description": "Busts your memory"
},
"el": {
"name": "Membuster",
"description": "Busts your memory"
},
"en-US": {
"name": "Membuster",
"description": "Busts your memory"
},
"es": {
"name": "Membuster",
"description": "Busts your memory"
},
"fr": {
"name": "Membuster",
"description": "Busts your memory"
},
"it": {
"name": "Membuster",
"description": "Busts your memory"
},
"pt-BR": {
"name": "Membuster",
"description": "Busts your memory"
},
"ru": {
"name": "Membuster",
"description": "Busts your memory"
},
"tr": {
"name": "Membuster",
"description": "Busts your memory"
},
"zh-TW": {
"name": "Membuster",
"description": "Busts your memory"
}
},
"default_locale": "en-US",
"icons": {
"84": "/style/icons/membuster_84.png",
"126": "/style/icons/membuster_126.png",
"142": "/style/icons/membuster_142.png",
"189": "/style/icons/membuster_189.png",
"284": "/style/icons/membuster_284.png"
}
}

View file

@ -0,0 +1,66 @@
var arr = [];
function bustSysmem() {
setInterval(allocChunk, 1000);
}
var chunks = [ ]; // root the chunks
var nextChunkMB = 32;
var totalAllocMB = 0;
function allocChunk() {
var allocSizeMB = nextChunkMB * 1024 * 1024;
var array = new ArrayBuffer(allocSizeMB);
// Ensure all pages are committed.
var view = new Int32Array(array);
for (var j = 0; j < view.length; j += 1024) {
view[j] = 42;
}
totalAllocMB += nextChunkMB;
log("Allocated "+ nextChunkMB +"MB; total so far is "+ totalAllocMB+ "MB");
if (nextChunkMB > 1) {
nextChunkMB >>= 1
}
chunks.push(array);
}
function bustGfxmem() {
setInterval(allocCanvas, 1000);
}
var totalGfxMB = 0;
function allocCanvas() {
var c = document.createElement("canvas");
// We want to allocate 1MB of gfx mem. Canvases are double buffered
// and default to RGBA. So we create one with 128K pixels.
var w = 128, h = 1024;
c.width = w;
c.height = h;
var ctx = c.getContext('2d');
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = 'rgba(0, 0, 255, 0.1)';
ctx.rect(0, 0, w, h);
ctx.fill();
++totalGfxMB;
log("Allocated 1MB gfx memory; total so far is ~"+ totalGfxMB +"MB");
document.getElementById("canvases").appendChild(c);
}
var logElt;
function log(msg) {
if (!logElt) {
logElt = document.getElementById("log");
}
logElt.textContent = msg;
console.log(msg);
}
window.onload = function() {
document.getElementById("sys").onclick = function() { bustSysmem(); };
document.getElementById("gfx").onclick = function() { bustGfxmem(); };
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Deutsche Telekom, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,364 @@
html {
font-size: 10px;
background-color: #F4F4F4;
}
[role="toolbar"] {
position: static;
}
ul {
font-size: 0.9em;
}
.gaia-icon::before {
font-style: normal;
position: relative;
}
.icon-settings::before {
color: white;
top: 2px;
left: 1px;
}
.note {
font-size: 0.7em;
padding-left: 15px;
padding-bottom: 5px;
background-color: rgb(248, 246, 246);
display: none;
}
#main article > header {
background-color: rgb(248, 246, 246);
color: #F97C17;
height: 30px;
line-height: 30px;
text-align: center;
text-transform: uppercase;
border-bottom: 1px white solid;
border-top: 1px white solid;
/* box-shadow: 0px 1px 5px 0px rgba(50, 50, 50, 0.1);*/
font-size: 12px;
box-shadow: 0px 1px 1px 0px rgba(33, 33, 33, 0.1);
}
.bottom-article > header {
z-index: 3;
position: relative;
}
.hide, .listview-hide {
display: none;
}
#settings p {
margin: 10px;
}
.error {
color: red;
}
section[role="region"] > header:first-child h1:nth-child(2) {
margin-left: 50px;
}
#hci-notification {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 300;
transition: all 0.6s ease;
transform: translateY(100%);
}
#hci-notification img {
width: 240px;
box-shadow: 0px 1px 4px 1px #7B7B7B;
border-radius: 8px;
}
#hci-notification.show {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 300;
transition: all 0.6s ease;
transform: translateY(0);
}
article.notification {
color: #9C9C9C;
font-size: 0.7em;
font-style: italic;
opacity: 0.95;
background: black;
padding-bottom: 10px;
}
.hci-card {
position: absolute;
width: 100%;
height: 100%;
color: white;
z-index: 1;
}
.hci-container {
text-align: center;
border-radius: 10px;
padding: 0px 20px;
color: rgb(77, 78, 83);
background: transparent linear-gradient(to bottom, rgb(234, 239, 242), rgb(212, 221, 228));
}
.hci-container p {
line-height: 3em;
font-weight: 300;
font-size: 0.8em;
}
@keyframes show-card {
0% { z-index: 2; transform: rotate(0deg); top: 0px; left: 0px; width: 240px;}
50% { z-index: 10; transform: rotate(0deg); top: -30px; left: -2px; width: 244px;}
100% { z-index: 10; transform: rotate(0deg); top: -8px; left: 0px; width: 240px;}
}
@keyframes hide-card {
0% { z-index: 10; transform: rotate(0deg); top: -8px; left: 0px; width: 240px;}
50% { z-index: 10; transform: rotate(0deg); top: -30px; left: -2px; width: 244px;}
100% { z-index: 2; transform: rotate(0deg); top: 0px; left: 0px; width: 240px;}
}
ul.swipeable > li.deselected > img {
animation-name: hide-card;
animation-duration: 0.5s;
z-index: 2;
position: relative;
top: 0px;
}
ul.swipeable > li.selected > img {
z-index: 10;
transform: rotate(0deg);
position: relative;
top: -8px;
animation-name: show-card;
animation-duration: 0.5s;
}
ul.details > li {
display: block;
padding: 15px 10px;
}
ul.details > li > p.label {
font-size: 1em;
}
.details span.date {
display: inline-block;
width: 67%;
}
li.details-more {
text-align: center;
vertical-align: bottom;
margin-right: 15px;
}
li.details-more > .details-note {
font-style: italic;
font-size: 0.8em;
color: rgb(133, 133, 133);
vertical-align: bottom;
}
/*.details-oneline p {
display: inline-block;
}*/
.active > header {
z-index: 3;
}
.slider {
background-color: rgb(248, 246, 246);
/* transform: translate3d(0, -131px, 0);
box-shadow: inset 0px 1px 5px 0px rgba(50, 50, 50, 0.1);
box-shadow: 0px 5px 5px 0px rgba(33, 33, 33, 0.1) inset; */
padding-top: 1rem;
height: 25rem;
overflow: hidden;
}
.bottom-slide .slider {
box-shadow: 0px 2px 2px 0px rgba(33, 33, 33, 0.1) inset;
}
.bottom-slide > header {
position: relative;
}
.top-slide {
margin-top: -26rem;
transition: transform 0.75s, margin-top 0.75s ease;
}
.details-visible .top-slide {
margin-top: 0px;
overflow: auto;
transform: translate3d(0,0,0)
}
.bottom-slide {
z-index: 3;
position: relative;
top: -10px;
transition: transform 0.75s, top 0.75s ease;
}
.bottom-slide.details-visible {
top: -253px;
transform: translate3d(0,0,0)
}
section[role="region"] > header:first-child .t-logo {
background: url(../img/icons/t_logo_white.png) center center / 5rem auto no-repeat;
width: 5rem;
}
.pocket, .listview-element-pocket {
width: 260px;
height: 40px;
border-radius: 12px;
position: relative;
z-index: 5;
top: 150px;
left: 0px;
background: linear-gradient(to bottom, rgb(200, 200, 200) 0%, #F4F4F4 77%, #F4F4F4 100%) repeat scroll 0% 0% transparent;
border-top: 1px solid #808080;
opacity: 0.9;
}
/*.nonpayment*/ .details li {
min-height: 0px
}
/*.nonpayment*/ .details p {
display: inline;
}
/*.nonpayment*/ .details p.val {
float: right;
margin-right: 15px;
}
.details .price {
font-family: "Fira Mono";
font-size: 13px;
width: 21%;
display: inline-block;
text-align: right;
}
.details .currency {
font-family: "Fira Mono";
font-size: 13px;
}
.sew, .listview-element-sew {
width: 92%;
height: 10px;
margin: 2px 0px 0px 5px;
border-radius: 12px;
padding-right: 10px;
border-top: 1px dashed #8C8C8C;
}
ul.transaction-list > li.transaction {
padding: 4px 0px 1px 10px;
color: rgb(133, 133, 133);
}
ul.transaction-list > li.transaction:after {
border: 0px none;
}
ul.details > li p {
font-size: 1em;
margin-left: 10px;
line-height: 1em;
}
ul.details > li p.value {
float: right;
margin-right: 10px;
font-size: 0.9em;
}
.error-view {
text-align: center;
padding: 10px;
margin-top: -2em;
}
.error-view h2.error {
font-weight: normal;
font-size: 1.2em;
}
.error-view button {
margin-bottom: 1em;
}
.error-img {
width: 5em;
height: 5em;
background-image: url(../img/errors/error.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
display: inline-block;
}
.error-api {
background-image: url(../img/errors/api-error.png);
}
.error-no-sim {
background-image: url(../img/errors/no-sim.png);
}
.error-sim {
background-image: url(../img/errors/sim-error.png);
}
.footer {
position: absolute;
left: 0px;
right: 0px;
bottom: 0px;
margin: 10px 10px 0px 10px;
}
@media (min-width: 360px) {
.bottom-slide {
top: -1px;
}
.bottom-slide.details-visible {
top: -26rem;
}
ul.details > li.details-more {
padding-right: 20px;
padding-left: 20px;
}
}

View file

@ -0,0 +1,131 @@
section#pin {
background-color: #F4F4F4;
}
.pin-unblocked {
font-weight: 300;
color: #333;
}
.pin-view h2 {
font-size: 1.2em;
font-weight: 300;
text-align: center;
}
.pin-view:not(hide) {
z-index: 200;
}
.pin-top {
/*font-style: italic;*/
height: 7.3em;
}
.pin-input {
text-align: center;
margin: 35px 0px 30px;
}
.pin-input > div {
border-radius: 50%;
display: inline-block;
width: 14px;
height: 14px;
border: 1.5px solid #ccc;
vertical-align: baseline;
margin: 0px 5px;
}
.pin-input > div.pin-digit-entered {
background: linear-gradient(to bottom, #f4f4f4 0%,#333333 100%);
border: 1.5px solid #333;
}
.pin-keypad {
display: table;
text-align: center;
border-collapse: collapse;
margin: 0px 0px 25px 0px;
}
.pin-keypad-row {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: transparent;
/*border-image: linear-gradient(45deg, #f4f4f4, #ccc, #f4f4f4) 1;*/
border-image: linear-gradient(to right, #f4f4f4 0%,#cccccc 30%,#cccccc 70%,#f4f4f4 100%) 1;
}
.pin-keypad-row:last-child {
border: none;
}
.pin-key {
display: table-cell;
width: 2.7em;
height: 2.7em;
vertical-align: middle;
border-right: 1px solid #ccc;
font-size: 1.7em;
cursor: pointer;
}
.pin-key.clear-key::after {
content: "clear";
font-size: 0.5em;
vertical-align: 35%;
}
.pin-key:active {
color: #F97C17;
/*background-color: #ccc;*/
}
.pin-key.gaia-icon::before {
position: relative;
font-size: 1em;
}
.pin-key.icon-clearcancel-cross::before {
font-size: 1.7em;
}
.pin-keypad-row:first-child .pin-key {
background: linear-gradient(to bottom, #f4f4f4, #ccc);
background-origin: border-box;
border-right: 1px solid transparent;
box-shadow: inset -999px 0 0 #f4f4f4;
}
.pin-keypad-row:last-child .pin-key {
background: linear-gradient(to top, #f4f4f4, #ccc);
background-origin: border-box;
border-right: 1px solid transparent;
box-shadow: inset -999px 0 0 #f4f4f4;
}
.pin-keypad-row .pin-key:last-child {
border: none;
}
.pin-warning {
margin-bottom: -39px;
position: relative;
top: -13px;
font-size: 0.85em;
text-align: center;
}
.pin-enable-warning {
margin-top: 1em;
}
.error-pin-enable {
background-image: url(../img/errors/pin-enable-fail.png);
}
.error-locked {
background-image: url(../img/errors/locked.png);
}

View file

@ -0,0 +1,172 @@
#settings {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
opacity: 1;
transition: all 0.6s ease;
background-color: #F4F4F4;
transform: translateY(100%);
}
#settings.edit {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
opacity: 1;
transition: all 0.6s ease;
transform: translateY(0);
}
.settings-subpage {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 101;
opacity: 1;
background-color: #F4F4F4;
transition: transform 0.6s ease;
transform: translateX(100%);
}
.settings-subpage.slide-in {
transition: transform 0.6s ease;
transform: translateX(0);
}
.settings-disabled {
pointer-events: none;
color: #ccc;
}
.settings-disabled .gaia-icon::before {
color: #ccc;
}
.settings-disclaimer {
font-size: 0.6em;
padding: 0px 15px 10px 15px;
line-height: 1.5em;
}
.settings-forward div {
width: 100%;
margin-left: 15px;
}
.settings-forward .gaia-icon {
text-align: right;
color: #868692;
}
.settings-forward .gaia-icon::before {
position: relative;
font-size: 1.5rem;
}
#settings-status {
opacity: 0;
transition: opacity 600ms ease 0s;
}
#settings-status.visible {
opacity: 1;
transition: opacity 1000ms ease 0s;
}
#settings-status p{
text-align: center;
}
#settings-about section > div {
text-align: center;
border-bottom: 1px solid #E7E7E7;
padding: 10px;
margin: 0px 15px;
}
#settings-about div:last-child {
border: none;
}
#settings-about div:first-child {
padding-top: 0px;
}
.about-logo img {
max-width: 9em;
}
.about-logo p {
line-height: 1.3em;
}
.about-logo p:first-child {
font-size: 1.1em;
}
.about-logo p:nth-child(2) {
font-size: 1.5em;
}
.about-logo p:nth-child(4) {
font-size: 1em;
}
.about-authors p, .about-contributors p {
font-size: 0.8em;
color: #a6a6a6;
line-height: 1.5em;
}
.about-authors strong, .about-contributors strong {
color: #333;
font-weight: normal;
}
.about-authors p {
text-align: left;
}
.about-authors strong {
float: right;
}
.about-contributors div {
margin-bottom: 0.5em;
}
.about-contributors h3 {
font-size: 0.8em;
margin-bottom: 2px;
margin-top: 1px;
font-weight: 500;
}
.about-contributors div p:nth-child(2) {
line-height: 1em;
margin: 5px 0px;
}
.about-contributors div p:nth-child(3) {
line-height: 1em;
font-size: 0.6em;
color: #333;
}
/* ugly hacks to override header.css, TODO migrate to proper gaia header */
section[role="region"].settings-subpage > header:first-child h1:nth-child(2) {
margin: -1px;
}
section.skin-organic.settings-subpage[role="region"] > header:first-child .gaia-icon {
color: #868692;
background: none;
}

View file

@ -0,0 +1,70 @@
ul.swipeable {
white-space: nowrap;
overflow: hidden;
font-size: 0px;
}
ul.swipeable > li {
display: inline-block;
width: 260px;
height: 206px;
overflow: hidden;
}
ul.swipeable > li > img, .listview-element-img {
width: 240px;
padding: 20px 10px 25px;
margin-top: -35px;
position: relative;
}
ul.swipeable > li:first-child {
margin-left: 3rem;
}
.swipeable-container {
z-index: 2;
background-color: #f4f4f4;
border-bottom: 1px solid #FBFBFB;
box-shadow: 0px 2px 2px 0px rgba(33, 33, 33, 0.1);
}
.swipeable-progress {
text-align: center;
height: 1.6rem;
position: relative;
z-index: 1;
}
.swipeable-progress div{
border-radius: 50%;
display: inline-block;
width: 6px;
height: 6px;
border: 2px solid #aeaeae;
vertical-align: baseline;
margin: 0px 2px;
}
.swipeable-progress div.swipeable-progress-selected {
background: #aeaeae;
}
.swipeable-progress div.swipeable-progress-active {
/* background-color: #e20074; */
border: 2px solid #F97C17;
}
.swipeable-progress div.swipeable-progress-active.swipeable-progress-selected {
background: #F97C17;
}
@media (min-width: 360px) {
ul.swipeable > li:first-child {
margin-left: 5rem;
}
.swipeable-container {
padding-top: 0.5rem;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Some files were not shown because too many files have changed in this diff Show more