/** * SettingsCache is a singleton that caches mozSettings values for fast * access. * * @module SettingsCache */ define(function() { 'use strict'; var _settings = window.navigator.mozSettings; // Cache of all current settings values. There's some large stuff // in here, but not much useful can be done with the settings app // without these, so we keep this around most of the time. var _settingsCache = null; // True when a request has already been made to fill the settings // cache. When this is true, no further get("*") requests should be // made; instead, pending callbacks should be added to // _pendingSettingsCallbacks. var _settingsCacheRequestSent = null; // There can be race conditions in which we need settings values, // but haven't filled the cache yet. This array tracks those // listeners. var _pendingSettingsCallbacks = []; var _callbacks = []; var _onSettingsChange = function sc_onSettingsChange(event) { var key = event.settingName; var value = event.settingValue; // Always update the cache if it's present, even if the DOM // isn't loaded yet. if (_settingsCache) { _settingsCache[key] = value; } _callbacks.forEach(function(callback) { callback(event); }); }; if (_settings) { _settings.onsettingchange = _onSettingsChange; } /** * Event reporting that a setting value is changed. * * @event module:SettingsCache#settingsChange * @property {MozSettingsEvent} event */ var SettingsCache = { // the reset function is for unit tests reset: function sc_reset() { _settings = window.navigator.mozSettings; if (_settings) { _settings.onsettingchange = _onSettingsChange; } _settingsCache = null; _settingsCacheRequestSent = null; _pendingSettingsCallbacks = []; _callbacks = []; }, get cache() { return _settingsCache; }, /** * Where callback is a function to be called with a request object for a * successful fetch of settings values, when those values are ready. * * @alias module:SettingsCache#getSettings * @param {Function} callback */ getSettings: function sc_getSettings(callback) { if (!_settings) { return; } if (_settingsCache && callback) { // Fast-path that we hope to always hit: our settings cache is // already available, so invoke the callback now. callback(_settingsCache); return; } if (!_settingsCacheRequestSent && !_settingsCache) { _settingsCacheRequestSent = true; var lock = _settings.createLock(); var request = lock.get('*'); request.onsuccess = function(e) { var result = request.result; var cachedResult = {}; for (var attr in result) { cachedResult[attr] = result[attr]; } _settingsCache = cachedResult; var cbk; while ((cbk = _pendingSettingsCallbacks.pop())) { cbk(result); } }; } if (callback) { _pendingSettingsCallbacks.push(callback); } }, /** * @alias module:SettingsCache#addEventListener * @param {String} eventName * @param {Function} callback */ addEventListener: function sc_addEventListener(eventName, callback) { if (eventName !== 'settingsChange') { return; } var index = _callbacks.indexOf(callback); if (index === -1) { _callbacks.push(callback); } }, /** * @alias module:SettingsCache#removeEventListener * @param {String} eventName * @param {Function} callback */ removeEventListener: function sc_removeEventListsner(eventName, callback) { if (eventName !== 'settingsChange') { return; } var index = _callbacks.indexOf(callback); if (index !== -1) { _callbacks.splice(index, 1); } } }; // Make a request for settings to warm the cache, since we need it // very soon in startup after the DOM is available. SettingsCache.getSettings(null); return SettingsCache; });