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
288
gaia/apps/costcontrol/js/settings/autosettings.js
Executable file
288
gaia/apps/costcontrol/js/settings/autosettings.js
Executable file
|
|
@ -0,0 +1,288 @@
|
|||
/* global debug */
|
||||
/* exported AutoSettings */
|
||||
|
||||
'use strict';
|
||||
|
||||
var AutoSettings = (function() {
|
||||
|
||||
var OPTION_CONFIGURERS = {
|
||||
'select': function _selectConfigurer(guiWidget) {
|
||||
var optionName = guiWidget.dataset.option;
|
||||
var span, parent = guiWidget.parentElement;
|
||||
if (parent.classList.contains('fake-select')) {
|
||||
var firstChild = parent.firstChild;
|
||||
if (!firstChild || firstChild.tagName !== 'SPAN') {
|
||||
span = document.createElement('span');
|
||||
parent.insertBefore(span, parent.firstChild);
|
||||
}
|
||||
}
|
||||
guiWidget.addEventListener('blur', function _onSelectChange() {
|
||||
debug('Value:', guiWidget.value);
|
||||
settings.option(optionName, guiWidget.value);
|
||||
});
|
||||
settings.observe(optionName, function _onOptionChange(value) {
|
||||
if (value === undefined) {
|
||||
value = settings.defaultValue(optionName);
|
||||
}
|
||||
guiWidget.value = value;
|
||||
if (span) {
|
||||
var selected = guiWidget.options[guiWidget.selectedIndex];
|
||||
var l10nId = selected.getAttribute('data-l10n-id');
|
||||
span.textContent = selected.textContent;
|
||||
if (l10nId) {
|
||||
span.setAttribute('data-l10n-id', l10nId);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// Select is used to simulate custom combo boxes. It displays a selection
|
||||
// dialog with an ok button and possibly a cancel button. The value is only
|
||||
// changed if ok button is pressed. If cancel provided and clicked then
|
||||
// the former value is restored.
|
||||
'customselect': function _customselectConfigurer(guiWidget) {
|
||||
|
||||
var dialog = document.getElementById(guiWidget.dataset.selectdialog);
|
||||
var optionName = guiWidget.dataset.option;
|
||||
|
||||
// Dialog
|
||||
|
||||
var okButton = dialog.querySelector('button.recommend');
|
||||
if (okButton) {
|
||||
okButton.addEventListener('click', function _onDialogOk() {
|
||||
var checked = dialog.querySelector('input[type="radio"]:checked');
|
||||
settings.option(optionName, checked.value);
|
||||
vmanager.closeCurrentView();
|
||||
});
|
||||
}
|
||||
|
||||
var cancelButton = dialog.querySelector('button.cancel');
|
||||
if (cancelButton) {
|
||||
cancelButton.addEventListener('click',
|
||||
function _onDialogCancel() {
|
||||
var currentValue = settings.option(optionName);
|
||||
settings.option(optionName, currentValue);
|
||||
vmanager.closeCurrentView();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
guiWidget.addEventListener('click', function _onWidgetClick() {
|
||||
vmanager.changeViewTo(dialog.id);
|
||||
});
|
||||
|
||||
// Keep the widget and the dialog synchronized
|
||||
settings.observe(optionName,
|
||||
function _onOptionChange(value) {
|
||||
|
||||
// Use default value if no value
|
||||
if (value === null || value === undefined) {
|
||||
value = getDefaultValue(optionName);
|
||||
}
|
||||
|
||||
var radio =
|
||||
dialog.querySelector('input[type="radio"][value="' + value + '"]');
|
||||
|
||||
if (!radio) {
|
||||
value = getDefaultValue(optionName);
|
||||
radio = dialog.querySelector('input[type="radio"][value="' +
|
||||
value + '"]');
|
||||
}
|
||||
radio.checked = true;
|
||||
|
||||
var textSpan = dialog.querySelector('input:checked + span');
|
||||
var tagSpan = guiWidget.querySelector('.tag');
|
||||
tagSpan.textContent = textSpan.textContent;
|
||||
}
|
||||
);
|
||||
|
||||
// Keep the UI localized
|
||||
window.addEventListener('localized', function _onLocalized() {
|
||||
var textSpan = dialog.querySelector('input:checked + span');
|
||||
var tagSpan = guiWidget.querySelector('.tag');
|
||||
tagSpan.textContent = textSpan.textContent;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
// Switch configurer handles an checkbox. When it changes, the checked
|
||||
// state is used as the content for the option.
|
||||
'switch' : function _switchConfigurer(guiWidget) {
|
||||
var optionName = guiWidget.dataset.option;
|
||||
|
||||
// Add an observer to keep synchronization
|
||||
settings.observe(
|
||||
optionName,
|
||||
function _onOptionChange(value) {
|
||||
if (value === null || value === undefined) {
|
||||
value = getDefaultValue(optionName);
|
||||
}
|
||||
guiWidget.checked = value;
|
||||
}
|
||||
);
|
||||
|
||||
// Add an event listener to switch the option
|
||||
guiWidget.addEventListener('change', function ccapp_onSwitchChange() {
|
||||
settings.option(optionName, guiWidget.checked);
|
||||
});
|
||||
},
|
||||
|
||||
// Input configurer handles an input box. When the content changes, the
|
||||
// content is parsed and sent to the proper option.
|
||||
'input' : function _inputConfigurer(guiWidget) {
|
||||
var optionName = guiWidget.dataset.option;
|
||||
|
||||
// Add an observer to keep synchronization
|
||||
settings.observe(
|
||||
optionName,
|
||||
function _onOptionChange(value) {
|
||||
if (value === null || value === undefined) {
|
||||
value = getDefaultValue(optionName);
|
||||
}
|
||||
guiWidget.value = value;
|
||||
}
|
||||
);
|
||||
|
||||
// Add an event listener to switch the option
|
||||
guiWidget.addEventListener('change', function _onContentChange() {
|
||||
var value = guiWidget.value;
|
||||
if (guiWidget.type === 'number') {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
settings.option(optionName, value);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Add a new type with its configuration function
|
||||
function addType(newType, configFunction) {
|
||||
OPTION_CONFIGURERS[newType] = configFunction;
|
||||
}
|
||||
|
||||
var customRecognizer;
|
||||
// Add a recognizer to detect new types
|
||||
function setCustomRecognizer(recognizer) {
|
||||
customRecognizer = recognizer;
|
||||
}
|
||||
|
||||
// Return the <li> wrapping the option
|
||||
function getEntryParent(item) {
|
||||
while (item && item.tagName !== 'LI') {
|
||||
item = item.parentNode;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// Return the default value for an option
|
||||
function getDefaultValue(optionName) {
|
||||
return settings.defaultValue(optionName);
|
||||
}
|
||||
|
||||
// Configure the web page
|
||||
var settings, vmanager;
|
||||
function initialize(settingsInterface, viewManager, root) {
|
||||
|
||||
root = root || 'body';
|
||||
settings = settingsInterface;
|
||||
vmanager = viewManager;
|
||||
|
||||
// Return the type of configurer needed based on the properties of
|
||||
// the HTML element marked as the option.
|
||||
function getWidgetType(widget) {
|
||||
if (widget.dataset.widgetType) {
|
||||
return widget.dataset.widgetType;
|
||||
}
|
||||
|
||||
var customType;
|
||||
if (customRecognizer === 'function') {
|
||||
customType = customRecognizer(widget, settings, vmanager);
|
||||
}
|
||||
|
||||
if (customType) {
|
||||
return customType;
|
||||
}
|
||||
|
||||
if (widget.tagName === 'SELECT') {
|
||||
return 'select';
|
||||
}
|
||||
|
||||
if (typeof widget.dataset.selectdialog !== 'undefined') {
|
||||
return 'customselect';
|
||||
}
|
||||
|
||||
if (widget.type === 'checkbox' ||
|
||||
widget.nodeName.toLowerCase() === 'gaia-switch') {
|
||||
return 'switch';
|
||||
}
|
||||
|
||||
if (['text', 'number'].indexOf(widget.type) !== -1) {
|
||||
return 'input';
|
||||
}
|
||||
}
|
||||
|
||||
// Install a dependency based on an expression. When expression is true
|
||||
// call the callback.
|
||||
function installDependency(expression, callback) {
|
||||
var equality = false;
|
||||
var parsed = expression.split('!=');
|
||||
if (parsed.length === 1) {
|
||||
parsed = expression.split('=');
|
||||
equality = true;
|
||||
}
|
||||
|
||||
var dependency = parsed[0];
|
||||
var referenceValue = parsed[1];
|
||||
settings.observe(dependency,
|
||||
function ccapp_dependencyAction(value) {
|
||||
var test = (('' + value) === referenceValue);
|
||||
callback(equality === test); // or is an equality test and values are
|
||||
// equal or is an inequality test and
|
||||
// values are different.
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Widgets
|
||||
var allGUIWidgets = document.querySelectorAll(root + ' .settings-option');
|
||||
[].forEach.call(allGUIWidgets, function _forEachWidget(guiWidget) {
|
||||
|
||||
var type = getWidgetType(guiWidget);
|
||||
var entry = getEntryParent(guiWidget);
|
||||
var configurer = OPTION_CONFIGURERS[type];
|
||||
if (configurer) {
|
||||
configurer(guiWidget, settings, vmanager, root);
|
||||
}
|
||||
|
||||
// Simple dependency resolution:
|
||||
|
||||
// enable / disable some options depending on the values of other
|
||||
var disableOn = guiWidget.dataset.disableOn;
|
||||
if (disableOn) {
|
||||
installDependency(disableOn, function _disable(passed) {
|
||||
guiWidget.disabled = passed;
|
||||
if (entry) {
|
||||
entry.setAttribute('aria-disabled', passed + '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// hide / show some options depending on the values of other
|
||||
var hideOn = guiWidget.dataset.hideOn;
|
||||
if (hideOn) {
|
||||
installDependency(hideOn, function _hide(passed) {
|
||||
guiWidget.hidden = passed;
|
||||
if (entry) {
|
||||
entry.hidden = passed;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
initialize: initialize,
|
||||
setCustomRecognizer: setCustomRecognizer,
|
||||
addType: addType
|
||||
};
|
||||
|
||||
}());
|
||||
234
gaia/apps/costcontrol/js/settings/limitdialog.js
Executable file
234
gaia/apps/costcontrol/js/settings/limitdialog.js
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
/* global _, addNetworkUsageAlarm, Common, Formatting, SimManager,
|
||||
CostControl */
|
||||
/* exported dataLimitConfigurer */
|
||||
'use strict';
|
||||
|
||||
function dataLimitConfigurer(guiWidget, settings, viewManager, widgetRoot) {
|
||||
var REMOVE_CHAR_CODE = 8,
|
||||
// It Represents any positive real number (up to three digits length),
|
||||
// with optional decimal point, accepting up to 2 decimal places. It is
|
||||
// not necessary adding the significant number when it is zero (eg. '.5'
|
||||
// is a valid entry)
|
||||
DATA_LIMIT_NUMERIC_FORMAT = new RegExp('^[0-9]{0,3}(\\.[0-9]{1,2})?$');
|
||||
|
||||
|
||||
var dialog = document.getElementById('data-limit-dialog');
|
||||
var dataLimitInput = dialog.querySelector('input');
|
||||
var format = function ccal_formatterDataUnit(value) {
|
||||
var unit = settings.option('dataLimitUnit');
|
||||
return Formatting.formatData([value, _(unit)]);
|
||||
};
|
||||
|
||||
window.addEventListener('localized', function _onLocalize() {
|
||||
var tagSpan = guiWidget.querySelector('.tag');
|
||||
tagSpan.textContent = format(dataLimitInput.value);
|
||||
});
|
||||
|
||||
// Configure dialog
|
||||
var okButton = dialog.querySelector('button.recommend');
|
||||
if (okButton) {
|
||||
okButton.addEventListener('click', function ccld_onDialogOk() {
|
||||
var value = parseFloat(dataLimitInput.value);
|
||||
settings.option('dataLimitValue', value);
|
||||
settings.option('dataLimitUnit', currentUnit);
|
||||
var dataLimit = Common.getDataLimit({'dataLimitValue': value,
|
||||
'dataLimitUnit': currentUnit});
|
||||
SimManager.requestDataSimIcc(function(dataSim) {
|
||||
var simInterface = Common.getDataSIMInterface(dataSim.iccId);
|
||||
addNetworkUsageAlarm(simInterface, dataLimit, null,
|
||||
() => checkDataLimit(dataLimit));
|
||||
});
|
||||
viewManager.closeCurrentView();
|
||||
});
|
||||
}
|
||||
|
||||
var dialogHeader = dialog.querySelector('#limit-dialog-header');
|
||||
if (dialogHeader) {
|
||||
dialogHeader.addEventListener('action',
|
||||
function ccld_onDialogCancel() {
|
||||
var oldValue = settings.option('dataLimitValue');
|
||||
var oldUnit = settings.option('dataLimitUnit');
|
||||
currentUnit = oldUnit;
|
||||
settings.option('dataLimitValue', oldValue);
|
||||
settings.option('dataLimitUnit', oldUnit);
|
||||
dataLimitInput.classList.remove('error');
|
||||
okButton.disabled = false;
|
||||
viewManager.closeCurrentView();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function checkDataLimit(dataLimit) {
|
||||
CostControl.getInstance(function _onCostControl(costcontrol) {
|
||||
var requestObj = {
|
||||
type: 'datausage'
|
||||
};
|
||||
costcontrol.request(requestObj, function _onDataStats(result) {
|
||||
var stats = result.data.mobile.total;
|
||||
if (stats > dataLimit) {
|
||||
var mobileUsage = {
|
||||
limit: Formatting.formatData(Formatting.roundData(dataLimit))
|
||||
};
|
||||
var message = _('data-limit-notification-title2', mobileUsage) +
|
||||
'\n' + _('data-limit-notification-text2');
|
||||
window.alert(message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isNotRemovingChar(newChar) {
|
||||
return REMOVE_CHAR_CODE !== newChar;
|
||||
}
|
||||
|
||||
function isAllowedValue(newValue) {
|
||||
return isValidValue(newValue) || wouldBeValid(newValue);
|
||||
}
|
||||
// Checks if value is numeric and it has a valid format
|
||||
// Not allowed entry characters '-', ',' or more than one '.'
|
||||
function isValidValue(newValue) {
|
||||
var containsNonValidCharacters = [',','-']
|
||||
.some(c => newValue.indexOf(c) > -1);
|
||||
var containsMoreThanOneDot = (newValue.match(/\./g) || []).length > 1;
|
||||
return !Number.isNaN(newValue) &&
|
||||
!containsNonValidCharacters && !containsMoreThanOneDot &&
|
||||
isValidFormat(newValue);
|
||||
}
|
||||
|
||||
function wouldBeValid(lowLimitValue) {
|
||||
var allowedEntry = new RegExp('^0?(\\.0?)?$');
|
||||
return allowedEntry.test(lowLimitValue);
|
||||
}
|
||||
|
||||
function preventBadInput(valid, newChar, currentInput) {
|
||||
// This method returns the string resultant of adding the character of the
|
||||
// keypress event
|
||||
function simulateNewValue(newChar, currentInput) {
|
||||
var value = currentInput.value,
|
||||
idx = currentInput.selectionStart,
|
||||
newCharValue = String.fromCharCode(newChar);
|
||||
return value.substr(0, idx) + newCharValue + value.substr(idx);
|
||||
}
|
||||
var newValue = simulateNewValue(newChar, currentInput);
|
||||
var isInvalid = !valid(newValue);
|
||||
return isInvalid;
|
||||
}
|
||||
|
||||
dataLimitInput.addEventListener('keypress',
|
||||
function cc_onKeypress(event) {
|
||||
var currentInput = event.target;
|
||||
var newChar = event.charCode || event.keyCode;
|
||||
|
||||
// If the entry is not valid or the result of the input is invalid, we
|
||||
// have to prevent the event propagation
|
||||
if (isNotRemovingChar(newChar) &&
|
||||
preventBadInput(isAllowedValue, newChar, currentInput)) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function isValidFormat(lowLimitValue) {
|
||||
var numberDataLimit = Number(lowLimitValue);
|
||||
return (numberDataLimit < 1000 &&
|
||||
numberDataLimit > 0 &&
|
||||
DATA_LIMIT_NUMERIC_FORMAT.test(numberDataLimit));
|
||||
}
|
||||
|
||||
// Disable OK button when dataLimitInput not matches any positive real number
|
||||
// (up to three digits length), with optional decimal point, accepting up
|
||||
// to 2 decimal places.
|
||||
dataLimitInput.addEventListener('input',
|
||||
function cc_ondataLimitInputChange(evt) {
|
||||
var lowLimitValue = evt.target.value.trim();
|
||||
var isNumericLowLimit = !Number.isNaN(Number(lowLimitValue));
|
||||
|
||||
if (isNumericLowLimit &&
|
||||
lowLimitValue.indexOf('.0') !== lowLimitValue.length - 2 &&
|
||||
lowLimitValue.indexOf('.') !== lowLimitValue.length - 1) {
|
||||
dataLimitInput.value = Number(lowLimitValue);
|
||||
}
|
||||
|
||||
var isValidValue = (isNumericLowLimit && isValidFormat(lowLimitValue));
|
||||
|
||||
if (isValidValue) {
|
||||
dataLimitInput.classList.remove('error');
|
||||
} else {
|
||||
dataLimitInput.classList.add('error');
|
||||
}
|
||||
okButton.disabled = (!isValidValue);
|
||||
}
|
||||
);
|
||||
|
||||
// Configure the swicth unit button
|
||||
var currentUnit = settings.option('dataLimitUnit');
|
||||
var switchUnitButton = dialog.querySelector('.switch-unit-button');
|
||||
function localizeSwitchUnitButton(unit) {
|
||||
switchUnitButton.setAttribute('data-l10n-id', 'unit-' + unit);
|
||||
switchUnitButton.querySelector('span.tag').setAttribute(
|
||||
'data-l10n-id', unit);
|
||||
}
|
||||
|
||||
localizeSwitchUnitButton(currentUnit);
|
||||
switchUnitButton.addEventListener('click',
|
||||
function ccapp_switchUnit() {
|
||||
currentUnit = (currentUnit === 'MB') ? 'GB' : 'MB';
|
||||
localizeSwitchUnitButton(currentUnit);
|
||||
}
|
||||
);
|
||||
|
||||
// Prevent to loose the focus when tapping on switch unit button
|
||||
// TODO: Replace with 'focusout' on dataLimitInput when fixing bug:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=687787
|
||||
switchUnitButton.addEventListener('mousedown',
|
||||
function _preventFocusLost(evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
);
|
||||
|
||||
// Keep the widget and the dialog synchronized
|
||||
settings.observe('dataLimitValue',
|
||||
function ccld_onValueChange(value) {
|
||||
// Use default value if no value
|
||||
if (value === null || typeof value === 'undefined') {
|
||||
value = settings.defaultValue('dataLimitValue');
|
||||
}
|
||||
|
||||
// Set dialog
|
||||
dataLimitInput.value = value;
|
||||
|
||||
var tagSpan = guiWidget.querySelector('.tag');
|
||||
tagSpan.textContent = format(dataLimitInput.value);
|
||||
}
|
||||
);
|
||||
|
||||
settings.observe('dataLimitUnit',
|
||||
function ccld_onUnitChange(value) {
|
||||
|
||||
// Use default value if no value
|
||||
if (value === null || typeof value === 'undefined') {
|
||||
value = settings.defaultValue('dataLimitUnit');
|
||||
}
|
||||
|
||||
// Set dialog
|
||||
localizeSwitchUnitButton(value);
|
||||
|
||||
var tagSpan = guiWidget.querySelector('.tag');
|
||||
tagSpan.textContent = format(dataLimitInput.value);
|
||||
}
|
||||
);
|
||||
|
||||
// Show the dialog
|
||||
var oldUnitValue;
|
||||
guiWidget.addEventListener('click', function ccld_onWidgetClick() {
|
||||
viewManager.changeViewTo(dialog.id, widgetRoot);
|
||||
dataLimitInput.focus();
|
||||
dataLimitInput.setSelectionRange(dataLimitInput.value.length,
|
||||
dataLimitInput.value.length);
|
||||
oldUnitValue = settings.option('dataLimitUnit');
|
||||
localizeSwitchUnitButton(oldUnitValue);
|
||||
});
|
||||
|
||||
}
|
||||
89
gaia/apps/costcontrol/js/settings/networkUsageAlarm.js
Executable file
89
gaia/apps/costcontrol/js/settings/networkUsageAlarm.js
Executable file
|
|
@ -0,0 +1,89 @@
|
|||
/* exported NetworkUsageAlarm */
|
||||
'use strict';
|
||||
var NetworkUsageAlarm = (function() {
|
||||
|
||||
function _addAlarm(networkInterface, value, onsuccess, onerror) {
|
||||
if (!networkInterface) {
|
||||
console.error('Error, the network interface is not defined when trying ' +
|
||||
'to add an alarm');
|
||||
(typeof onerror === 'function') && onerror();
|
||||
return;
|
||||
}
|
||||
if (value === null || typeof value === 'undefined') {
|
||||
console.error('Limit value is undefined, impossible add an alarm');
|
||||
(typeof onerror === 'function') && onerror();
|
||||
return;
|
||||
}
|
||||
|
||||
var request = navigator.mozNetworkStats.addAlarm(networkInterface, value);
|
||||
request.onsuccess = onsuccess;
|
||||
request.onerror = function error(e) {
|
||||
console.error('Error, when trying to addAlarm to the interfaceId: ' +
|
||||
networkInterface.id + ' and limit: ' + value);
|
||||
(typeof onerror === 'function') && onerror();
|
||||
};
|
||||
}
|
||||
|
||||
function _clearAlarms(networkInterface, callback) {
|
||||
if (!networkInterface) {
|
||||
console.error('Error, the network interface is not defined when trying ' +
|
||||
'to remove alarms');
|
||||
return;
|
||||
}
|
||||
var request = navigator.mozNetworkStats.getAllAlarms(networkInterface);
|
||||
request.onsuccess = function success() {
|
||||
var alarmList = request.result;
|
||||
var pendingRequest = alarmList.length;
|
||||
|
||||
function checkForCompletion() {
|
||||
pendingRequest--;
|
||||
if (pendingRequest === 0) {
|
||||
(typeof callback === 'function') && callback();
|
||||
}
|
||||
}
|
||||
|
||||
function _onErrorRemoveAlarm() {
|
||||
console.error('Error when trying to remove one alarm.');
|
||||
checkForCompletion();
|
||||
}
|
||||
|
||||
for (var i = 0; i < alarmList.length; i++) {
|
||||
if (alarmList[i] && alarmList[i].alarmId) {
|
||||
var rA = navigator.mozNetworkStats.removeAlarms(alarmList[i].alarmId);
|
||||
rA.onsuccess = checkForCompletion;
|
||||
rA.onerror = _onErrorRemoveAlarm;
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingRequest === 0) {
|
||||
(typeof callback === 'function') && callback();
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function error() {
|
||||
console.error('Error when trying to get alarms from the API to clear it');
|
||||
};
|
||||
}
|
||||
|
||||
function _updateAlarm(dataInterface, value, onsuccess, onerror) {
|
||||
if (dataInterface && value) {
|
||||
var addNewAlarm = _addAlarm.bind(null, dataInterface, value, onsuccess,
|
||||
onerror);
|
||||
_clearAlarms(dataInterface, addNewAlarm);
|
||||
} else {
|
||||
if (!dataInterface) {
|
||||
console.error('Error, the network interface is not defined when ' +
|
||||
'trying to update an alarm');
|
||||
} else {
|
||||
console.error('Error, the data limit value is not defined when ' +
|
||||
'trying to update an alarm');
|
||||
}
|
||||
(typeof onerror === 'function') && onerror();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
updateAlarm: _updateAlarm,
|
||||
clearAlarms: _clearAlarms
|
||||
};
|
||||
}());
|
||||
367
gaia/apps/costcontrol/js/settings/settings.js
Executable file
367
gaia/apps/costcontrol/js/settings/settings.js
Executable file
|
|
@ -0,0 +1,367 @@
|
|||
/* global BalanceView, LazyLoader, AutoSettings, BalanceLowLimitView,
|
||||
ResetMenuDialog, ConfirmDialog, ViewManager, dataLimitConfigurer,
|
||||
Formatting
|
||||
*/
|
||||
/* exported debug, sendBalanceThresholdNotification */
|
||||
/*
|
||||
* Settings is in charge of setup the setting section. It uses an AutoSettings
|
||||
* object to automatically bind markup with local settings.
|
||||
*
|
||||
* Settings have three drawing areas with views for current values of balance,
|
||||
* data usage and telephony.
|
||||
*/
|
||||
'use strict';
|
||||
// Import global objects from parent window
|
||||
var ConfigManager = window.parent.ConfigManager;
|
||||
var CostControl = window.parent.CostControl;
|
||||
var SimManager = window.parent.SimManager;
|
||||
var Common = window.parent.Common;
|
||||
var NetworkUsageAlarm = window.parent.NetworkUsageAlarm;
|
||||
|
||||
// Import global functions from parent window
|
||||
var addNetworkUsageAlarm = window.parent.addNetworkUsageAlarm;
|
||||
var resetTelephony = window.parent.resetTelephony;
|
||||
navigator.mozL10n = window.parent.navigator.mozL10n;
|
||||
|
||||
|
||||
// Import debug
|
||||
var DEBUGGING = window.parent.DEBUGGING;
|
||||
var debug = window.parent.debug;
|
||||
|
||||
var Settings = (function() {
|
||||
|
||||
var costcontrol, vmanager, initialized, endLoadSettingsNotified;
|
||||
var plantypeSelector, phoneActivityTitle, phoneActivitySettings;
|
||||
var balanceTitle, balanceSettings, reportsTitle;
|
||||
var balanceView, resetMenuDialog, confirmDialog;
|
||||
|
||||
function configureUI() {
|
||||
CostControl.getInstance(function _onCostControl(instance) {
|
||||
costcontrol = instance;
|
||||
|
||||
// Debug options
|
||||
if (DEBUGGING) {
|
||||
loadDeveloperAids();
|
||||
}
|
||||
|
||||
vmanager = new ViewManager();
|
||||
|
||||
// HTML entities
|
||||
plantypeSelector = document.getElementById('plantype-settings');
|
||||
phoneActivityTitle = document.getElementById('phone-activity-settings');
|
||||
phoneActivitySettings =
|
||||
document.querySelector('#phone-activity-settings + .settings');
|
||||
balanceTitle = document.getElementById('balance-settings');
|
||||
balanceSettings =
|
||||
document.querySelector('#balance-settings + .settings');
|
||||
reportsTitle = document.getElementById('phone-internet-settings');
|
||||
|
||||
// Subviews
|
||||
var balanceConfig = ConfigManager.configuration.balance;
|
||||
balanceView = new BalanceView(
|
||||
document.getElementById('balance'),
|
||||
document.querySelector('#balance + .meta'),
|
||||
balanceConfig ? balanceConfig.minimum_delay : undefined
|
||||
);
|
||||
|
||||
var resetDialog = document.getElementById('reset-dialog');
|
||||
var resetDataDialog = document.getElementById('reset-data-dialog');
|
||||
|
||||
resetMenuDialog = new ResetMenuDialog(resetDataDialog, vmanager);
|
||||
confirmDialog = new ConfirmDialog(resetDialog, vmanager);
|
||||
resetMenuDialog.initializeResetModes(confirmDialog);
|
||||
|
||||
// Autosettings
|
||||
AutoSettings.addType('data-limit', dataLimitConfigurer);
|
||||
AutoSettings.initialize(ConfigManager, vmanager, '#settings-view');
|
||||
configureTelephonyReset();
|
||||
configureDataResets();
|
||||
addDoneConstrains();
|
||||
|
||||
// Add an observer on dataLimit switch to active o deactivate alarms
|
||||
ConfigManager.observe(
|
||||
'dataLimit',
|
||||
function _onDataLimitChange(value, old, key, settings) {
|
||||
SimManager.requestDataSimIcc(function(dataSim) {
|
||||
var iccId = dataSim.iccId;
|
||||
var currentDataInterface = Common.getDataSIMInterface(iccId);
|
||||
if (!value) {
|
||||
NetworkUsageAlarm.clearAlarms(currentDataInterface);
|
||||
} else {
|
||||
addNetworkUsageAlarm(currentDataInterface,
|
||||
Common.getDataLimit(settings));
|
||||
}
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// Update layout when changing plantype
|
||||
ConfigManager.observe('plantype', updateUI, true);
|
||||
|
||||
// Update views
|
||||
ConfigManager.observe('lastTelephonyActivity',
|
||||
function _updateTelephony(activity, old, key, settings) {
|
||||
updateTelephony(activity, settings.lastTelephonyReset);
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
ConfigManager.observe('lastDataUsage',
|
||||
function _updateDataUsage(stats, old, key, settings) {
|
||||
updateDataUsage(stats, settings.lastCompleteDataReset);
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
ConfigManager.observe('lastBalance',
|
||||
function _updateBalance(balance) {
|
||||
updateBalance(balance, balance.currency);
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// Close button needs to acquire a reference to the settings view
|
||||
// manager to close itself.
|
||||
var close = document.getElementById('close-settings');
|
||||
close.addEventListener('click', function() {
|
||||
closeSettings();
|
||||
});
|
||||
|
||||
function _setResetTimeToDefault(value, old, key, settings) {
|
||||
var today = new Date();
|
||||
var defaultResetTime = (settings.trackingPeriod === 'weekly') ?
|
||||
today.getDay() : today.getDate();
|
||||
if (settings.resetTime !== defaultResetTime) {
|
||||
ConfigManager.setOption({ resetTime: defaultResetTime });
|
||||
} else {
|
||||
Common.updateNextReset(settings.trackingPeriod, settings.resetTime);
|
||||
}
|
||||
}
|
||||
|
||||
function _updateNextReset(value, old, key, settings) {
|
||||
Common.updateNextReset(settings.trackingPeriod, settings.resetTime);
|
||||
}
|
||||
|
||||
ConfigManager.observe('resetTime', _updateNextReset, true);
|
||||
ConfigManager.observe('trackingPeriod', _setResetTimeToDefault, true);
|
||||
|
||||
initialized = true;
|
||||
|
||||
Settings.updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
function closeSettings() {
|
||||
window.parent.location.hash = '#';
|
||||
}
|
||||
|
||||
// Loads extra HTML useful for debugging
|
||||
function loadDeveloperAids() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType('text/plain');
|
||||
xhr.open('GET', '/debug.html', false);
|
||||
xhr.send();
|
||||
|
||||
if (xhr.status === 200) {
|
||||
var src = document.createElement('DIV');
|
||||
// XXX: We use innerHTML precisely because we need parse the content
|
||||
// without introducing the overhead of DOM methods.
|
||||
src.innerHTML = xhr.responseText;
|
||||
var reference = document.getElementById('plantype-settings');
|
||||
var parent = reference.parentNode;
|
||||
[].forEach.call(src.childNodes, function(node) {
|
||||
reference = parent.insertBefore(node, reference.nextSibling);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Configure reset dialogs for telephony and data usage
|
||||
function configureTelephonyReset() {
|
||||
var resetTelephonyButton = document.getElementById('reset-telephony');
|
||||
resetTelephonyButton.addEventListener('click', function() {
|
||||
confirmDialog.setMessage('reset-telephony-confirmation-warning', {});
|
||||
function resetAction() {
|
||||
resetTelephony();
|
||||
updateUI();
|
||||
confirmDialog.closeConfirmDialog();
|
||||
}
|
||||
confirmDialog.updateAcceptAction(resetAction);
|
||||
confirmDialog.showConfirmDialog();
|
||||
});
|
||||
}
|
||||
|
||||
// Configure reset dialogs for telephony and data usage
|
||||
function configureDataResets() {
|
||||
// Button reset Data Usage Settings send to reset data dialog
|
||||
var resetDataUsage = document.getElementById('reset-data-usage');
|
||||
resetDataUsage.addEventListener('click', function _onDataReset() {
|
||||
var currentTotals = {
|
||||
mobile: costcontrol.lastDataResults.mobile.total,
|
||||
wifi: costcontrol.lastDataResults.wifi.total
|
||||
};
|
||||
resetMenuDialog.showResetMenuDialog(currentTotals);
|
||||
});
|
||||
}
|
||||
|
||||
// Add particular constrains to the "Done" button
|
||||
var balanceLowLimitView;
|
||||
function addDoneConstrains() {
|
||||
var closeButton = document.getElementById('close-settings');
|
||||
balanceLowLimitView = new BalanceLowLimitView(
|
||||
document.getElementById('low-limit'),
|
||||
document.getElementById('low-limit-input')
|
||||
);
|
||||
balanceLowLimitView.onvalidation = function(evt) {
|
||||
closeButton.disabled = !evt.isValid;
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener('localized', function _onLocalize() {
|
||||
if (initialized) {
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
|
||||
var currentMode;
|
||||
function updateUI() {
|
||||
ConfigManager.requestAll(function _onInfo(configuration, settings) {
|
||||
// L10n
|
||||
Common.localizeWeekdaySelector(
|
||||
document.getElementById('select-weekday'));
|
||||
|
||||
// Layout
|
||||
var mode = ConfigManager.getApplicationMode();
|
||||
if (currentMode !== mode) {
|
||||
currentMode = mode;
|
||||
var hidePlantypeSelector = (mode === 'DATA_USAGE_ONLY');
|
||||
var hidePhoneActivity = (mode !== 'POSTPAID');
|
||||
var hideBalance = (mode !== 'PREPAID');
|
||||
var hideReportsTitle = (mode === 'PREPAID');
|
||||
var textReportsTitle = (mode === 'POSTPAID') ?
|
||||
'phone-and-internet-data-report' : 'internet-data-report';
|
||||
|
||||
reportsTitle.querySelector('span').setAttribute(
|
||||
'data-l10n-id', textReportsTitle
|
||||
);
|
||||
|
||||
balanceLowLimitView.disabled = (mode !== 'PREPAID');
|
||||
plantypeSelector.hidden = hidePlantypeSelector;
|
||||
phoneActivityTitle.hidden = hidePhoneActivity;
|
||||
phoneActivitySettings.hidden = hidePhoneActivity;
|
||||
balanceTitle.hidden = hideBalance;
|
||||
balanceSettings.hidden = hideBalance;
|
||||
reportsTitle.hidden = hideReportsTitle;
|
||||
}
|
||||
|
||||
// Views
|
||||
var requestObj = {
|
||||
type: 'datausage'
|
||||
};
|
||||
costcontrol.request(requestObj, function _onDataStats(result) {
|
||||
var stats = result.data;
|
||||
updateDataUsage(stats, settings.lastCompleteDataReset);
|
||||
});
|
||||
|
||||
switch (mode) {
|
||||
case 'PREPAID':
|
||||
updateBalance(settings.lastBalance, configuration.credit.currency);
|
||||
break;
|
||||
case 'POSTPAID':
|
||||
updateTelephony(settings.lastTelephonyActivity,
|
||||
settings.lastTelephonyReset);
|
||||
break;
|
||||
}
|
||||
if (endLoadSettingsNotified) {
|
||||
window.performance.measure('loadSettings', 'loadSettingsStart');
|
||||
endLoadSettingsNotified = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update data usage view on settings
|
||||
function updateDataUsage(datausage, lastCompleteDataReset) {
|
||||
var mobileUsage = document.querySelector('#mobile-data-usage > span');
|
||||
var data = Formatting.roundData(datausage.mobile.total);
|
||||
mobileUsage.textContent = Formatting.formatData(data);
|
||||
|
||||
var wifiUsage = document.querySelector('#wifi-data-usage > span');
|
||||
data = Formatting.roundData(datausage.wifi.total);
|
||||
wifiUsage.textContent = Formatting.formatData(data);
|
||||
|
||||
updateDataUsageTimestamp(lastCompleteDataReset, datausage.timestamp);
|
||||
}
|
||||
|
||||
var dataUsagePeriod = { begin: null, end: null };
|
||||
function updateDataUsageTimestamp(begin, end) {
|
||||
dataUsagePeriod.begin = begin;
|
||||
dataUsagePeriod.end = end;
|
||||
var timestamp = document.querySelector('#wifi-data-usage + .meta');
|
||||
timestamp.innerHTML = '';
|
||||
timestamp.appendChild(Formatting.formatTimeHTML(begin, end));
|
||||
}
|
||||
|
||||
// Update balance view on settings
|
||||
function updateBalance(lastBalance, currency) {
|
||||
var limitCurrency = document.getElementById('settings-low-limit-currency');
|
||||
limitCurrency.textContent = currency;
|
||||
balanceView.update(lastBalance);
|
||||
}
|
||||
|
||||
// Update telephony counters on settings
|
||||
function updateTelephony(activity, lastTelephonyReset) {
|
||||
var calltimeSpan = document.getElementById('calltime');
|
||||
var smscountSpan = document.getElementById('smscount');
|
||||
Common.localize(calltimeSpan, 'magnitude', {
|
||||
value: Formatting.computeTelephonyMinutes(activity),
|
||||
unit: 'min.'
|
||||
});
|
||||
Common.localize(smscountSpan, 'magnitude', {
|
||||
value: activity.smscount,
|
||||
unit: 'SMS'
|
||||
});
|
||||
updateTelephonyTimestamp(lastTelephonyReset, activity.timestamp);
|
||||
}
|
||||
|
||||
var telephonyPeriod = { begin: null, end: null };
|
||||
function updateTelephonyTimestamp(begin, end) {
|
||||
telephonyPeriod.begin = begin;
|
||||
telephonyPeriod.end = end;
|
||||
var timestamp = document.getElementById('telephony-timestamp');
|
||||
timestamp.innerHTML = '';
|
||||
timestamp.appendChild(Formatting.formatTimeHTML(begin, end));
|
||||
}
|
||||
|
||||
window.addEventListener('timeformatchange', function () {
|
||||
updateTelephonyTimestamp(telephonyPeriod.begin, telephonyPeriod.end);
|
||||
updateDataUsageTimestamp(dataUsagePeriod.begin, dataUsagePeriod.end);
|
||||
});
|
||||
|
||||
return {
|
||||
initialize: function() {
|
||||
var SCRIPTS_NEEDED = [
|
||||
'js/utils/toolkit.js',
|
||||
'shared/js/date_time_helper.js',
|
||||
'js/utils/formatting.js',
|
||||
'js/views/BalanceLowLimitView.js',
|
||||
'js/settings/limitdialog.js',
|
||||
'js/settings/autosettings.js',
|
||||
'js/view_manager.js',
|
||||
'js/views/BalanceView.js',
|
||||
'js/views/ResetMenuDialog.js',
|
||||
'js/views/ConfirmDialog.js'
|
||||
];
|
||||
LazyLoader.load(SCRIPTS_NEEDED, function() {
|
||||
if (!Common.allNetworkInterfaceLoaded) {
|
||||
Common.loadNetworkInterfaces(configureUI);
|
||||
} else {
|
||||
configureUI();
|
||||
}
|
||||
});
|
||||
},
|
||||
updateUI: updateUI
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
Settings.initialize();
|
||||
Loading…
Reference in a new issue