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
644
gaia/apps/ftu/js/late_customization.js
Executable file
644
gaia/apps/ftu/js/late_customization.js
Executable file
|
|
@ -0,0 +1,644 @@
|
|||
/* global URLSearchParams, Navigation, UIManager,
|
||||
IconsHelper, MobileOperator, Sanitizer, LazyLoader */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const MANIFEST_SETTING_NAME = 'latecustomization.url';
|
||||
const OPERATOR_INFO_SETTING_NAME = 'latecustomization.operatorInfo';
|
||||
const COMMS_CHANNEL = 'ftucomms';
|
||||
const ICON_SIZE = 32;
|
||||
const IMG_PATH = 'style/images/late_customization';
|
||||
const ICON_INSTALLING_URL = `${IMG_PATH}/app_installing.svg`;
|
||||
const ICON_FAILED_URL = `${IMG_PATH}/app_install_unrecoverable.svg`;
|
||||
const ICON_DEFAULT_URL = `${IMG_PATH}/default_icon.svg`;
|
||||
|
||||
function LateCustomizationPanel() {
|
||||
this.debug('LateCustomizationPanel ctor');
|
||||
}
|
||||
|
||||
LateCustomizationPanel.prototype = {
|
||||
DEBUG: false,
|
||||
name: 'LateCustomizationPanel',
|
||||
constructor: LateCustomizationPanel,
|
||||
_initialized: false,
|
||||
_downloadPossible: false,
|
||||
|
||||
/* The manifest URL for the customization (comes from mozSettings) */
|
||||
// e.g. https://marketplace-dev.allizom.org/\
|
||||
// api/v2/late-customization/?carrier=telenor®ion=ca
|
||||
_customizationManifestUrl: '',
|
||||
|
||||
get enabled() {
|
||||
return Boolean(this._customizationManifestUrl &&
|
||||
(this._operatorInfo || this._predefinedOperatorInfo));
|
||||
},
|
||||
|
||||
get operatorInfo() {
|
||||
var info = {};
|
||||
Object.assign(info,
|
||||
this._operatorInfo || this.getInfoFromMobileConnection(),
|
||||
this._predefinedOperatorInfo);
|
||||
return info;
|
||||
},
|
||||
|
||||
get carrierName() {
|
||||
var info = this.operatorInfo;
|
||||
return info.carrier || info.operator;
|
||||
},
|
||||
|
||||
get region() {
|
||||
return this.operatorInfo.region || '';
|
||||
},
|
||||
|
||||
start: function() {
|
||||
window.addEventListener('hashchange', this);
|
||||
window.addEventListener('ftu-done', this);
|
||||
window.addEventListener('online', this);
|
||||
window.addEventListener('offline', this);
|
||||
|
||||
this._operatorInfo = this.getInfoFromMobileConnection();
|
||||
this.waitForSettingValues().then((results) => {
|
||||
var [url, predefinedInfo] = results;
|
||||
if (url) {
|
||||
this._customizationManifestUrl = url;
|
||||
}
|
||||
if (predefinedInfo) {
|
||||
// store operator/params separate from
|
||||
// bona-fide mobileconnection details
|
||||
this._predefinedOperatorInfo = predefinedInfo;
|
||||
}
|
||||
this.debug(
|
||||
'start, resolved manifest url: ' + this._customizationManifestUrl,
|
||||
' is enabled? ' + this.enabled,
|
||||
' operatorInfo: ', this._operatorInfo);
|
||||
|
||||
if (this.enabled) {
|
||||
// bail completely if not enabled: don't register step, don't render
|
||||
this.init();
|
||||
} else {
|
||||
this.debug('Not enabled; not initializing');
|
||||
if (!this._operatorInfo) {
|
||||
// keep watching in case we can init later
|
||||
this.waitForOperatorInfo().then((info) => {
|
||||
if (!this._initialized && this.enabled) {
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).catch(e => {
|
||||
this.debug('start: didnt init, error from waitForSettingValues: ', e);
|
||||
});
|
||||
},
|
||||
|
||||
init: function() {
|
||||
// conditions for displaying Late Customization pane
|
||||
// are embodied in this.enabled. If not enabled, init is skipped
|
||||
// and the step is not registered at all.
|
||||
if (this._initialized || !this.enabled) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
this.appManifestUrls = [];
|
||||
this.render();
|
||||
|
||||
Navigation.registerStep({
|
||||
hash: '#late_customization',
|
||||
afterStep: 'firefox_accounts'
|
||||
});
|
||||
|
||||
this.attemptToPopulateAppList();
|
||||
},
|
||||
|
||||
postConnectMessage: function(message) {
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
if (app.connect) {
|
||||
app.connect(COMMS_CHANNEL).then(function onConnAccepted(ports) {
|
||||
ports.forEach(function(port) {
|
||||
port.postMessage(message);
|
||||
});
|
||||
}, function onConnRejected(reason) {
|
||||
console.error('Cannot connect to comms channel: ', reason);
|
||||
});
|
||||
} else {
|
||||
console.error ('mozApps does not have a connect method. ' +
|
||||
'Cannot connect to the comms channel.');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
attemptToPopulateAppList: function() {
|
||||
this.debug('attemptToPopulateAppList, waiting to go online');
|
||||
// start waiting to go online.
|
||||
// We bail if ftu completes first
|
||||
var connected = this.whenUsableConnection();
|
||||
|
||||
connected.then(() => {
|
||||
this.debug('attemptToPopulateAppList, came online');
|
||||
return this.fetchAppsToInstall().then((appsMap) => {
|
||||
if (!(this._appsToInstall && this._appsToInstall.size)) {
|
||||
// no apps, bail early
|
||||
this.debug('No apps in response from ' +
|
||||
this._customizationManifestUrl + ', bailing');
|
||||
return;
|
||||
}
|
||||
var urls = Array.from(this._appsToInstall.keys());
|
||||
var postMessageDetail = {
|
||||
type: 'late-customization-apps',
|
||||
urls: urls
|
||||
};
|
||||
this.postConnectMessage(postMessageDetail);
|
||||
this.updateAppList(this._appsToInstall);
|
||||
}).catch(err => {
|
||||
console.warn('LateCustomizationPanel, error fetching customization ' +
|
||||
'manifest from: ' + this._customizationManifestUrl,
|
||||
err.message);
|
||||
});
|
||||
}).catch((e) => {
|
||||
this.debug('No connection available in time to ' +
|
||||
'fetch late customization manifest:', e);
|
||||
});
|
||||
return connected;
|
||||
},
|
||||
fetchAppsToInstall: function() {
|
||||
if (this._appsToInstall) {
|
||||
return Promise.resolve(this._appsToInstall);
|
||||
}
|
||||
return this.fetchManifest().then((manifestResp) => {
|
||||
var appsMap = this.getAppsFromManifest(manifestResp);
|
||||
if (!appsMap) {
|
||||
return Promise.reject('getAppsFromManifest didnt return appsMap');
|
||||
}
|
||||
return (this._appsToInstall = appsMap);
|
||||
}, (err) => {
|
||||
console.warn('LateCustomization: ', err.message || err);
|
||||
});
|
||||
},
|
||||
|
||||
waitForSettingValues: function() {
|
||||
var haveUrl, haveInfo;
|
||||
var lock = navigator.mozSettings.createLock();
|
||||
|
||||
function getSetting(name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
lock = (lock && !lock.closed) ?
|
||||
lock : navigator.mozSettings.createLock();
|
||||
var get = lock.get(name);
|
||||
get.addEventListener('success', (function() {
|
||||
resolve(get.result[name]);
|
||||
}));
|
||||
get.addEventListener('error', (function() {
|
||||
reject();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (this._customizationManifestUrl) {
|
||||
haveUrl = Promise.resolve(this._customizationManifestUrl);
|
||||
} else {
|
||||
haveUrl = getSetting(MANIFEST_SETTING_NAME);
|
||||
}
|
||||
haveInfo = getSetting(OPERATOR_INFO_SETTING_NAME);
|
||||
|
||||
return Promise.all([haveUrl, haveInfo]);
|
||||
},
|
||||
|
||||
waitForOperatorInfo: function() {
|
||||
var info = this.getInfoFromMobileConnection();
|
||||
if (info) {
|
||||
this._operatorInfo = info;
|
||||
return Promise.resolve(info);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._operatorInfoRequest = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
getInfoFromMobileConnection: function() {
|
||||
var connections = navigator.mozMobileConnections;
|
||||
if (connections) {
|
||||
for (var i=0, conn; i<connections.length; i++) {
|
||||
conn = connections[i];
|
||||
if(conn && conn.voice && conn.voice.connected) {
|
||||
var operatorInfos = MobileOperator.userFacingInfo(conn);
|
||||
if (operatorInfos.operator) {
|
||||
operatorInfos.mcc = conn.voice.network.mcc;
|
||||
operatorInfos.mnc = conn.voice.network.mnc;
|
||||
return operatorInfos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getAppsFromManifest: function(data) {
|
||||
var apps = new Map();
|
||||
var langs = navigator.languages;
|
||||
var list = data && (data.apps || data.objects);
|
||||
if (!(list && list.length)) {
|
||||
return apps;
|
||||
}
|
||||
list.filter((app) => {
|
||||
return app.is_disabled !== true &&
|
||||
(new Set(app.device_types)).has('firefoxos');
|
||||
}).forEach((appData) => {
|
||||
appData.manifestURL = appData.manifest_url;
|
||||
// get locale-appropriate name for user display
|
||||
var appName = '';
|
||||
langs.some(function tryLanguage(lang) {
|
||||
if (this[lang]) {
|
||||
appName = this[lang];
|
||||
// aborts [].some
|
||||
return true;
|
||||
}
|
||||
}, appData.name);
|
||||
|
||||
appData.displayName = appName;
|
||||
this.debug('app data: ', appData.displayName, appData);
|
||||
apps.set(appData.manifestURL, appData);
|
||||
});
|
||||
return apps;
|
||||
},
|
||||
|
||||
attemptInstalls: function() {
|
||||
this._installQueue = [];
|
||||
if (this._appsToInstall && this._appsToInstall.size) {
|
||||
var attempts = [];
|
||||
this._appsToInstall.forEach((appData, manifestURL) => {
|
||||
if (appData.installState == 'installed' ||
|
||||
appData.installState == 'pending') {
|
||||
this.debug('attemptInstalls: app install already underway: ',
|
||||
manifestURL, appData.installState);
|
||||
} else {
|
||||
attempts.push(this.enqueAppInstall(appData));
|
||||
}
|
||||
});
|
||||
return Promise.all(attempts);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
installApp: function(appData) {
|
||||
this.debug('installApp: ', appData.manifestURL);
|
||||
return this.whenUsableConnection().then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
var req = window.navigator.mozApps.install(appData.manifestURL);
|
||||
var onOutcome = (arg) => {
|
||||
var selector = `li[data-manifest-url="${appData.manifestURL}"]`;
|
||||
var itemNode = this.appListNode.querySelector(selector);
|
||||
var manifestURL = req.result && req.result.manifestURL;
|
||||
this.debug('appinstall DOMRequest outcome:', arg, req.result);
|
||||
|
||||
if (req.error) {
|
||||
delete appData.installState;
|
||||
itemNode && itemNode.classList.add('failed');
|
||||
console.warn('Install error: ', req.error);
|
||||
} else if (req.result) {
|
||||
appData.installState = req.result.installState;
|
||||
this.debug('Install success, app: ',
|
||||
manifestURL, appData.installState);
|
||||
}
|
||||
itemNode && itemNode.classList.remove('pending');
|
||||
resolve(req);
|
||||
};
|
||||
req.onsuccess = req.onerror = onOutcome;
|
||||
});
|
||||
}).catch(() => {
|
||||
this.debug('Lost connection while attempting install of ' +
|
||||
appData.manifestURL);
|
||||
});
|
||||
},
|
||||
|
||||
enqueAppInstall: function(app) {
|
||||
var installQueue = this._installQueue;
|
||||
installQueue.push(app);
|
||||
this.debug('enqueAppInstall, queue: ', app.manifestURL);
|
||||
|
||||
var currentInstallAttempt = this._installAttempt || Promise.resolve();
|
||||
return currentInstallAttempt.then(req => {
|
||||
if (req && req.error) {
|
||||
console.warn('install error:', req.error);
|
||||
}
|
||||
if (this._installQueue.length) {
|
||||
this._installAttempt =
|
||||
this.installApp(this._installQueue.shift());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
debug: function() {
|
||||
if (!this.DEBUG) {
|
||||
return;
|
||||
}
|
||||
var args = Array.from(arguments);
|
||||
args.unshift('LateCustomizationPanel');
|
||||
console.log.apply(console, args);
|
||||
},
|
||||
|
||||
buildManifestUrl: function(url, infos={}) {
|
||||
var apiURL = new URL(url);
|
||||
var params = new URLSearchParams(apiURL.search.substr(1));
|
||||
for(var key in infos) {
|
||||
if (typeof infos[key] !== 'undefined') {
|
||||
params.set(key, infos[key]);
|
||||
}
|
||||
}
|
||||
apiURL.search = params.toString();
|
||||
return apiURL.href;
|
||||
},
|
||||
|
||||
fetchManifest: function() {
|
||||
var url = this.buildManifestUrl(this._customizationManifestUrl,
|
||||
this.operatorInfo);
|
||||
if (!url) {
|
||||
return Promise.reject(new Error('No manifest URL available'));
|
||||
}
|
||||
this.debug('fetchManifest at: ' + url);
|
||||
return new Promise((resolve, reject) => {
|
||||
LazyLoader.getJSON(url, true).then((resp) => {
|
||||
if (resp.objects) {
|
||||
resolve(resp);
|
||||
} else {
|
||||
reject('No apps in reponse to: ' + url);
|
||||
}
|
||||
}).catch(ex => {
|
||||
reject(ex);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
whenUsableConnection: function() {
|
||||
if (this._pendingNextOnline) {
|
||||
return this._pendingNextOnline;
|
||||
}
|
||||
var canDownload = navigator.onLine;
|
||||
if (canDownload) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._nextOnlineRequest = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
switch (evt.type) {
|
||||
case 'hashchange':
|
||||
if (this.enabled &&
|
||||
evt.target.location.hash === '#late_customization') {
|
||||
if (this._appsToInstall && this._appsToInstall.size) {
|
||||
this.onPanelShown();
|
||||
} else {
|
||||
Navigation.skipStep();
|
||||
}
|
||||
}
|
||||
// TODO: is some event I can listen to to know the sim is ready
|
||||
// to be asked these questions?
|
||||
if (this._operatorInfoRequest) {
|
||||
var info = this.getInfoFromMobileConnection();
|
||||
if (info) {
|
||||
this._operatorInfo = info;
|
||||
this._operatorInfoRequest.resolve(info);
|
||||
this._operatorInfoRequest = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'online':
|
||||
case 'offline':
|
||||
// install is underway, try to resume
|
||||
if (navigator.onLine &&
|
||||
this._installQueue && this._installQueue.length) {
|
||||
return this.attemptInstalls();
|
||||
}
|
||||
if (navigator.onLine && this._nextOnlineRequest) {
|
||||
this._nextOnlineRequest.resolve(true);
|
||||
} else if (this._nextOnlineRequest) {
|
||||
this._nextOnlineRequest.reject(new Error('offline'));
|
||||
// re-start the wait/fetch/install process
|
||||
if (!this._appsToInstall) {
|
||||
this.attemptToPopulateAppList();
|
||||
}
|
||||
}
|
||||
this._nextOnlineRequest = null;
|
||||
break;
|
||||
case 'ftu-done':
|
||||
// abort anything pending
|
||||
if (this._operatorInfoRequest) {
|
||||
this._operatorInfoRequest.reject(
|
||||
new Error('FTU complete with no viable operator'));
|
||||
this._operatorInfoRequest = null;
|
||||
}
|
||||
if (this._nextOnlineRequest) {
|
||||
this._nextOnlineRequest.reject(
|
||||
new Error('done before connection became available'));
|
||||
this._nextOnlineRequest = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onPanelShown: function() {
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id',
|
||||
'late-customization');
|
||||
this.finalizeRender();
|
||||
this.attemptInstalls();
|
||||
},
|
||||
|
||||
_createElement: function(name, options) {
|
||||
var elm = document.createElement(name);
|
||||
if (options.id) {
|
||||
elm.id = options.id;
|
||||
}
|
||||
if (options.className) {
|
||||
elm.className = options.className;
|
||||
}
|
||||
if (options.dataset) {
|
||||
for(var p in options.dataset) {
|
||||
elm.dataset[p] = options.dataset[p];
|
||||
}
|
||||
}
|
||||
if (options.attributes) {
|
||||
for(var a in options.attributes) {
|
||||
elm.setAttribute(a, options.attributes[a]);
|
||||
}
|
||||
}
|
||||
if ('textContent' in options) {
|
||||
elm.textContent = options.textContent;
|
||||
}
|
||||
return elm;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var section = this.element =
|
||||
document.getElementById('late_customization');
|
||||
if (!section) {
|
||||
section = this._createElement('section', {
|
||||
attributes: { role: 'region' },
|
||||
id: 'late_customization'
|
||||
});
|
||||
this.element = section;
|
||||
}
|
||||
|
||||
section.innerHTML = Sanitizer.escapeHTML `
|
||||
<section id="late-customization-cando" class="content hidden">
|
||||
<p id="late_customization-downloading-message"></p>
|
||||
</section>
|
||||
<section id="late-customization-nocando" class="content">
|
||||
<p id="late_customization-download-later-message"></p>
|
||||
<p id="late_customization-download-later-marketplace"></p>
|
||||
</section>
|
||||
<article data-type="list">
|
||||
<ul id="late_customization-applist" class="hidden"></ul>
|
||||
</article>`;
|
||||
|
||||
function setTranslationById(domId, l10Id, attrs) {
|
||||
var elem = section.querySelector('#' + domId);
|
||||
document.l10n.setAttributes(elem, l10Id, attrs);
|
||||
}
|
||||
|
||||
var carrierName = this.carrierName;
|
||||
if (carrierName) {
|
||||
setTranslationById(
|
||||
'late_customization-downloading-message',
|
||||
'late-customization-downloading-message',
|
||||
{ carrierName: this.carrierName });
|
||||
setTranslationById(
|
||||
'late_customization-download-later-message',
|
||||
'late-customization-download-later',
|
||||
{ carrierName: this.carrierName });
|
||||
} else {
|
||||
// Use an alternate string if we have no carrier name to display
|
||||
setTranslationById(
|
||||
'late_customization-downloading-message',
|
||||
'late-customization-downloading-message-default');
|
||||
setTranslationById(
|
||||
'late_customization-download-later-message',
|
||||
'late-customization-download-later-default');
|
||||
}
|
||||
setTranslationById(
|
||||
'late_customization-download-later-marketplace',
|
||||
'late-customization-download-later-marketplace',
|
||||
{ carrierName: this.carrierName });
|
||||
|
||||
if (!section.parentNode) {
|
||||
document.querySelector('#activation-screen > article')
|
||||
.appendChild(section);
|
||||
}
|
||||
this.appListNode = document.querySelector('#late_customization-applist');
|
||||
},
|
||||
|
||||
renderAppItem: function(appData) {
|
||||
var itemNode = this._createElement('li', {
|
||||
dataset: { manifestUrl: appData.manifestURL },
|
||||
className: 'pending'
|
||||
});
|
||||
var listNode = this.element.querySelector('#late_customization-applist');
|
||||
if (!listNode) {
|
||||
this.debug('No element found to render app details into');
|
||||
return;
|
||||
}
|
||||
|
||||
itemNode.innerHTML = Sanitizer.escapeHTML `
|
||||
<span class="icon"
|
||||
style="background-image: url(${ICON_INSTALLING_URL})"></span>
|
||||
<p dir="auto">${appData.displayName}</p>
|
||||
`;
|
||||
listNode.appendChild(itemNode);
|
||||
var iconElem = itemNode.querySelector('.icon');
|
||||
|
||||
this.getAppIconUrl(appData).then((iconUrl) => {
|
||||
this.debug('getAppIconUrl returned:', iconUrl);
|
||||
var img = new Image();
|
||||
img.onload = () => {
|
||||
iconElem.style.backgroundImage = 'url('+img.src+')';
|
||||
};
|
||||
img.onerror = () => {
|
||||
iconElem.style.backgroundImage = 'url('+ICON_DEFAULT_URL+')';
|
||||
};
|
||||
img.src = iconUrl.href;
|
||||
});
|
||||
return itemNode;
|
||||
},
|
||||
|
||||
getAppIconUrl: function(appData, iconSize = ICON_SIZE) {
|
||||
var siteObj = {};
|
||||
var manifest = appData;
|
||||
var manifestURL = appData.manifest_url;
|
||||
var origin = manifest.origin = new URL(manifestURL).origin;
|
||||
var hasIcons = (manifest.icons && Object.keys(manifest.icons).length);
|
||||
if (!hasIcons) {
|
||||
return Promise.resolve(new URL(ICON_DEFAULT_URL));
|
||||
}
|
||||
//Getting the icons from the FirefoxOS-style manifest
|
||||
siteObj.manifestURL = manifestURL;
|
||||
siteObj.manifest = manifest;
|
||||
var gotIcon = new Promise((resolve, reject) => {
|
||||
IconsHelper.getIcon(origin, iconSize, null, siteObj)
|
||||
.then((iconUrl) => {
|
||||
this.debug('getIcon returned: ', iconUrl);
|
||||
resolve(iconUrl);
|
||||
}).catch(err => {
|
||||
this.debug('Failed to load icon from: ' +
|
||||
JSON.stringify(appData.icons));
|
||||
console.warn('getIcon errback, err: ',err);
|
||||
resolve(new URL(ICON_FAILED_URL));
|
||||
});
|
||||
});
|
||||
return gotIcon;
|
||||
},
|
||||
|
||||
finalizeRender: function() {
|
||||
var container = this.element;
|
||||
var appsMap = this._appsToInstall;
|
||||
var hasApps = (appsMap && appsMap.size);
|
||||
|
||||
var canDoElm = container.querySelector('#late-customization-cando');
|
||||
var noCanDoElm = container.querySelector('#late-customization-nocando');
|
||||
var appListElm = container.querySelector('#late_customization-applist');
|
||||
|
||||
canDoElm.classList.toggle('hidden', !hasApps);
|
||||
appListElm.classList.toggle('hidden', !hasApps);
|
||||
noCanDoElm.classList.toggle('hidden', !!hasApps);
|
||||
},
|
||||
|
||||
updateAppList: function(appsMap) {
|
||||
var list = this.element.querySelector('#late_customization-applist');
|
||||
|
||||
this.debug('updateAppList: ', appsMap);
|
||||
|
||||
while (list.firstChild) {
|
||||
list.firstChild.remove();
|
||||
}
|
||||
|
||||
if (appsMap && appsMap.size) {
|
||||
for (var appData of appsMap.values()) {
|
||||
this.debug('calling renderAppItem with app: ', appData.manifestURL);
|
||||
this.renderAppItem(appData);
|
||||
}
|
||||
}
|
||||
this.finalizeRender();
|
||||
}
|
||||
};
|
||||
|
||||
exports.LateCustomizationPanel = LateCustomizationPanel;
|
||||
var panel;
|
||||
|
||||
document.l10n.ready.then(() => {
|
||||
panel = exports.lateCustomizationPanel = new LateCustomizationPanel();
|
||||
});
|
||||
|
||||
window.addEventListener('ftu-setup', function onSetup(evt) {
|
||||
window.removeEventListener('ftu-setup', onSetup);
|
||||
panel && (panel.manager = evt.detail);
|
||||
panel && panel.start();
|
||||
});
|
||||
|
||||
})(window);
|
||||
Loading…
Reference in a new issue