VulpesOS/gaia/apps/camera/js/lib/setting.js
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

251 lines
5.6 KiB
JavaScript
Executable file

define(function(require, exports, module) {
'use strict';
/**
* Dependencies
*/
var debug = require('debug')('setting');
var model = require('model');
/**
* Exports
*/
module.exports = Setting;
/**
* Mixin `Model` methods
*/
model(Setting.prototype);
/**
* Initialize a new `Setting`.
*
* @param {Object} data
*/
function Setting(data) {
this.key = data.key;
this.storage = data.storage || localStorage;
this.reset(data, { silent: true });
this.select = this.select.bind(this);
this.next = this.next.bind(this);
this.configure(data);
if (data.persistent) { this.on('change:selected', this.save); }
}
Setting.prototype.configure = function(data) {
var optionsDefined = !!(data.options && data.options.length);
this.options = { defined: optionsDefined };
this.resetOptions(data.options);
};
Setting.prototype.resetOptions = function(list) {
list = list || [];
var hash = this.optionsToHash(list);
this.options.all = hash;
this.options.available = hash;
this.set('options', list, { silent: true });
this.updateSelected({ silent: true });
};
Setting.prototype.filterOptions = function(keys, options) {
var available = this.options.available = {};
var hash = this.options.all;
var filtered = [];
(keys || []).forEach(function(key) {
var option = hash[key];
if (option !== undefined) {
filtered.push(option);
available[key] = option;
}
});
this.sortByIndex(filtered);
this.set('options', filtered, options);
this.updateSelected({ silent: true });
};
/**
* Convert this Setting's `options` array as defined
* in config/app.js into an object as key/value pairs.
* This allows us to look up an option by its `key`.
*
* e.g.: [ { key: 'a', title: 'A' }, ... ] ->
* { a: { key: 'a', title: 'A' }, ... }
*
* We use this hash to validate
* incoming options and ot mixin
* config data with dynamic options.
*
* @param {Array} options
* @return {undefined|Object}
*/
Setting.prototype.optionsToHash = function(options) {
var hash = {};
options.forEach(function(option, index) {
option.index = index;
hash[option.key] = option;
});
return hash;
};
/**
* Get the selected option,
* or just a particular key
* if given.
*
* @param {String} key
* @return {Object|*}
*/
Setting.prototype.selected = function(key) {
var hash = this.options.available;
var option = hash[this.get('selected')];
return key ? option && option[key] : option;
};
/**
* Select an option.
*
* Accepts an string key to select,
* or an integer index relating to
* the current options list.
*
* Options:
*
* - {Boolean} silent
*
* @param {String|Number} key
* @param {Object} opts Model#set() options
*/
Setting.prototype.select = function(key, options) {
var isIndex = typeof key === 'number';
var list = this.get('options');
var available = this.options.available;
var selected = isIndex ? list[key] : available[key];
// If there are no options, exit
if (!list.length) { return; }
// If an option was not found,
// default to selecting the first.
if (!selected) { return this.select(0, options); }
// Store the new choice
this.set('selected', selected.key, options);
};
/**
* Sorts the current options list
* by their originally defined
* index in the config JSON.
*
* @private
*/
Setting.prototype.sortByIndex = function(list) {
return list.sort(function(a, b) { return a.index - b.index; });
};
/**
* Silently updates the `selected`
* property of the Setting.
*
* If no selected key is present
* we attempt to select the last
* fetched persited selection.
*
* @private
*/
Setting.prototype.updateSelected = function(options) {
this.select(this.get('selected') || this.fetched, options);
};
/**
* Set the `selected` option to
* the next option in the list.
*
* First option is chosen if
* there is no next option.
*
* @public
*/
Setting.prototype.next = function() {
var options = this.get('options');
var selected = this.selected();
var index = options.indexOf(selected);
var newIndex = (index + 1) % options.length;
this.select(newIndex);
debug('set \'%s\' to index: %s', this.key, newIndex);
};
/**
* Persists the current selection
* to storage for retreval in the
* next session.
*
* We're using localStorage as performance
* vastly outweighed indexedBD (asyncStorage)
* by 1ms/500ms on Hamachi device.
*
* @public
*/
Setting.prototype.save = function() {
var selected = this.get('selected');
debug('saving key: %s, selected: %s', this.key, selected);
this.storage.setItem('setting:' + this.key, selected);
debug('saved key: %s', selected);
};
/**
* Fetches the persisted selection
* from storage, updating the
* `selected` key.
*
* We're using localStorage, as performance
* vastly outweighed indexedBD (asyncStorage)
* by 1ms/500ms on Hamachi device.
*
* Leaving in the `done` callback in-case
* storage goes async again in future.
*
* @param {Function} done
* @public
*/
Setting.prototype.fetch = function() {
if (!this.get('persistent')) { return; }
debug('fetch value key: %s', this.key);
this.fetched = this.storage.getItem('setting:' + this.key);
debug('fetched %s value: %s', this.key, this.fetched);
if (this.fetched) { this.select(this.fetched, { silent: true }); }
};
/**
* States whether this setting
* is currently supported.
*
* 'Supported' means, it's not been
* disabled, and there are options
* to be chosen from.
*
* @return {Boolean}
*/
Setting.prototype.supported = function() {
return this.enabled() && !!this.get('options').length;
};
/**
* Check if this setting is not
* marked as disabled.
*
* @return {Boolean}
*/
Setting.prototype.enabled = function() {
return !this.get('disabled');
};
});