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
492
assets/webapps/costcontrol.gaiamobile.org/js/views/datausage.js
Normal file
492
assets/webapps/costcontrol.gaiamobile.org/js/views/datausage.js
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
/* global debug, ConfigManager, CostControl, Formatting,
|
||||
SimManager, Common, ChartUtils */
|
||||
/* jshint -W120 */
|
||||
|
||||
/*
|
||||
* The data usage tab is in charge of usage charts of mobile and wi-fi networks.
|
||||
*
|
||||
* It has several canvas areas layered one above the others.
|
||||
*/
|
||||
'use strict';
|
||||
var DataUsageTab = (function() {
|
||||
|
||||
var graphicArea, graphicPattern;
|
||||
var backgroundLayer, wifiLayer, mobileLayer, axisLayer, todayLayer,
|
||||
warningLayer, limitsLayer;
|
||||
var wifiOverview, mobileOverview;
|
||||
var wifiToggle, mobileToggle;
|
||||
var appList, noData;
|
||||
|
||||
var costcontrol, initialized, model;
|
||||
|
||||
function setupTab() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
CostControl.getInstance(function _onCostControl(instance) {
|
||||
costcontrol = instance;
|
||||
|
||||
// HTML entities
|
||||
graphicArea = document.getElementById('graphic-area');
|
||||
graphicPattern = document.getElementById('graphic-pattern');
|
||||
backgroundLayer = document.getElementById('background-layer');
|
||||
wifiLayer = document.getElementById('wifi-layer');
|
||||
mobileLayer = document.getElementById('mobile-layer');
|
||||
axisLayer = document.getElementById('axis-layer');
|
||||
todayLayer = document.getElementById('today-layer');
|
||||
warningLayer = document.getElementById('warning-layer');
|
||||
limitsLayer = document.getElementById('limits-layer');
|
||||
wifiOverview = document.getElementById('wifiOverview');
|
||||
mobileOverview = document.getElementById('mobileOverview');
|
||||
wifiToggle = document.getElementById('wifiCheck');
|
||||
mobileToggle = document.getElementById('mobileCheck');
|
||||
appList = document.getElementById('app-usage-list');
|
||||
noData = document.getElementById('app-usage-no-data');
|
||||
|
||||
window.addEventListener('localized', localize);
|
||||
|
||||
// Update and chart visibility
|
||||
document.addEventListener('visibilitychange', updateWhenVisible);
|
||||
wifiToggle.addEventListener('change', toggleWifi);
|
||||
mobileToggle.addEventListener('change', toggleMobile);
|
||||
|
||||
// Setup the model
|
||||
SimManager.requestDataSimIcc(function(dataSimIcc) {
|
||||
ConfigManager.requestSettings(dataSimIcc.iccId,
|
||||
function _onSettings(settings) {
|
||||
debug('First time setup for model');
|
||||
model = {
|
||||
height: ChartUtils.toDevicePixels(graphicArea.clientHeight),
|
||||
width: ChartUtils.toDevicePixels(graphicArea.clientWidth),
|
||||
originX: Math.floor(
|
||||
ChartUtils.toDevicePixels(graphicArea.clientWidth) * 0.15),
|
||||
endX: Math.floor(
|
||||
ChartUtils.toDevicePixels(graphicArea.clientWidth) * 0.95),
|
||||
axis: {
|
||||
Y: {
|
||||
lower: 0,
|
||||
margin: 0.20
|
||||
},
|
||||
X: {
|
||||
lower: ChartUtils.calculateLowerDate(settings),
|
||||
upper: ChartUtils.calculateUpperDate(settings)
|
||||
}
|
||||
},
|
||||
limits: {
|
||||
enabled: settings.dataLimit,
|
||||
value: ChartUtils.getLimitInBytes(settings),
|
||||
dataLimitValue: settings.dataLimitValue,
|
||||
dataLimitUnit: settings.dataLimitUnit
|
||||
},
|
||||
data: {
|
||||
wifi: {
|
||||
enabled: true
|
||||
},
|
||||
mobile: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
todayLabel: {}
|
||||
};
|
||||
ChartUtils.expandModel(model);
|
||||
resetButtonState(settings);
|
||||
|
||||
ConfigManager.observe('dataLimit', toggleDataLimit, true);
|
||||
ConfigManager.observe('dataLimitValue', setDataLimit, true);
|
||||
ConfigManager.observe('lastCompleteDataReset', updateDataUsage, true);
|
||||
ConfigManager.observe('lastDataReset', updateDataUsage, true);
|
||||
ConfigManager.observe('nextReset', changeNextReset, true);
|
||||
|
||||
function finishInit() {
|
||||
initialized = true;
|
||||
updateDataUsage();
|
||||
}
|
||||
|
||||
Common.loadApps()
|
||||
.then(finishInit)
|
||||
.catch(function(reason) {
|
||||
debug(reason);
|
||||
finishInit();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function localize() {
|
||||
if (initialized) {
|
||||
ChartUtils.drawTodayLayer(todayLayer, model);
|
||||
ChartUtils.drawAxisLayer(axisLayer, model, mobileToggle.checked);
|
||||
ChartUtils.drawLimits(limitsLayer, model, mobileToggle.checked);
|
||||
}
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.removeEventListener('visibilitychange', updateWhenVisible);
|
||||
wifiToggle.removeEventListener('click', toggleWifi);
|
||||
mobileToggle.removeEventListener('click', toggleMobile);
|
||||
ConfigManager.removeObserver('dataLimit', toggleDataLimit);
|
||||
ConfigManager.removeObserver('dataLimitValue', setDataLimit);
|
||||
ConfigManager.removeObserver('lastCompleteDataReset', updateDataUsage);
|
||||
ConfigManager.removeObserver('lastDataReset', updateDataUsage);
|
||||
ConfigManager.removeObserver('nextReset', changeNextReset);
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
function resetButtonState(settings) {
|
||||
var isMobileChartVisible = settings.isMobileChartVisible;
|
||||
if (typeof isMobileChartVisible === 'undefined') {
|
||||
isMobileChartVisible = true;
|
||||
}
|
||||
if (isMobileChartVisible !== mobileToggle.checked) {
|
||||
mobileToggle.checked = isMobileChartVisible;
|
||||
toggleMobile();
|
||||
}
|
||||
|
||||
var isWifiChartVisible = settings.isWifiChartVisible;
|
||||
if (typeof isWifiChartVisible === 'undefined') {
|
||||
isWifiChartVisible = false;
|
||||
}
|
||||
if (isWifiChartVisible !== wifiToggle.checked) {
|
||||
wifiToggle.checked = isWifiChartVisible;
|
||||
toggleWifi();
|
||||
}
|
||||
}
|
||||
|
||||
// UPDATES
|
||||
|
||||
// On visibility change
|
||||
function updateWhenVisible(evt) {
|
||||
if (!document.hidden) {
|
||||
updateDataUsage();
|
||||
}
|
||||
}
|
||||
|
||||
function requestDataUsage(perApp) {
|
||||
return new Promise(function(resolve) {
|
||||
SimManager.requestDataSimIcc(function(dataSimIcc) {
|
||||
ConfigManager.requestSettings(dataSimIcc.iccId,
|
||||
function _onSettings(settings) {
|
||||
|
||||
var request = { type: 'datausage' };
|
||||
if (perApp) {
|
||||
request.apps = Common.allApps.map(function(app) {
|
||||
return app.manifestURL;
|
||||
});
|
||||
}
|
||||
|
||||
costcontrol.request(request, resolve);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function maybeRequestPerAppUsage() {
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!model.data.mobile.total) {
|
||||
noData.hidden = false;
|
||||
appList.hidden = true;
|
||||
// Repeated here to not rely on the deferred `updateApps()` call which
|
||||
// would add an annoying delay until clearing the data.
|
||||
clearAppList();
|
||||
|
||||
} else {
|
||||
noData.hidden = true;
|
||||
appList.hidden = false;
|
||||
|
||||
// Bug 1064491: request per-app data usage on the next tick of
|
||||
// the main loop to avoid performance regression in startup
|
||||
setTimeout(function() {
|
||||
requestDataUsage(true).then(updateApps);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCharts(result) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (result.status === 'success') {
|
||||
SimManager.requestDataSimIcc(function(dataSimIcc) {
|
||||
ConfigManager.requestSettings(dataSimIcc.iccId,
|
||||
function _onSettings(settings) {
|
||||
var modelData = result.data;
|
||||
model.data.wifi.samples = modelData.wifi.samples;
|
||||
model.data.wifi.total = modelData.wifi.total;
|
||||
|
||||
model.data.mobile.samples = modelData.mobile.samples;
|
||||
model.data.mobile.total = modelData.mobile.total;
|
||||
|
||||
model.limits.enabled = settings.dataLimit;
|
||||
model.limits.value = ChartUtils.getLimitInBytes(settings);
|
||||
model.limits.dataLimitValue = settings.dataLimitValue;
|
||||
model.axis.X.upper = ChartUtils.calculateUpperDate(settings);
|
||||
model.axis.X.lower = ChartUtils.calculateLowerDate(settings);
|
||||
ChartUtils.expandModel(model);
|
||||
resolve();
|
||||
|
||||
debug('Rendering');
|
||||
drawCharts();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
reject(new Error(
|
||||
'Error requesting data usage. This should not happen.'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateApps(result) {
|
||||
if (result.status === 'success') {
|
||||
model.data.wifi.apps = result.data.wifi.apps;
|
||||
model.data.mobile.apps = result.data.mobile.apps;
|
||||
drawApps(model);
|
||||
}
|
||||
}
|
||||
|
||||
// OBSERVERS
|
||||
|
||||
function toggleDataLimit(value) {
|
||||
model.limits.enabled = value;
|
||||
ChartUtils.drawBackgroundLayer(backgroundLayer, model,
|
||||
mobileToggle.checked);
|
||||
ChartUtils.drawAxisLayer(axisLayer, model, mobileToggle.checked);
|
||||
ChartUtils.drawLimits(limitsLayer, model, mobileToggle.checked);
|
||||
ChartUtils.drawWarningLayer(warningLayer, model);
|
||||
}
|
||||
|
||||
function setDataLimit(value, old, key, settings) {
|
||||
model.limits.value = ChartUtils.getLimitInBytes(settings);
|
||||
model.limits.dataLimitValue = settings.dataLimitValue;
|
||||
ChartUtils.expandModel(model);
|
||||
drawCharts();
|
||||
}
|
||||
|
||||
function updateDataUsage() {
|
||||
requestDataUsage()
|
||||
.then(updateCharts)
|
||||
.then(maybeRequestPerAppUsage);
|
||||
}
|
||||
|
||||
function changeNextReset(value, old, key, settings) {
|
||||
model.axis.X.upper = ChartUtils.calculateUpperDate(settings);
|
||||
model.axis.X.lower = ChartUtils.calculateLowerDate(settings);
|
||||
ChartUtils.expandModel(model);
|
||||
drawCharts();
|
||||
}
|
||||
|
||||
// USER INTERFACE
|
||||
|
||||
// On tapping on wifi toggle
|
||||
function toggleWifi() {
|
||||
var isChecked = wifiToggle.checked;
|
||||
wifiLayer.hidden = !isChecked;
|
||||
// save wifi toggled state
|
||||
ConfigManager.setOption({ isWifiChartVisible: isChecked });
|
||||
|
||||
if (model) {
|
||||
drawApps(model);
|
||||
}
|
||||
}
|
||||
|
||||
// On tapping on mobile toggle
|
||||
function toggleMobile() {
|
||||
var isChecked = mobileToggle.checked;
|
||||
mobileLayer.hidden = !isChecked;
|
||||
warningLayer.hidden = !isChecked;
|
||||
limitsLayer.hidden = !isChecked;
|
||||
// save wifi toggled state
|
||||
ConfigManager.setOption({ isMobileChartVisible: isChecked });
|
||||
|
||||
if (model) {
|
||||
ChartUtils.drawBackgroundLayer(backgroundLayer, model, isChecked);
|
||||
ChartUtils.drawAxisLayer(axisLayer, model, isChecked);
|
||||
ChartUtils.drawLimits(limitsLayer, model, isChecked);
|
||||
drawApps(model);
|
||||
}
|
||||
}
|
||||
|
||||
function drawCharts() {
|
||||
// Update overview
|
||||
var wifiData = Formatting.roundData(model.data.wifi.total);
|
||||
var mobileData = Formatting.roundData(model.data.mobile.total);
|
||||
wifiOverview.textContent = Formatting.formatData(wifiData);
|
||||
mobileOverview.textContent = Formatting.formatData(mobileData);
|
||||
|
||||
// Render the charts
|
||||
ChartUtils.drawBackgroundLayer(backgroundLayer, model,
|
||||
mobileToggle.checked);
|
||||
ChartUtils.drawTodayLayer(todayLayer, model);
|
||||
ChartUtils.drawAxisLayer(axisLayer, model, mobileToggle.checked);
|
||||
ChartUtils.drawDataLayer(wifiLayer, model, 'wifi', {
|
||||
stroke: ChartUtils.WIFI_CHART_STROKE,
|
||||
fill: ChartUtils.WIFI_CHART_FILL
|
||||
});
|
||||
ChartUtils.drawDataLayer(mobileLayer, model, 'mobile', {
|
||||
stroke: ChartUtils.MOBILE_CHART_STROKE,
|
||||
fill: ChartUtils.MOBILE_CHART_FILL,
|
||||
pattern: graphicPattern
|
||||
});
|
||||
ChartUtils.drawWarningLayer(warningLayer, model);
|
||||
ChartUtils.drawLimits(limitsLayer, model, mobileToggle.checked);
|
||||
drawApps(model);
|
||||
}
|
||||
|
||||
var cachedAppItems = {};
|
||||
function drawApps(model) {
|
||||
|
||||
function createAppItem(app) {
|
||||
var isSystem = app.manifestURL === Common.SYSTEM_MANIFEST;
|
||||
var appElement = document.createElement('li');
|
||||
appElement.className = 'app-item';
|
||||
|
||||
var linkElement = document.createElement('a');
|
||||
if (!isSystem) {
|
||||
linkElement.href = '##appdetail-view?manifestURL=' + app.manifestURL;
|
||||
}
|
||||
appElement.appendChild(linkElement);
|
||||
|
||||
var imgElement = document.createElement('img');
|
||||
imgElement.className = 'app-image';
|
||||
imgElement.src = Common.getAppIcon(app);
|
||||
imgElement.setAttribute('role', 'presentation');
|
||||
imgElement.onerror = function() {
|
||||
console.warn('Unable to load icon: ' + this.src);
|
||||
this.src = '/style/images/app/icons/default.png';
|
||||
};
|
||||
linkElement.appendChild(imgElement);
|
||||
|
||||
var appInfoElement = document.createElement('div');
|
||||
appInfoElement.className = 'app-info';
|
||||
linkElement.appendChild(appInfoElement);
|
||||
|
||||
var nameElement = document.createElement('div');
|
||||
nameElement.className = 'app-info-row app-name';
|
||||
nameElement.textContent = Common.getLocalizedAppName(app);
|
||||
appInfoElement.appendChild(nameElement);
|
||||
|
||||
var barElement = document.createElement('div');
|
||||
barElement.className = 'app-info-row app-usage-bar';
|
||||
barElement.setAttribute('role', 'presentation');
|
||||
appInfoElement.appendChild(barElement);
|
||||
|
||||
var usedBarElement = document.createElement('div');
|
||||
usedBarElement.className = 'app-usage-bar-used';
|
||||
usedBarElement.setAttribute('role', 'presentation');
|
||||
barElement.appendChild(usedBarElement);
|
||||
|
||||
var usageElement = document.createElement('div');
|
||||
usageElement.className = 'app-info-row app-usage-total';
|
||||
appInfoElement.appendChild(usageElement);
|
||||
|
||||
return appElement;
|
||||
}
|
||||
|
||||
function updateAppItemUsage(appItem, total) {
|
||||
var usedBarElement =
|
||||
appItem.getElementsByClassName('app-usage-bar-used')[0];
|
||||
var totalElement =
|
||||
appItem.getElementsByClassName('app-usage-total')[0];
|
||||
|
||||
var barTotal = mobileTotal;
|
||||
if (model.limits.enabled && model.limits.value !== null) {
|
||||
barTotal = Math.max(barTotal, model.limits.value);
|
||||
}
|
||||
|
||||
var usedPercent = (total / barTotal) * 100;
|
||||
usedBarElement.style.width = usedPercent + '%';
|
||||
totalElement.textContent = '' + Formatting.formatData(
|
||||
Formatting.roundData(total));
|
||||
}
|
||||
|
||||
// Front-end workaround for Bug 1083680: Noticeable difference between by
|
||||
// application breakdown totals and the total displayed in chart and widget.
|
||||
// This method adds the residual traffic (the traffic that cannot be not
|
||||
// allocated to an app) to the System application.
|
||||
function fixResidualTraffic() {
|
||||
var breakdownTotal = 0;
|
||||
mobileApps[Common.SYSTEM_MANIFEST] = {total: 0};
|
||||
if (manifests.length > 0) {
|
||||
breakdownTotal =
|
||||
manifests.reduce(function(accumulatedTraffic, appManifest) {
|
||||
return accumulatedTraffic + mobileApps[appManifest].total;
|
||||
}, 0);
|
||||
}
|
||||
var residualTraffic = mobileTotal - breakdownTotal;
|
||||
// System traffic is the residual traffic
|
||||
if (residualTraffic > 0) {
|
||||
mobileApps[Common.SYSTEM_MANIFEST].total = residualTraffic;
|
||||
if (!manifests[Common.SYSTEM_MANIFEST]) {
|
||||
manifests.push(Common.SYSTEM_MANIFEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearAppList();
|
||||
|
||||
var mobileTotal = model.data.mobile.total;
|
||||
var mobileApps = model.data.mobile.apps;
|
||||
if (!mobileApps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out apps that have not used any data yet.
|
||||
var manifests = Object.keys(mobileApps).filter(function(key) {
|
||||
return mobileApps[key].total > 0;
|
||||
});
|
||||
|
||||
// Note: The second premise (mobileTotal === 0) must be removed when the
|
||||
// fixResidualTraffic method will be eliminated.
|
||||
if (manifests.length === 0 && mobileTotal === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fixResidualTraffic();
|
||||
|
||||
// Sort by total data usage, descending
|
||||
manifests.sort(function(a, b) {
|
||||
return mobileApps[b].total - mobileApps[a].total;
|
||||
});
|
||||
|
||||
var fragment = document.createDocumentFragment();
|
||||
manifests.forEach(function(manifestURL) {
|
||||
var app = Common.allApps.find(function(app) {
|
||||
return app.manifestURL === manifestURL;
|
||||
});
|
||||
|
||||
if (!app) {
|
||||
debug('No app with manifest URL: ' + manifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
var appTotal = mobileApps[manifestURL].total;
|
||||
var appItem = cachedAppItems[manifestURL];
|
||||
if (!appItem) {
|
||||
appItem = cachedAppItems[manifestURL] = createAppItem(app);
|
||||
}
|
||||
|
||||
updateAppItemUsage(appItem, appTotal);
|
||||
fragment.appendChild(appItem);
|
||||
});
|
||||
|
||||
appList.appendChild(fragment);
|
||||
}
|
||||
|
||||
function clearAppList() {
|
||||
appList.innerHTML = '';
|
||||
}
|
||||
|
||||
return {
|
||||
initialize: setupTab,
|
||||
finalize: finalize
|
||||
};
|
||||
}());
|
||||
|
||||
DataUsageTab.initialize();
|
||||
Loading…
Reference in a new issue