294 lines
9.1 KiB
JavaScript
Executable file
294 lines
9.1 KiB
JavaScript
Executable file
'use strict';
|
|
/* global GaiaGrid, configurator */
|
|
|
|
(function(exports) {
|
|
|
|
var appMgr = navigator.mozApps.mgmt;
|
|
var apps = null;
|
|
|
|
window.performance.mark('homescreenStart@system.gaiamobile.org');
|
|
|
|
appMgr.getAll().onsuccess = function onsuccess(event) {
|
|
apps = event.target.result;
|
|
window.dispatchEvent(new CustomEvent('navigator-mozapps-ready'));
|
|
};
|
|
|
|
/**
|
|
* ApplicationSource is responsible for populating the iniial application
|
|
* results as well as mapping indexedDB records to app objects for launching.
|
|
* @param {Object} store The backing database store class.
|
|
*/
|
|
function ApplicationSource(store) {
|
|
this.store = store;
|
|
this.entries = [];
|
|
this.entriesByManifestUrl = {};
|
|
this.svPreviouslyInstalledApps = [];
|
|
// Store the pending apps to be installed until SingleVariant conf is loaded
|
|
var pendingInstallRequests = [];
|
|
|
|
function addSVEventListener() {
|
|
window.addEventListener('singlevariant-ready', function svFileReady(ev) {
|
|
window.removeEventListener('singlevariant-ready', svFileReady);
|
|
for (var i = 0; i < pendingInstallRequests; i++) {
|
|
pendingInstallRequests[i]();
|
|
}
|
|
});
|
|
}
|
|
|
|
addSVEventListener();
|
|
|
|
var self = this;
|
|
function addIcons() {
|
|
window.removeEventListener('navigator-mozapps-ready', addIcons);
|
|
for (var i = 0, iLen = apps.length; i < iLen; i++) {
|
|
self.makeIcons(apps[i]);
|
|
}
|
|
apps = null;
|
|
}
|
|
|
|
apps ? addIcons() : window.addEventListener('navigator-mozapps-ready',
|
|
addIcons);
|
|
|
|
/**
|
|
* Adds a new application to the layout when the user installed it
|
|
* from market
|
|
*
|
|
* @param {Application} application - The application object
|
|
*/
|
|
function install(application) {
|
|
/* jshint validthis: true */
|
|
|
|
var manifest = application.manifest || application.updateManifest;
|
|
if (manifest.role && app.HIDDEN_ROLES.indexOf(manifest.role) !== -1) {
|
|
return;
|
|
}
|
|
|
|
// There is a last divider that is always in the list, but not rendered
|
|
// unless in edit mode.
|
|
// Remove this divider, append the app, then re-append the divider.
|
|
this.addIconToGrid(application);
|
|
var svApp = configurator.getSingleVariantApp(application.manifestURL);
|
|
var lastElem = app.grid.getIndexLastIcon();
|
|
if (configurator.isSimPresentOnFirstBoot && svApp &&
|
|
svApp.location < lastElem &&
|
|
!this.isPreviouslyInstalled(application.manifestURL)) {
|
|
app.grid.popDivider();
|
|
app.grid.removeNonVisualElements();
|
|
lastElem = app.grid.getIndexLastIcon();
|
|
app.grid.moveTo(lastElem, svApp.location);
|
|
_moveAhead(svApp.location + 1);
|
|
this.addPreviouslyInstalledSvApp(application.manifestURL);
|
|
app.itemStore.savePrevInstalledSvApp(this.svPreviouslyInstalledApps);
|
|
}
|
|
|
|
app.grid.render();
|
|
app.itemStore.deferredSave(app.grid.getItems());
|
|
}
|
|
|
|
/**
|
|
* Goes through all the items in the grid from startPos.
|
|
* If it finds one whose desired position is lower than it's current
|
|
* position then it'll switch it with the item ahead of it on the list
|
|
* (it'll move ahead one position).
|
|
* This is a auxiliary function, if the grid configuration is correct,
|
|
* it'll be sorted after the last item is inserted, although at intermediate
|
|
* steps it can and will be incorrectly sorted.
|
|
* @param {number} startPos - Starting position
|
|
*/
|
|
function _moveAhead(startPos) {
|
|
var elems = app.grid.getItems();
|
|
for (var i = startPos, iLen = elems.length; i < iLen; i++) {
|
|
var item = elems[i];
|
|
//At the moment SV only configures apps
|
|
if (item instanceof GaiaGrid.Mozapp) {
|
|
//elems[i].identifier returns manifestURL IDENTIFIER_SEP entry_point
|
|
var svApp = configurator.getSingleVariantApp(elems[i].identifier);
|
|
if (svApp && i > svApp.location) {
|
|
app.grid.moveTo(i, i - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
appMgr.oninstall = function oninstall(event) {
|
|
if (configurator.isSingleVariantReady) {
|
|
install.bind(this)(event.application);
|
|
} else {
|
|
pendingInstallRequests.push(install.bind(this, event.application));
|
|
}
|
|
}.bind(this);
|
|
|
|
appMgr.onuninstall = function onuninstall(event) {
|
|
var application = event.application;
|
|
var manifest = application.updateManifest || application.manifest;
|
|
|
|
this.removeIconFromGrid(application.manifestURL);
|
|
app.itemStore.deferredSave(app.grid.getItems());
|
|
|
|
if (app.HIDDEN_ROLES.indexOf(manifest.role) !== -1) {
|
|
return;
|
|
}
|
|
}.bind(this);
|
|
|
|
}
|
|
|
|
ApplicationSource.prototype = {
|
|
|
|
/**
|
|
* Synchronizes our local result set with mozApps.
|
|
*/
|
|
synchronize: function() {
|
|
var storeItems = this.store._allItems;
|
|
var toAdd = [];
|
|
|
|
var appIconsByManifestUrl = {};
|
|
for (var i = 0, iLen = storeItems.length; i < iLen; i++) {
|
|
var item = storeItems[i];
|
|
if (!(item instanceof GaiaGrid.Mozapp)) {
|
|
continue;
|
|
}
|
|
appIconsByManifestUrl[item.detail.manifestURL] = item;
|
|
}
|
|
|
|
for (i = 0, iLen = this.entries.length; i < iLen; i++) {
|
|
var entry = this.entries[i];
|
|
var manifest = entry.app.updateManifest || entry.app.manifest;
|
|
|
|
if (!appIconsByManifestUrl[entry.detail.manifestURL] &&
|
|
!manifest.entry_points) {
|
|
toAdd.push(entry);
|
|
} else {
|
|
delete appIconsByManifestUrl[entry.detail.manifestURL];
|
|
}
|
|
}
|
|
|
|
// Check for icons we need to delete
|
|
for (i in appIconsByManifestUrl) {
|
|
this.removeIconFromGrid(i);
|
|
}
|
|
|
|
toAdd.forEach(function _toAdd(newApp) {
|
|
this.addIconToGrid(newApp.app);
|
|
}, this);
|
|
|
|
app.itemStore.deferredSave(app.grid.getItems());
|
|
},
|
|
|
|
/**
|
|
* Adds a new icon to the grid
|
|
*/
|
|
addIconToGrid: function(application) {
|
|
this.makeIcons(application);
|
|
var appObject = this.mapToApp({
|
|
manifestURL: application.manifestURL
|
|
});
|
|
app.grid.appendItemToExpandedGroup(appObject);
|
|
app.grid.render();
|
|
},
|
|
|
|
/**
|
|
* Removes an icon from the grid.
|
|
* @param {String} manifestURL
|
|
*/
|
|
removeIconFromGrid: function(manifestURL) {
|
|
var icons = app.grid.getIcons();
|
|
var appObject = icons[manifestURL];
|
|
if (appObject) {
|
|
appObject.removeFromGrid();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Populates the initial application data from mozApps.
|
|
* @param {Function} success Called after we fetch all initial data.
|
|
*/
|
|
populate: function(success) {
|
|
success(this.entries);
|
|
},
|
|
|
|
/**
|
|
* Creates entries for an app based on hidden roles and entry points.
|
|
*/
|
|
makeIcons: function(eachApp) {
|
|
var manifest = eachApp.manifest || eachApp.updateManifest;
|
|
|
|
if (app.HIDDEN_ROLES.indexOf(manifest.role) !== -1) {
|
|
return;
|
|
}
|
|
|
|
function eachIcon(icon) {
|
|
/* jshint validthis:true */
|
|
|
|
// If there is no icon entry, do not push it onto items.
|
|
if (!icon.icon) {
|
|
return;
|
|
}
|
|
icon.setPosition(this.store.getNextPosition());
|
|
this.entries.push(icon);
|
|
}
|
|
|
|
this.entriesByManifestUrl[eachApp.manifestURL] = eachApp;
|
|
|
|
if (manifest.entry_points) {
|
|
for (var i in manifest.entry_points) {
|
|
eachIcon.call(this, new GaiaGrid.Mozapp(eachApp, i));
|
|
}
|
|
} else {
|
|
eachIcon.call(this, new GaiaGrid.Mozapp(eachApp));
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Maps a database entry to a mozApps application
|
|
*/
|
|
mapToApp: function(entry) {
|
|
// Handle non app objects for applications which exist in our local store
|
|
// but not mozApps.
|
|
var app = this.entriesByManifestUrl[entry.manifestURL];
|
|
app = app || {
|
|
manifestURL: entry.manifestURL,
|
|
manifest: {
|
|
name: '',
|
|
icons: []
|
|
}
|
|
};
|
|
|
|
// The Browser artwork changed with Vulpes branding. Don't keep its
|
|
// decorated icon from an older homescreen database; let mozApps return
|
|
// the manifest icon at the current grid's best available resolution.
|
|
var isVulpesBrowser = /^(?:app:\/\/search\.gaiamobile\.org|http:\/\/search\.localhost(?::\d+)?)\/manifest\.webapp$/
|
|
.test(entry.manifestURL);
|
|
|
|
return new GaiaGrid.Mozapp(app, entry.entryPoint, {
|
|
// cached decorated icon blob to load faster
|
|
decoratedIconBlob: isVulpesBrowser ? null : entry.decoratedIconBlob
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Add a reference to singleVariant app previously installed
|
|
*/
|
|
addPreviouslyInstalledSvApp: function(manifest) {
|
|
this.svPreviouslyInstalledApps.push({manifestURL: manifest});
|
|
},
|
|
|
|
/*
|
|
* Return true if manifest is in the array of installed singleVariant apps,
|
|
* false otherwise
|
|
* @param {string} app's manifest consulted
|
|
*/
|
|
isPreviouslyInstalled: function(manifest) {
|
|
for (var i = 0, elemNum = this.svPreviouslyInstalledApps.length;
|
|
i < elemNum; i++) {
|
|
if (this.svPreviouslyInstalledApps[i].manifestURL === manifest) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
};
|
|
|
|
exports.ApplicationSource = ApplicationSource;
|
|
|
|
}(window));
|