Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
8
gaia/tools/extensions/gaia-build@gaiamobile.org/Makefile
Executable file
8
gaia/tools/extensions/gaia-build@gaiamobile.org/Makefile
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
xpi:
|
||||
zip gaia-build@gaiamobile.org.xpi -r bootstrap.js install.rdf chrome.manifest content/
|
||||
|
||||
run:
|
||||
mkdir -p profile/extensions/
|
||||
echo "../../" > profile/extensions/gaia-build@gaiamobile.org
|
||||
echo "user_pref(\"extensions.autoDisableScopes\", 14);" > profile/user.js
|
||||
$(FIREFOX) -profile $(shell pwd)/profile -no-remote about:gaia
|
||||
68
gaia/tools/extensions/gaia-build@gaiamobile.org/bootstrap.js
vendored
Executable file
68
gaia/tools/extensions/gaia-build@gaiamobile.org/bootstrap.js
vendored
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
// Register about:gaia page
|
||||
let classID = Components.ID('{58d667ee-f9e8-4545-841c-927d86906620}');
|
||||
|
||||
function AboutGaia() {}
|
||||
AboutGaia.prototype = {
|
||||
classID: classID,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
|
||||
|
||||
getURIFlags: function(aURI) {
|
||||
return Ci.nsIAboutModule.ALLOW_SCRIPT;
|
||||
},
|
||||
|
||||
newChannel: function(aURI, aLoadInfo) {
|
||||
let spec = 'chrome://gaia-build/content/aboutGaia.html';
|
||||
let uri = Services.io.newURI(spec, null, null);
|
||||
let channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
|
||||
channel.originalURI = aURI;
|
||||
return channel;
|
||||
}
|
||||
};
|
||||
|
||||
function registerAbout() {
|
||||
let contract = '@mozilla.org/network/protocol/about;1?what=gaia';
|
||||
let instance = null;
|
||||
let newFactory = {
|
||||
createInstance: function(outer, iid) {
|
||||
if (outer)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
if (instance === null)
|
||||
instance = new AboutGaia();
|
||||
return instance;
|
||||
},
|
||||
lockFactory: function(aLock) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
|
||||
};
|
||||
Cm.registerFactory(classID, '', contract, newFactory);
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
try {
|
||||
registerAbout();
|
||||
} catch (e) {
|
||||
Cu.reportError(e + '\n' + e.stack);
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
}
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
||||
|
||||
1
gaia/tools/extensions/gaia-build@gaiamobile.org/chrome.manifest
Executable file
1
gaia/tools/extensions/gaia-build@gaiamobile.org/chrome.manifest
Executable file
|
|
@ -0,0 +1 @@
|
|||
content gaia-build content/
|
||||
410
gaia/tools/extensions/gaia-build@gaiamobile.org/content/aboutGaia.css
Executable file
410
gaia/tools/extensions/gaia-build@gaiamobile.org/content/aboutGaia.css
Executable file
|
|
@ -0,0 +1,410 @@
|
|||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
select, button {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#tools > label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#available-apps > li {
|
||||
display: inline-block;
|
||||
margin: 0.2rem;
|
||||
}
|
||||
|
||||
.page > ul {
|
||||
padding-left: 0.2rem;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.page > ul > li {
|
||||
display: inline-block;
|
||||
margin: 0.2rem;
|
||||
}
|
||||
|
||||
.page > .container > div {
|
||||
font-size: 0.7em;
|
||||
display: inline-block;
|
||||
margin: 0.2rem;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
overflow: hidden;
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
border-radius: 0.4rem;
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
#available-apps div.app {
|
||||
display: inline-block;
|
||||
margin: 0.2rem;
|
||||
padding: 0.2rem;
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
|
||||
#available-apps div.app[data-source="external"] {
|
||||
background-color: #fcaf3e;
|
||||
}
|
||||
|
||||
#available-apps div.app:hover {
|
||||
background-color: #204a87;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#available-apps {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#main {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
#tools {
|
||||
width: 18rem;
|
||||
border-right: 1px solid black;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
#content {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
|
||||
#notification {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 15rem;
|
||||
height: 25rem;
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
display: block;
|
||||
float: right;
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
font-size: 0.8em;
|
||||
margin: 0.5rem;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#add-page {
|
||||
font-size: 5em;
|
||||
text-align: center;
|
||||
line-height: 25rem;
|
||||
}
|
||||
|
||||
.page > input[type="radio"], #dock > input[type="radio"] {
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page > input[type="radio"] + label, #dock > input[type="radio"] + label {
|
||||
opacity: 0.1;
|
||||
display: block;
|
||||
height: 0;
|
||||
margin-left: -1.5rem;
|
||||
margin-top:1.2rem;
|
||||
transform: rotate(-45deg);
|
||||
width: 4rem;
|
||||
line-height: 1.5rem;
|
||||
border-bottom: 1.5rem solid red;
|
||||
border-left: 1.5rem solid transparent;
|
||||
border-right: 1.5rem solid transparent;
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
color: white;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
#dock > input[type="radio"] + label {
|
||||
padding-top: 1.3rem;
|
||||
margin-left:-2rem;
|
||||
}
|
||||
|
||||
.page > input[type="radio"]:checked + label,
|
||||
#dock > input[type="radio"]:checked + label {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.page > input[type="radio"]:checked + label:after {
|
||||
content: "selected";
|
||||
}
|
||||
|
||||
#dock {
|
||||
margin: 1rem;
|
||||
width: 24rem;
|
||||
height: 5rem;
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
#dock > .container {
|
||||
margin-top: -2rem;
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
.page > .container {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
#grid {
|
||||
overflow-x: scroll;
|
||||
|
||||
display: -moz-box;
|
||||
-moz-box-orient: horizontal;
|
||||
-moz-box-pack: center;
|
||||
-moz-box-align: center;
|
||||
|
||||
box-orient: horizontal;
|
||||
box-pack: center;
|
||||
box-align: center;
|
||||
}
|
||||
|
||||
div.lightbox {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.lightbox:target {
|
||||
display: table;
|
||||
}
|
||||
|
||||
div.lightbox .lightbox-background {
|
||||
display: table-cell;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.lightbox .lightbox-background .lightbox-container {
|
||||
display: block;
|
||||
margin: auto;
|
||||
padding: 2rem;
|
||||
background-color: white;
|
||||
height: 25rem;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
border: 1px #000000 solid;
|
||||
border-radius: 0.5rem;
|
||||
text-align: justify;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
div.lightbox .lightbox-background .closemsg {
|
||||
display: block;
|
||||
margin: auto;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
text-align: right;
|
||||
z-index: 5001;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.lightbox .lightbox-background .closemsg, div.lightbox .lightbox-background .lightbox-container {
|
||||
width: 50rem;
|
||||
}
|
||||
|
||||
.closemsg::after {
|
||||
content: "\00D7";
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
right: -2rem;
|
||||
top: -1rem;
|
||||
z-index: 5002;
|
||||
color: #ffffff;
|
||||
border: 1px #ffffff solid;
|
||||
border-radius: 1rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
line-height: 1.8rem;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
background-color: #000000;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.closemsg.hidden:after {
|
||||
content: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.closemsg::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000000;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.lightbox-buttons {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.lightbox-buttons > a {
|
||||
display: inline-block;
|
||||
margin: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid black;
|
||||
padding: 0.5rem;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#gaia-path-lightbox .lightbox-container {
|
||||
text-align: center;
|
||||
line-height: 25rem;
|
||||
}
|
||||
|
||||
#marketplace-search-result .lightbox-container {
|
||||
background-color: #BEBEBE;
|
||||
}
|
||||
|
||||
#reset-gaia {
|
||||
font-size: 0.8em;
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tool-section {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.marketplace-app-container {
|
||||
background-color: white;
|
||||
margin: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
position: relative;
|
||||
height: 8rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.marketplace-app-container:hover {
|
||||
box-shadow: 0 0 0.5rem black;
|
||||
}
|
||||
|
||||
.marketplace-app-container > * {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.marketplace-app-icon {
|
||||
left: 1rem;
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.marketplace-app-ratings {
|
||||
top: 5rem;
|
||||
color: #3388C1;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.marketplace-app-review {
|
||||
left: 6rem;
|
||||
top: 5.5rem;
|
||||
font-size: 0.8em;
|
||||
color: #3388C1;
|
||||
}
|
||||
|
||||
.marketplace-app-name {
|
||||
left: 6rem;
|
||||
top: 1rem;
|
||||
color: #333333;
|
||||
font-family: "Fira Sans","Open Sans","Helvetica Neue",Arial,sans-serif;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.marketplace-app-author {
|
||||
left: 6rem;
|
||||
top: 3.5rem;
|
||||
font-size: 0.8em;
|
||||
color: #A7A7A7;
|
||||
}
|
||||
|
||||
.noted {
|
||||
font-size: 0.8em;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.marketplace-app-type {
|
||||
position: relative;
|
||||
float: right;
|
||||
margin-right: 2rem;
|
||||
width: 10rem;
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.marketplace-app-type[data-is-packaged="true"] {
|
||||
background-color: #204a78;
|
||||
}
|
||||
|
||||
.marketplace-app-type[data-is-packaged="true"]:before {
|
||||
content: "Packaged app";
|
||||
background-color: #204a78;
|
||||
}
|
||||
|
||||
.lightbox-title {
|
||||
font-size: 2em;
|
||||
border-bottom: 1px solid black;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.lightbox-content {
|
||||
margin: 2rem;
|
||||
}
|
||||
|
||||
.lightbox-content a {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.operator input {
|
||||
margin: 0.2rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0.6rem;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#certain-mcc-mnc:not(:checked) ~ #operators-options {
|
||||
display: none
|
||||
}
|
||||
87
gaia/tools/extensions/gaia-build@gaiamobile.org/content/aboutGaia.html
Executable file
87
gaia/tools/extensions/gaia-build@gaiamobile.org/content/aboutGaia.html
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Homescreen customization</title>
|
||||
<link rel="stylesheet" href="chrome://gaia-build/content/aboutGaia.css">
|
||||
<script type="application/javascript;version=1.8" src="chrome://gaia-build/content/gaia.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="chrome://gaia-build/content/aboutGaia.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="tools">
|
||||
<section class="tool-section">
|
||||
<div id="builds-label">Build config: </div>
|
||||
<select name="" id="gaia-builds"></select>
|
||||
</section>
|
||||
<section class="tool-section">
|
||||
<div id="available-apps-label">Available apps</div>
|
||||
<div id="available-apps"></div>
|
||||
</section>
|
||||
<section class="tool-section">
|
||||
<div id="marketplace-label">Search from Marketplace</div>
|
||||
<form>
|
||||
<input id="marketplace-keyword" type="text" placeholder="keyword"></input>
|
||||
<button type="submit" id="marketplace-search">Search</button>
|
||||
</form>
|
||||
</section>
|
||||
<button id="launch">Preview</button><button id="save">Save</button>
|
||||
<a id="reset-gaia">Reset gaia path</a>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="notification">Your app will be added into selected page.</div>
|
||||
<div id="grid">
|
||||
<div class="page" id="add-page">+</div>
|
||||
</div>
|
||||
<div id="dock" class="page">
|
||||
<input type="radio" name="page-selection" id="page0" checked="checked">
|
||||
<label for="page0"></label>
|
||||
<div class="container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lightbox" id="options">
|
||||
<div class="lightbox-background">
|
||||
<a href="#" class="closemsg"></a>
|
||||
<div class="lightbox-container">
|
||||
<div class="lightbox-title" id="options-title"></div>
|
||||
<div id="operators-disabled-desc" class="description hidden">
|
||||
Built-in apps can't be disabled for certain operators
|
||||
</div>
|
||||
<div class="lightbox-content hidden" id="options-content">
|
||||
<input id="certain-mcc-mnc" type="checkbox">
|
||||
<label for="certain-mcc-mnc">Available only for certain operators</label>
|
||||
<hr>
|
||||
<div id="operators-options">
|
||||
<div id="operators"></div>
|
||||
<div class="description">Use commas to split different mcc/mnc pairs, e.g., "310-410, 310-260" (without double quotes)</div>
|
||||
<a id="add-operator">Add operator</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lightbox-buttons">
|
||||
<a href="#">Close</a>
|
||||
<a href="#" id="options-remove">Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lightbox" id="gaia-path-lightbox">
|
||||
<div class="lightbox-background">
|
||||
<a class="closemsg hidden"></a>
|
||||
<div class="lightbox-container">
|
||||
Gaia path is necessary.
|
||||
<button id="select-gaia">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lightbox" id="marketplace-search-result">
|
||||
<div class="lightbox-background">
|
||||
<a href="#" class="closemsg"></a>
|
||||
<div class="lightbox-container">
|
||||
<div class="noted">Noted: Packaged app can't be executed in Browser, you need to verify it in b2g-desktop or device.</div>
|
||||
<div id="marketplace-search-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
862
gaia/tools/extensions/gaia-build@gaiamobile.org/content/aboutGaia.js
Executable file
862
gaia/tools/extensions/gaia-build@gaiamobile.org/content/aboutGaia.js
Executable file
|
|
@ -0,0 +1,862 @@
|
|||
'use strict';
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const RE_MCCMNC = /^\d{3}-\d{3}$/;
|
||||
const PAGE_MAX_APP = 16;
|
||||
const DOCK_MAX_APP = 5;
|
||||
const MARKETPLACE_INSTALL_ORIGIN = 'https://marketplace.firefox.com';
|
||||
const EXTERNAL_APP_DIR = 'external-apps';
|
||||
const MARKETPLACE_SEARCH_HASH = '#marketplace-search-result';
|
||||
const MARKETPLACE_SEARCH_BASEURL = 'https://marketplace.firefox.com/api/v1/' +
|
||||
'apps/search/?device=firefoxos&q=';
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/FileUtils.jsm');
|
||||
|
||||
const GAIA_DIR_PREF_NAME = 'extensions.gaia.dir';
|
||||
const DEFAULT_PAGE = 2;
|
||||
var utils, variant;
|
||||
|
||||
var Homescreen = {
|
||||
operators: {},
|
||||
|
||||
init: function hs_init() {
|
||||
var self = this;
|
||||
this.addPageButton = document.getElementById('add-page');
|
||||
this.grid = document.getElementById('grid');
|
||||
this.dock = document.getElementById('dock');
|
||||
this.apps = [];
|
||||
this.searchResultContainer =
|
||||
document.getElementById('marketplace-search-container');
|
||||
this.addPageButton.addEventListener('click', this.addPage.bind(this));
|
||||
this.operatorsElement = document.getElementById('operators');
|
||||
|
||||
if (this.grid.children.length <= 1) {
|
||||
for (var i = 0; i < DEFAULT_PAGE; i++) {
|
||||
this.addPage();
|
||||
}
|
||||
}
|
||||
|
||||
var gaiaBuilds = document.getElementById('gaia-builds');
|
||||
gaiaBuilds.addEventListener('change', function(evt) {
|
||||
var selected = gaiaBuilds.options[gaiaBuilds.options.selectedIndex];
|
||||
self.selectBuild(selected.value);
|
||||
});
|
||||
document.getElementById('save').addEventListener('click',
|
||||
this.save.bind(this));
|
||||
document.getElementById('select-gaia').addEventListener('click',
|
||||
this.selectGaiaFolder.bind(this));
|
||||
document.getElementById('options-remove').addEventListener('click',
|
||||
this.removeApp.bind(this));
|
||||
document.getElementById('reset-gaia').addEventListener('click',
|
||||
resetGaiaPath);
|
||||
document.getElementById('marketplace-keyword').addEventListener('click',
|
||||
function(evt) {
|
||||
this.select();
|
||||
});
|
||||
document.getElementById('marketplace-search').addEventListener('click',
|
||||
function(evt) {
|
||||
var keyword = document.getElementById('marketplace-keyword').value;
|
||||
if (keyword) {
|
||||
self.search(keyword);
|
||||
} else {
|
||||
alert('Please specify a keyword.');
|
||||
}
|
||||
});
|
||||
|
||||
function createOperator() {
|
||||
var div = document.createElement('div');
|
||||
div.classList.add('operator');
|
||||
self.createOperatorEditor(div);
|
||||
self.operatorsElement.appendChild(div);
|
||||
}
|
||||
|
||||
document.getElementById('add-operator').addEventListener('click',
|
||||
createOperator);
|
||||
document.getElementById('certain-mcc-mnc').addEventListener('change',
|
||||
function(evt) {
|
||||
var app = self.apps[self.editingAppElement.dataset.name];
|
||||
if (evt.target.checked) {
|
||||
app.forOperators = true;
|
||||
} else {
|
||||
app.forOperators = false;
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('hashchange', function(evt) {
|
||||
var oldURL = new URL(evt.oldURL);
|
||||
if (oldURL.hash === MARKETPLACE_SEARCH_HASH) {
|
||||
delete self.searchResult;
|
||||
}
|
||||
});
|
||||
createOperator();
|
||||
},
|
||||
|
||||
search: function hs_search(keyword) {
|
||||
var {downloadJSON} = utils;
|
||||
this.searchResultContainer.innerHTML = 'Loading...';
|
||||
window.location.hash = MARKETPLACE_SEARCH_HASH;
|
||||
var self = this;
|
||||
var url = MARKETPLACE_SEARCH_BASEURL + encodeURI(keyword);
|
||||
downloadJSON(url, function(json) {
|
||||
if (json) {
|
||||
self.searchResult = json;
|
||||
self.showSearchResult(self.searchResult);
|
||||
} else {
|
||||
var msg = 'Marketplace search request failed';
|
||||
alert(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
showSearchResult: function hs_showSearchResult(json) {
|
||||
var result = this.searchResultContainer;
|
||||
while(result.firstChild) {
|
||||
result.removeChild(result.firstChild);
|
||||
}
|
||||
json.objects.forEach(function(obj, index) {
|
||||
result.appendChild(this.createSearchEntry(obj, index));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
createSearchEntry: function hs_createSearchEntry(obj, index) {
|
||||
const FULL_STARS = 5;
|
||||
var container = document.createElement('a');
|
||||
container.classList.add('marketplace-app-container');
|
||||
|
||||
var elements = {
|
||||
'icon': 'img',
|
||||
'name': 'div',
|
||||
'author': 'div',
|
||||
'ratings': 'div',
|
||||
'review': 'div',
|
||||
'type': 'div'
|
||||
};
|
||||
for (var key in elements) {
|
||||
var el = document.createElement(elements[key]);
|
||||
el.classList.add('marketplace-app-' + key);
|
||||
container.appendChild(el);
|
||||
elements[key] = el;
|
||||
}
|
||||
|
||||
elements.name.textContent = obj.name;
|
||||
elements.author.textContent = obj.author;
|
||||
elements.icon.src = obj.icons['64'];
|
||||
var ratings = Math.round(obj.ratings.average);
|
||||
elements.ratings.innerHTML = new Array(ratings + 1).join('★');
|
||||
var len = elements.ratings.innerHTML.length;
|
||||
elements.ratings.innerHTML += new Array(FULL_STARS - len + 1).join('☆');
|
||||
elements.ratings.dataset.average = obj.ratings.average;
|
||||
elements.review.textContent = obj.ratings.count + ' reviews';
|
||||
elements.type.dataset.isPackaged = obj.is_packaged;
|
||||
|
||||
container.dataset.index = index;
|
||||
container.addEventListener('click', this.addAppFromMarketplace.bind(this));
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
selectBuild: function hs_selectBuild(buildname) {
|
||||
if (!this.gaiaConfig) {
|
||||
throw new Error('Homescreen.gaiaConfig doesn\'t exist');
|
||||
}
|
||||
var c = this.gaiaConfig;
|
||||
var {getAppsByList} = utils;
|
||||
var apps = getAppsByList(this.buildConfig[buildname].content,
|
||||
c.GAIA_DIR, c.GAIA_DISTRIBUTION_DIR);
|
||||
this.setApps(apps);
|
||||
},
|
||||
|
||||
selectGaiaFolder: function hs_selectGaiaFolder() {
|
||||
this.reset();
|
||||
let path = this.promptFolder();
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
Services.prefs.setCharPref(GAIA_DIR_PREF_NAME, path);
|
||||
},
|
||||
|
||||
promptFolder: function hs_promptFolder() {
|
||||
let fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, 'Select Gaia directory', Ci.nsIFilePicker.modeGetFolder);
|
||||
let res = fp.show();
|
||||
if (res != Ci.nsIFilePicker.returnCancel) {
|
||||
return fp.file.path;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
reset: function hs_reset() {
|
||||
var availableApps = document.getElementById('available-apps');
|
||||
while (availableApps.firstChild) {
|
||||
availableApps.removeChild(availableApps.firstChild);
|
||||
}
|
||||
|
||||
var pages = document.getElementsByClassName('container');
|
||||
Array.prototype.forEach.call(pages, function(page, index) {
|
||||
while (page.firstChild) {
|
||||
page.removeChild(page.firstChild);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
resetBuilds: function hs_resetBuilds() {
|
||||
var builds = document.getElementById('gaia-builds');
|
||||
Array.prototype.forEach.call(builds.children, function(option) {
|
||||
option.remove();
|
||||
});
|
||||
},
|
||||
|
||||
setApps: function hs_setApps(apps) {
|
||||
this.installedApps = {};
|
||||
var availableApps = document.getElementById('available-apps');
|
||||
this.apps = apps;
|
||||
this.reset();
|
||||
|
||||
for (var app in apps) {
|
||||
// app with role in manifest means it has specific purpose and those apps
|
||||
// usually doesn't show on homescreen. e.g., role of bluetooth, pdf.js &
|
||||
// ringtones is "system" which means they are executed by another apps but
|
||||
// doesn't show on homescreen, but those apps are essential for gaia .
|
||||
if (apps[app].manifest.role) {
|
||||
this.installedApps[app] = apps[app];
|
||||
continue;
|
||||
} else {
|
||||
this.createAppElements(apps[app]).forEach(function(el) {
|
||||
availableApps.appendChild(el);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showOptions: function hs_showOptions(evt) {
|
||||
var self = this;
|
||||
this.editingAppElement = evt.target;
|
||||
var operatorsCheckbox = document.getElementById('certain-mcc-mnc');
|
||||
var operatorsOptions = document.getElementById('options-content');
|
||||
var operatorsDisabledMsg = document.getElementById('operators-disabled-desc');
|
||||
var name = this.editingAppElement.dataset.name;
|
||||
document.getElementById('options-title').innerHTML = evt.target.innerHTML;
|
||||
window.location.hash = '#options';
|
||||
|
||||
function updateOperators(name) {
|
||||
var inputs =
|
||||
document.querySelectorAll('.operator input[type="checkbox"]');
|
||||
Array.prototype.forEach.call(inputs, function(input) {
|
||||
var operatorId = input.dataset.operatorId;
|
||||
input.checked = self.operators[operatorId].apps[name] ? true : false;
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.apps[name]) {
|
||||
throw new Error('App doesn\'t exist: ' + name);
|
||||
} else if (this.apps[name].metadata &&
|
||||
this.apps[name].metadata.source === 'external') {
|
||||
updateOperators(name);
|
||||
operatorsCheckbox.checked = this.apps[name].forOperators || false;
|
||||
operatorsOptions.classList.remove('hidden');
|
||||
operatorsDisabledMsg.classList.add('hidden');
|
||||
operatorsCheckbox.disabled = false;
|
||||
} else {
|
||||
operatorsDisabledMsg.classList.remove('hidden');
|
||||
operatorsOptions.classList.add('hidden');
|
||||
operatorsCheckbox.disabled = true;
|
||||
}
|
||||
},
|
||||
|
||||
updateOperators: function hs_updateOperators(operator, appName) {
|
||||
var prevOperatorId;
|
||||
if (this.editingOperator) {
|
||||
prevOperatorId = this.editingOperator.id;
|
||||
} else {
|
||||
prevOperatorId = operator.id;
|
||||
}
|
||||
|
||||
if (this.operators[prevOperatorId] && prevOperatorId !== operator.id) {
|
||||
operator.apps = this.operators[prevOperatorId].apps;
|
||||
delete this.operators[prevOperatorId];
|
||||
}
|
||||
this.operators[operator.id] = operator;
|
||||
|
||||
if (appName) {
|
||||
operator.apps[appName] = true;
|
||||
}
|
||||
},
|
||||
|
||||
getOperator: function hs_getOperator(name) {
|
||||
var operator;
|
||||
this.operators.every(function(opr) {
|
||||
if (opr.id === name) {
|
||||
operator = opr;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return operator;
|
||||
},
|
||||
|
||||
convertToOperator: function hs_convertToOperator(el) {
|
||||
var operatorId = el.querySelector('[data-field="operatorId"]').value;
|
||||
var mccmnc = el.querySelector('[data-field="mccmnc"]').value;
|
||||
var formatError = false;
|
||||
var mccmncArray = mccmnc.split(',').map(function(item) {
|
||||
var pair = item.trim();
|
||||
formatError = !RE_MCCMNC.test(pair) || formatError;
|
||||
return pair;
|
||||
});
|
||||
if (formatError) {
|
||||
throw new Error('MCC/MNC format incorrect, both of MCC and MNC need ' +
|
||||
'3 digits like "310-410" (without double quotes)');
|
||||
}
|
||||
return {
|
||||
'id': operatorId,
|
||||
'mcc-mnc': mccmncArray,
|
||||
'apps': {}
|
||||
};
|
||||
},
|
||||
|
||||
switchOperatorMode: function hs_switchOperatorMode(operatorDiv, toEdit) {
|
||||
function removeAllChildren(element) {
|
||||
while (element.firstChild) {
|
||||
element.removeChild(element.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
if (toEdit) {
|
||||
var operatorId =
|
||||
operatorDiv.querySelector('input[type="checkbox"]').dataset.operatorId;
|
||||
var operator = this.operators[operatorId];
|
||||
this.editingOperator = operator;
|
||||
removeAllChildren(operatorDiv);
|
||||
this.createOperatorEditor(operatorDiv, operator);
|
||||
} else {
|
||||
var operator = this.convertToOperator(operatorDiv);
|
||||
removeAllChildren(operatorDiv);
|
||||
this.createOperatorView(operatorDiv, operator.id);
|
||||
delete this.editingOperator;
|
||||
}
|
||||
},
|
||||
|
||||
createOperatorView: function hs_createOperatorView(div, operatorId) {
|
||||
var {normalizeString} = utils;
|
||||
var self = this;
|
||||
var operator = this.operators[operatorId];
|
||||
|
||||
var checkbox = document.createElement('input');
|
||||
checkbox.dataset.operatorId = operatorId;
|
||||
checkbox.dataset.normalizedName = normalizeString(operatorId);
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.id = 'checkbox-' + checkbox.dataset.normalizedName;
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.setAttribute('for', checkbox.id);
|
||||
label.innerHTML = operatorId;
|
||||
|
||||
var span = document.createElement('span');
|
||||
span.innerHTML = ' - ';
|
||||
var edit = document.createElement('a');
|
||||
edit.innerHTML = 'edit';
|
||||
|
||||
checkbox.checked = (operator &&
|
||||
operator.apps[this.editingAppElement.dataset.name]);
|
||||
|
||||
checkbox.addEventListener('change', function(evt) {
|
||||
var operator = self.operators[evt.target.dataset.operatorId];
|
||||
if (evt.target.checked) {
|
||||
operator.apps[self.editingAppElement.dataset.name] = true;
|
||||
} else {
|
||||
delete operator.apps[self.editingAppElement.dataset.name];
|
||||
}
|
||||
});
|
||||
|
||||
edit.addEventListener('click', function(evt) {
|
||||
self.switchOperatorMode(div, true);
|
||||
});
|
||||
|
||||
span.appendChild(edit);
|
||||
[checkbox, label, span].forEach(function(child) {
|
||||
div.appendChild(child);
|
||||
});
|
||||
},
|
||||
|
||||
createOperatorEditor: function hs_createOperatorEditor(div, operator) {
|
||||
var self = this;
|
||||
function createInput(field, placeholder) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.placeholder = placeholder;
|
||||
input.dataset.field = field;
|
||||
return input;
|
||||
}
|
||||
|
||||
var idInput = createInput('operatorId', 'Operator ID');
|
||||
var mccmncInput = createInput('mccmnc', 'mcc/mnc');
|
||||
var save = document.createElement('a');
|
||||
save.innerHTML = 'Save';
|
||||
save.addEventListener('click', function(evt) {
|
||||
var operatorDiv = evt.target.parentElement;
|
||||
try {
|
||||
var operator = self.convertToOperator(operatorDiv);
|
||||
self.updateOperators(operator, self.editingAppElement.dataset.name);
|
||||
self.switchOperatorMode(operatorDiv, false);
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (operator) {
|
||||
idInput.value = operator.id;
|
||||
mccmncInput.value = operator['mcc-mnc'].join(', ');
|
||||
}
|
||||
|
||||
[idInput, mccmncInput, save].forEach(function(el) {
|
||||
div.appendChild(el);
|
||||
});
|
||||
},
|
||||
|
||||
removeApp: function hs_removeApp() {
|
||||
if (!this.editingAppElement) {
|
||||
throw new Error('You didn\'t select an app to edit');
|
||||
}
|
||||
var availableApps = document.getElementById('available-apps');
|
||||
availableApps.appendChild(this.createApp(this.editingAppElement.dataset));
|
||||
this.editingAppElement.remove();
|
||||
delete this.editingAppElement;
|
||||
|
||||
for (var key in this.operators) {
|
||||
delete this.operators[key].apps[this.editingAppElement.dataset.name];
|
||||
}
|
||||
|
||||
window.location.hash = '';
|
||||
},
|
||||
|
||||
getMetadata: function hs_getMetadata(entry, manifest, callback) {
|
||||
var {normalizeString} = utils;
|
||||
var name = normalizeString(entry.name)
|
||||
var metadata = {
|
||||
'name': name,
|
||||
'installOrigin': MARKETPLACE_INSTALL_ORIGIN,
|
||||
'manifestURL': entry.manifest_url,
|
||||
'source': 'external'
|
||||
};
|
||||
|
||||
if (entry.is_packaged) {
|
||||
metadata.origin = 'app://' + name;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('HEAD', manifest.package_path, false);
|
||||
xhr.addEventListener('load', function() {
|
||||
if (xhr.status !== 200 && xhr.status !== 304) {
|
||||
console.warn('Get header of packaged app failed, Status Code: ' +
|
||||
xhr.status);
|
||||
callback(metadata);
|
||||
return;
|
||||
}
|
||||
|
||||
var etag = xhr.getResponseHeader('etag');
|
||||
if (etag) {
|
||||
metadata.packageEtag = etag;
|
||||
}
|
||||
callback(metadata);
|
||||
});
|
||||
xhr.send();
|
||||
} else {
|
||||
var url = new URL(entry.manifest_url);
|
||||
metadata.origin = url.origin;
|
||||
callback(metadata);
|
||||
}
|
||||
},
|
||||
|
||||
filterProperties: function hs_filterProperties(obj, names) {
|
||||
var newObj = {};
|
||||
for (var prop in obj) {
|
||||
if (names.indexOf(prop) >= 0) {
|
||||
newObj[prop] = obj[prop];
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
},
|
||||
|
||||
processAppDownload: function hs_processAppDownload(metadata, json) {
|
||||
var {getFile, ensureFolderExists} = utils;
|
||||
var {downloadApp} = variant;
|
||||
var filteredMetadata = this.filterProperties(metadata, ['origin',
|
||||
'manifestURL', 'installOrigin', 'etag', 'packageEtag']);
|
||||
var appPath = getFile(this.gaiaConfig.GAIA_DISTRIBUTION_DIR,
|
||||
EXTERNAL_APP_DIR, metadata.name);
|
||||
ensureFolderExists(appPath);
|
||||
downloadApp(json, filteredMetadata, appPath.path, function() {
|
||||
var {getApp} = utils;
|
||||
var app = getApp(EXTERNAL_APP_DIR, metadata.name,
|
||||
this.gaiaConfig.GAIA_DIR,
|
||||
this.gaiaConfig.GAIA_DISTRIBUTION_DIR);
|
||||
app.metadata = metadata;
|
||||
this.apps[metadata.name] = app;
|
||||
window.location.hash = '';
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
addAppFromMarketplace: function hs_addAppFromMarketplace(evt) {
|
||||
var self = this;
|
||||
var {normalizeString} = utils;
|
||||
var entry = this.searchResult.objects[evt.currentTarget.dataset.index];
|
||||
var name = normalizeString(entry.name);
|
||||
if (this.apps[name]) {
|
||||
alert(name + ' app already exists.');
|
||||
return;
|
||||
}
|
||||
this.searchResultContainer.innerHTML = 'Downloading...';
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', entry.manifest_url, false);
|
||||
xhr.addEventListener('load', function() {
|
||||
if (xhr.status !== 200 && xhr.status !== 304) {
|
||||
alert('Download Error, Status Code: ' + xhr.status);
|
||||
window.location.hash = '';
|
||||
return;
|
||||
}
|
||||
|
||||
var manifest = JSON.parse(xhr.responseText);
|
||||
self.getMetadata(entry, manifest, function(metadata) {
|
||||
var app = self.createApp({
|
||||
'name': metadata.name,
|
||||
'source': 'external'
|
||||
});
|
||||
document.getElementById('available-apps').appendChild(app);
|
||||
var etag = xhr.getResponseHeader('etag');
|
||||
if (etag) {
|
||||
metadata.etag = etag;
|
||||
}
|
||||
self.processAppDownload(metadata, manifest);
|
||||
});
|
||||
});
|
||||
xhr.send();
|
||||
},
|
||||
|
||||
addToPage: function hs_addToPage(evt) {
|
||||
var props = {};
|
||||
Object.keys(evt.target.dataset).forEach(function(key) {
|
||||
props[key] = evt.target.dataset[key];
|
||||
});
|
||||
var selectedPage =
|
||||
document.querySelector('input[type=radio]:checked ~ .container');
|
||||
var app = this.createApp(props, true);
|
||||
var max = selectedPage.parentElement.id === 'dock' ?
|
||||
DOCK_MAX_APP : PAGE_MAX_APP;
|
||||
if (selectedPage.children.length < max) {
|
||||
selectedPage.appendChild(app);
|
||||
evt.target.remove();
|
||||
} else {
|
||||
alert('can\'t add anymore');
|
||||
}
|
||||
},
|
||||
|
||||
createAppElements: function hs_createAppElements(app) {
|
||||
function logMissingIcon(app, entryPoint) {
|
||||
var name = app + (entryPoint ? ':' + entryPoint : '');
|
||||
console.warn('icons for ' + name + ' doesn\'t exist');
|
||||
}
|
||||
var elements = [];
|
||||
if (!app.manifest.icons && !app.manifest.entry_points) {
|
||||
logMissingIcon(app.name);
|
||||
} else if (!app.manifest.entry_points && app.manifest.icons) {
|
||||
elements.push(this.createApp({'name': app.name}));
|
||||
} else {
|
||||
for (var entryPoint in app.manifest.entry_points) {
|
||||
if (!app.manifest.entry_points[entryPoint].icons) {
|
||||
logMissingIcon(app.name, entryPoint);
|
||||
continue;
|
||||
}
|
||||
elements.push(this.createApp({
|
||||
'name': app.name,
|
||||
'entryPoint': entryPoint
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
},
|
||||
|
||||
createApp: function hs_createApp(props, inPage) {
|
||||
var div = document.createElement('div');
|
||||
div.classList.add('app');
|
||||
div.innerHTML = props.name;
|
||||
if (props.entryPoint) {
|
||||
div.innerHTML += ':' + props.entryPoint;
|
||||
}
|
||||
|
||||
Object.keys(props).forEach(function(key) {
|
||||
div.dataset[key] = props[key];
|
||||
});
|
||||
|
||||
if (!div.dataset.source) {
|
||||
div.dataset.source = 'builtin';
|
||||
}
|
||||
|
||||
if (!inPage) {
|
||||
div.addEventListener('click', this.addToPage.bind(this));
|
||||
} else {
|
||||
div.addEventListener('click', this.showOptions.bind(this));
|
||||
}
|
||||
return div;
|
||||
},
|
||||
|
||||
setBuildConfig: function hs_setBuildConfig(config) {
|
||||
this.reset();
|
||||
this.buildConfig = config;
|
||||
var buildsElement = document.getElementById('gaia-builds');
|
||||
for (var build in config) {
|
||||
var option = document.createElement('option');
|
||||
option.setAttribute('value', build);
|
||||
option.innerHTML = build;
|
||||
|
||||
if (build === 'production') {
|
||||
option.setAttribute('selected', 'selected');
|
||||
this.selectBuild(build);
|
||||
}
|
||||
buildsElement.appendChild(option);
|
||||
}
|
||||
},
|
||||
|
||||
addPage: function hs_addPage() {
|
||||
var self = this;
|
||||
var lastInput =
|
||||
document.querySelector('#grid > div:nth-last-child(2) > input');
|
||||
var lastId = lastInput ? parseInt(lastInput.id.substr('page'.length)) : 0;
|
||||
var page = document.createElement('div');
|
||||
page.classList.add('page');
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.setAttribute('type', 'radio');
|
||||
input.setAttribute('name', 'page-selection');
|
||||
input.id = 'page' + (lastId + 1);
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.setAttribute('for', input.id);
|
||||
|
||||
var closeBtn = document.createElement('div');
|
||||
closeBtn.classList.add('close-button');
|
||||
closeBtn.innerHTML = '×';
|
||||
|
||||
closeBtn.addEventListener('click', function(evt) {
|
||||
var targetPage = evt.target.parentNode;
|
||||
var targetInput = targetPage.getElementsByTagName('input')[0];
|
||||
if (targetInput.checked) {
|
||||
document.getElementById('page0').checked = true;
|
||||
}
|
||||
|
||||
var apps = targetPage.querySelectorAll('.app');
|
||||
var availableApps = document.getElementById('available-apps');
|
||||
Array.prototype.forEach.call(apps, function(app) {
|
||||
var {name, entryPoint} = app.dataset;
|
||||
var restoredApp = self.createApp({
|
||||
'name': name, 'entryPoint': entryPoint});
|
||||
availableApps.appendChild(restoredApp);
|
||||
});
|
||||
|
||||
targetPage.remove();
|
||||
});
|
||||
|
||||
var container = document.createElement('div');
|
||||
container.classList.add('container');
|
||||
|
||||
page.appendChild(closeBtn);
|
||||
page.appendChild(input);
|
||||
page.appendChild(label);
|
||||
page.appendChild(container);
|
||||
|
||||
this.grid.insertBefore(page, this.addPageButton);
|
||||
},
|
||||
|
||||
save: function hs_save(evt) {
|
||||
var self = this;
|
||||
if (!utils) {
|
||||
throw new Error('utils module doesn\'t exist');
|
||||
}
|
||||
|
||||
var variant = {
|
||||
'apps': {},
|
||||
'operators': []
|
||||
};
|
||||
|
||||
var operators = JSON.parse(JSON.stringify(this.operators));
|
||||
var distDir = utils.getFile(this.gaiaConfig.GAIA_DISTRIBUTION_DIR);
|
||||
utils.ensureFolderExists(distDir);
|
||||
|
||||
var pages = document.querySelectorAll('div.page > div.container');
|
||||
var grid = {
|
||||
homescreens: [],
|
||||
search_page: { enabled: true }
|
||||
};
|
||||
|
||||
Array.prototype.forEach.call(pages, function(page, index) {
|
||||
var pageArray = [];
|
||||
Array.prototype.forEach.call(page.children, function(el, position) {
|
||||
var parent;
|
||||
var {name, entryPoint} = el.dataset;
|
||||
|
||||
if (!self.apps[name]) {
|
||||
throw new Error('App doesn\'t exist: ' + name);
|
||||
}
|
||||
|
||||
if (self.apps[name].forOperators) {
|
||||
for (var key in self.operators) {
|
||||
if (self.operators[key].apps[name]) {
|
||||
operators[key].apps[name] = {
|
||||
'id': name,
|
||||
'screen': index,
|
||||
'position': position
|
||||
};
|
||||
variant.apps[name] =
|
||||
self.filterProperties(self.apps[name].metadata,
|
||||
['origin', 'manifestURL', 'installOrigin', 'etag']
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var f = utils.getFile(self.apps[name].path);
|
||||
parent = f.parent.leafName;
|
||||
|
||||
var appArray = [parent, name];
|
||||
if (entryPoint) {
|
||||
appArray.push(entryPoint);
|
||||
}
|
||||
pageArray.push(appArray);
|
||||
|
||||
self.installedApps[name] = self.apps[name];
|
||||
});
|
||||
|
||||
if (index !== pages.length - 1) {
|
||||
grid.homescreens.push(pageArray);
|
||||
} else {
|
||||
// insert apps in dock to first one element of pageArray
|
||||
grid.homescreens.splice(0, 0, pageArray);
|
||||
}
|
||||
});
|
||||
|
||||
var appslistFile = distDir.clone();
|
||||
appslistFile.append('apps.list');
|
||||
|
||||
var applistArray = [];
|
||||
for (var key in self.installedApps) {
|
||||
applistArray.push(self.installedApps[key].parent + '/' + key);
|
||||
}
|
||||
|
||||
utils.writeContent(appslistFile, applistArray.join('\n'));
|
||||
|
||||
var homescreenFile = distDir.clone();
|
||||
homescreenFile.append('homescreens.json');
|
||||
utils.writeContent(homescreenFile, JSON.stringify(grid, undefined, 2));
|
||||
|
||||
for (var key in operators) {
|
||||
var operator = operators[key];
|
||||
var apps = [];
|
||||
for (var appname in operator.apps) {
|
||||
apps.push(operator.apps[appname]);
|
||||
}
|
||||
operator.apps = apps;
|
||||
variant.operators.push(operators[key]);
|
||||
}
|
||||
|
||||
var variantFile = distDir.clone();
|
||||
variantFile.append('variant.json');
|
||||
utils.writeContent(variantFile, JSON.stringify(variant, undefined, 2));
|
||||
|
||||
// open directory in file manager if click save button
|
||||
if (evt && evt.target) {
|
||||
distDir.launch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function resetGaiaPath() {
|
||||
Homescreen.resetBuilds();
|
||||
Services.prefs.setCharPref(GAIA_DIR_PREF_NAME, '');
|
||||
window.location.hash = '#gaia-path-lightbox';
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
Homescreen.init();
|
||||
|
||||
Gaia.addEventListener('setupFinished', function() {
|
||||
utils = Gaia.require('utils');
|
||||
variant = Gaia.require('variant');
|
||||
Homescreen.gaiaConfig = Gaia.config;
|
||||
var {getFile, getBuildConfig} = utils;
|
||||
var configs = getBuildConfig(getFile(Gaia.config.GAIA_DIR, 'build',
|
||||
'config').path);
|
||||
Homescreen.setBuildConfig(configs);
|
||||
});
|
||||
|
||||
var observer = {
|
||||
observe: function(subject, topic, data) {
|
||||
if (data !== GAIA_DIR_PREF_NAME) {
|
||||
return;
|
||||
}
|
||||
var gaiaPath = Services.prefs.getCharPref(GAIA_DIR_PREF_NAME);
|
||||
|
||||
if (!gaiaPath) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Gaia.setup(gaiaPath);
|
||||
window.location.hash = '';
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
resetGaiaPath();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Services.prefs.addObserver(GAIA_DIR_PREF_NAME, observer, false);
|
||||
|
||||
document.getElementById('launch').addEventListener('click', function(evt) {
|
||||
Homescreen.save();
|
||||
var installedAppPaths = [];
|
||||
for (var key in Homescreen.installedApps) {
|
||||
installedAppPaths.push(Homescreen.installedApps[key].path);
|
||||
}
|
||||
|
||||
var original = evt.target.textContent;
|
||||
evt.target.textContent = 'Building gaia... please wait.';
|
||||
setTimeout(function() {
|
||||
if (!Services.prefs.prefHasUserValue(GAIA_DIR_PREF_NAME)) {
|
||||
Homescreen.selectGaiaFolder();
|
||||
}
|
||||
Gaia.profile.remove(true);
|
||||
utils.ensureFolderExists(Gaia.profile);
|
||||
Gaia.config.GAIA_APPDIRS = installedAppPaths.join(' ');
|
||||
Gaia.install();
|
||||
Gaia.launch();
|
||||
evt.target.textContent = original;
|
||||
}, 100);
|
||||
});
|
||||
if (Services.prefs.prefHasUserValue(GAIA_DIR_PREF_NAME)) {
|
||||
var gaiaPath = Services.prefs.getCharPref(GAIA_DIR_PREF_NAME);
|
||||
if (gaiaPath) {
|
||||
try {
|
||||
Gaia.setup(gaiaPath);
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
resetGaiaPath();
|
||||
}
|
||||
} else {
|
||||
resetGaiaPath();
|
||||
}
|
||||
} else {
|
||||
resetGaiaPath();
|
||||
}
|
||||
});
|
||||
|
||||
// FIXME: this is a workaround because we can't use <a href="#something"> to
|
||||
// apply hash tag to url if using about:gaia.
|
||||
if (window.location.href === 'about:gaia') {
|
||||
window.location = 'chrome://gaia-build/content/aboutGaia.html';
|
||||
}
|
||||
171
gaia/tools/extensions/gaia-build@gaiamobile.org/content/gaia.js
Executable file
171
gaia/tools/extensions/gaia-build@gaiamobile.org/content/gaia.js
Executable file
|
|
@ -0,0 +1,171 @@
|
|||
var Gaia = {
|
||||
_listeners: {},
|
||||
buildModules: {},
|
||||
require: null,
|
||||
|
||||
loadXpcshellScript: function gaia_loadXpcshellScript(config, name) {
|
||||
let module =
|
||||
Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
|
||||
for (var i in config) {
|
||||
module[i] = config[i];
|
||||
}
|
||||
Services.scriptloader.loadSubScript(
|
||||
'file:///' + config.GAIA_DIR.replace(/\\/g, '/') + '/build/' + name +
|
||||
'.js', module
|
||||
);
|
||||
return module;
|
||||
},
|
||||
|
||||
getConfig: function gaia_getConfig(profilePath, gaiaPath) {
|
||||
let os = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULRuntime).OS;
|
||||
const SEP = os === 'WINNT' ? '\\' : '/';
|
||||
let config = {};
|
||||
config.GAIA_DIR = gaiaPath;
|
||||
config.PROFILE_DIR = profilePath;
|
||||
config.PROFILE_FOLDER = 'profile-debug';
|
||||
config.GAIA_SCHEME = 'http://';
|
||||
config.GAIA_DOMAIN = 'gaiamobile.org';
|
||||
config.DEBUG = 1;
|
||||
config.DEVICE_DEBUG = 0;
|
||||
config.LOCAL_DOMAINS = 1;
|
||||
config.DESKTOP = 1;
|
||||
config.HOMESCREEN = config.GAIA_SCHEME + 'system.' + config.GAIA_DOMAIN;
|
||||
config.GAIA_PORT = ':8080';
|
||||
config.GAIA_LOCALES_PATH = 'locales';
|
||||
config.LOCALES_FILE = 'shared/resources/languages.json';
|
||||
config.LOCALE_BASEDIR = '';
|
||||
config.BUILD_APP_NAME = '*';
|
||||
config.PRODUCTION = '0';
|
||||
config.GAIA_OPTIMIZE = '0';
|
||||
config.GAIA_DEV_PIXELS_PER_PX = '1';
|
||||
config.DOGFOOD = '0';
|
||||
config.OFFICIAL = '';
|
||||
config.GAIA_DEFAULT_LOCALE = 'en-US';
|
||||
config.GAIA_PRETRANSLATE = '1';
|
||||
config.GAIA_CONCAT_LOCALES = '1';
|
||||
config.GAIA_ENGINE = 'xpcshell';
|
||||
config.TARGET_BUILD_VARIANT = '';
|
||||
config.NOFTU = '1';
|
||||
config.REMOTE_DEBUGGER = '0';
|
||||
config.SETTINGS_PATH = ['build', 'config',
|
||||
'custom-settings.json'].join(SEP);
|
||||
config.GAIA_DISTRIBUTION_DIR = [gaiaPath, 'distribution'].join(SEP);
|
||||
config.GAIA_BUILD_DIR = 'file://' + gaiaPath + '/build/';
|
||||
config.GAIA_KEYBOARD_LAYOUTS = 'en,pt-BR,es,de,fr,pl';
|
||||
return config;
|
||||
},
|
||||
|
||||
getTmpFolder: function gaia_getTmpFolder(name) {
|
||||
let file = Cc['@mozilla.org/file/directory_service;1']
|
||||
.getService(Ci.nsIProperties).get('TmpD', Ci.nsIFile);
|
||||
file.append(name);
|
||||
file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0777', 8));
|
||||
return file;
|
||||
},
|
||||
|
||||
installExtension: function gaia_installExtension(extensionsDir, gaiaPath,
|
||||
extName, domain) {
|
||||
let utils = this.buildModules['utils'];
|
||||
let ext = extensionsDir.clone();
|
||||
let src = utils.getFile(gaiaPath, 'tools', 'extensions', extName);
|
||||
let destname = extName.contains('@') ? extName : extName + '@' + domain;
|
||||
ext.append(destname);
|
||||
utils.writeContent(ext, src.path);
|
||||
},
|
||||
|
||||
setup: function gaia_setup(gaiaPath) {
|
||||
var self = this;
|
||||
if (!gaiaPath) {
|
||||
throw new Error('gaiaPath is empty');
|
||||
}
|
||||
|
||||
this.profile = this.getTmpFolder('gaia');
|
||||
|
||||
// Generate global variables required by build scripts
|
||||
try {
|
||||
this.config = this.getConfig(this.profile.path, gaiaPath);
|
||||
} catch (e) {
|
||||
var msg = 'Can\'t load Gaia configuration, You may choose wrong gaia' +
|
||||
' directory';
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
try {
|
||||
this.require = this.loadXpcshellScript(this.config,
|
||||
'xpcshell-commonjs').require;
|
||||
} catch (e) {
|
||||
throw new Error('xpcshell-commonjs doesn\'t exist, you may choose wrong' +
|
||||
' gaia directory');
|
||||
}
|
||||
|
||||
var modules = ['utils', 'preferences', 'applications-data', 'variant',
|
||||
'webapp-manifests', 'webapp-optimize', 'webapp-zip', 'settings'];
|
||||
modules.forEach(function(m) { self.buildModules[m] = self.require(m); });
|
||||
|
||||
var evt = new CustomEvent('setupFinished');
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
install: function gaia_install() {
|
||||
var self = this;
|
||||
if (!this.config || !this.profile) {
|
||||
throw new Error('config or profile doesn\'t exist');
|
||||
}
|
||||
var config = this.config;
|
||||
|
||||
// Execute all parts of the build system
|
||||
this.buildModules['preferences'].execute(config);
|
||||
this.buildModules['applications-data'].execute(config);
|
||||
this.buildModules['webapp-manifests'].execute(config);
|
||||
this.buildModules['settings'].execute(config);
|
||||
|
||||
// Install all helper addons
|
||||
let extensions = this.profile.clone();
|
||||
extensions.append('extensions');
|
||||
this.buildModules['utils'].ensureFolderExists(extensions);
|
||||
|
||||
let exts = ['desktop-helper', 'browser-helper@gaiamobile.org', 'httpd'];
|
||||
exts.forEach(function(ext) {
|
||||
self.installExtension(extensions, config.GAIA_DIR, ext,
|
||||
config.GAIA_DOMAIN);
|
||||
});
|
||||
},
|
||||
|
||||
launch: function gaia_launch() {
|
||||
// Launch firefox with the custom profile
|
||||
let firefox = Cc['@mozilla.org/file/directory_service;1']
|
||||
.getService(Ci.nsIProperties).get('XREExeF', Ci.nsIFile);
|
||||
let process = Cc['@mozilla.org/process/util;1']
|
||||
.createInstance(Ci.nsIProcess);
|
||||
process.init(firefox);
|
||||
let args = ['-no-remote', '-profile', this.config.PROFILE_DIR];
|
||||
process.run(false, args, args.length);
|
||||
},
|
||||
|
||||
addEventListener: function gaia_addEventListener(type, listener) {
|
||||
if (!this._listeners[type]) {
|
||||
this._listeners[type] = [];
|
||||
}
|
||||
this._listeners[type].push(listener);
|
||||
},
|
||||
|
||||
removeEventListener: function gaia_removeEventListener(type, listener) {
|
||||
if (this._listeners[type]) {
|
||||
this._listeners[type].every(function(li, index) {
|
||||
if (li === listener) {
|
||||
this._listeners.splice(index, 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
dispatchEvent: function gaia_dispatchEvent(event) {
|
||||
if (this._listeners[event.type]) {
|
||||
this._listeners[event.type].forEach(function(listener) {
|
||||
listener(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
22
gaia/tools/extensions/gaia-build@gaiamobile.org/install.rdf
Executable file
22
gaia/tools/extensions/gaia-build@gaiamobile.org/install.rdf
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>gaia-build@gaiamobile.org</em:id>
|
||||
<em:name>gaia-build</em:name>
|
||||
<em:version>0.1</em:version>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>6.0</em:minVersion>
|
||||
<em:maxVersion>32.0.*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:unpack>true</em:unpack>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:description>Build and setup gaia into a firefox instance</em:description>
|
||||
<em:homepageURL>https://github.com/mozilla-b2g/gaia/</em:homepageURL>
|
||||
</Description>
|
||||
</RDF>
|
||||
Loading…
Reference in a new issue