diff --git a/ui (gen4)/features/all.js b/ui (gen4)/features/all.js index e83969ae..3568beb9 100755 --- a/ui (gen4)/features/all.js +++ b/ui (gen4)/features/all.js @@ -17,6 +17,7 @@ require('features/tags') require('features/marks') require('features/location') require('features/recover') +require('features/config') require('features/history') require('features/app') require('features/peer') diff --git a/ui (gen4)/features/config.js b/ui (gen4)/features/config.js new file mode 100755 index 00000000..bf38afbd --- /dev/null +++ b/ui (gen4)/features/config.js @@ -0,0 +1,293 @@ +/********************************************************************** +* +* Features: +* - config +* general config API +* - localstorage-config +* maintain configuration state in localStorage +* - fs-config +* maintain configuration state in file system +* +* XXX this module need refactoring... +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + +var toggler = require('lib/toggler') +var actions = require('lib/actions') +var features = require('lib/features') + +var core = require('features/core') + + + +/*********************************************************************/ +// XXX might be a good idea to add an external payload mechanism for +// other data to be saved to avoid re-implementing the same logic +// ...like is done in features/history.js + +var ConfigActions = actions.Actions({ + config: { + 'config-store-key': 'config', + + // NOTE: this is in seconds... + // NOTE: if this is null or 0 the timer will not start... + 'config-auto-save-interval': 3*60, + }, + + // XXX should we store this in something like .default_config and + // clone it??? + // ...do not think so, as the __base_config should always be set + // to the values set in code... (check this!) + __base_config: null, + __config_loaded: null, + __auto_save_config_timer: null, + + + // Disable localStorage in child, preventing two viewers from messing + // things up in one store... + clone: [function(){ + return function(res){ + res.config['config-store-key'] = null + } + }], + + // XXX make this a protocol to support multiple sources... + // ...load only one, by priority/order + // might be good to make this similar to collections loading... + storeConfig: ['File/Store configuration', + function(key){ + // XXX + }], + loadConfig: ['File/Load stored configuration', + function(key){ + // XXX + }], + // XXX need to load the reset config, and not just set it... + resetConfig: ['File/Reset settings', + function(){ + this.config = this.__base_config || this.config + }], + + toggleAutoStoreConfig: ['File/Store configuration', + toggler.Toggler(null, + function(_, state){ + if(state == null){ + return this.__auto_save_config_timer || 'none' + + } else { + var that = this + var interval = this.config['config-auto-save-interval'] + + // no timer interval set... + if(!interval){ + return false + } + + // this cleans up before 'on' and fully handles 'off' action... + if(this.__auto_save_config_timer != null){ + clearTimeout(this.__auto_save_config_timer) + delete this.__auto_save_config_timer + } + + if(state == 'running' + && interval + && this.__auto_save_config_timer == null){ + + var runner = function(){ + clearTimeout(that.__auto_save_config_timer) + + //that.logger && that.logger.emit('config', 'saving to local storage...') + that.storeConfig() + + var interval = that.config['config-auto-save-interval'] + if(!interval){ + delete that.__auto_save_config_timer + return + } + interval *= 1000 + + that.__auto_save_config_timer = setTimeout(runner, interval) + } + + runner() + } + } + }, + 'running')], +}) + + +var Config = +module.Config = core.ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'config', + depends: [ + ], + priority: 80, + suggested: [ + 'localstorage-config', + 'fs-config', + ], + + actions: ConfigActions, + + handlers: [ + // NOTE: considering that allot depends on this it must be + // first to run... + ['start.pre', + function(){ + this.logger && this.logger + .push('Startup') + .emit('loaded', 'config') + this + .loadConfig() + .toggleAutoStoreConfig('on') + }], + ['stop.pre', + function(){ + this.logger && this.logger + .push('Shutdown') + .emit('stored', 'config') + this + .storeConfig() + .toggleAutoStoreConfig('off') + }], + ], +}) + + + +//--------------------------------------------------------------------- + +var ConfigLocalStorageActions = actions.Actions({ + config: { + // XXX not sure what should be the default... + 'config-local-storage-save-diff': true, + }, + + storeConfig: ['File/Store configuration', + function(key){ + var key = key || this.config['config-store-key'] + + if(key != null){ + // build a diff... + if(this.config['config-local-storage-save-diff']){ + var base = this.__base_config || {} + var cur = this.config + var config = {} + Object.keys(cur) + .forEach(function(e){ + if(cur.hasOwnProperty(e) + && base[e] != cur[e] + // NOTE: this may go wrong for objects + // if key order is different... + // ...this is no big deal as false + // positives are not lost data... + || JSON.stringify(base[e]) != JSON.stringify(cur[e])){ + config[e] = cur[e] + } + }) + + // full save... + } else { + var config = this.config + } + + // store... + localStorage[key] = JSON.stringify(config) + } + }], + loadConfig: ['File/Load stored configuration', + function(key){ + key = key || this.config['config-store-key'] + + if(key && localStorage[key]){ + // get the original (default) config and keep it for + // reference... + // NOTE: this is here so as to avoid creating 'endless' + // config inheritance chains... + base = this.__base_config = this.__base_config || this.config + + var loaded = JSON.parse(localStorage[key]) + loaded.__proto__ = base + + this.config = loaded + } + }], +}) + + +var ConfigLocalStorage = +module.ConfigLocalStorage = core.ImageGridFeatures.Feature({ + title: '', + doc: '', + + // XXX rename??? + tag: 'localstorage-config', + depends: [ + 'config', + 'ui', + ], + priority: 80, + + isApplicable: function(){ + return typeof(localStorage) != 'undefined' + && localStorage != null }, + + actions: ConfigLocalStorageActions, +}) + + + +//--------------------------------------------------------------------- +// XXX store config locations: +// - app +// - home +// XXX config override location/filename to support portable apps... + +var ConfigFS = actions.Actions({ + config: { + }, + +}) + + +var ConfigLocalStorage = +module.ConfigLocalStorage = core.ImageGridFeatures.Feature({ + title: '', + doc: '', + + tag: 'fs-config', + depends: [ + 'config-local-storage', + 'fs', + ], + + actions: ConfigFS, + + handlers: [ + // NOTE: considering that allot depends on this it must be + // first to run... + ['loadConfig', + function(){ + }], + ['storeConfig', + function(){ + }], + ], +}) + + + +//--------------------------------------------------------------------- + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/ui (gen4)/features/meta.js b/ui (gen4)/features/meta.js index b12ede90..3fa086cd 100755 --- a/ui (gen4)/features/meta.js +++ b/ui (gen4)/features/meta.js @@ -135,7 +135,7 @@ core.ImageGridFeatures.Feature('imagegrid-testing', [ // ...this is best included by direct feature dependency. 'index-format', - 'config-local-storage', + 'config', 'ui-url-hash', 'fail-safe-devtools', diff --git a/ui (gen4)/features/ui.js b/ui (gen4)/features/ui.js index 50945d45..06be3547 100755 --- a/ui (gen4)/features/ui.js +++ b/ui (gen4)/features/ui.js @@ -5,8 +5,6 @@ * Features: * - ui * implements basic ui actions... -* - config-local-storage -* maintain configuration state in local storage * - ui-url-hash * handle .location.hash * - ui-cursor @@ -785,182 +783,6 @@ module.Viewer = core.ImageGridFeatures.Feature({ /*********************************************************************/ // Utilities and Services... -var ConfigLocalStorageActions = actions.Actions({ - config: { - 'config-local-storage-key': 'config', - - // NOTE: this is in seconds... - // NOTE: if this is null or 0 the timer will not start... - 'config-auto-save-local-storage-interval': 3*60, - - // XXX not sure what should be the default... - 'config-local-storage-save-diff': true, - }, - - // XXX should we store this in something like .default_config and - // clone it??? - // ...do not think so, as the __base_config should always be set - // to the values set in code... (check this!) - __base_config: null, - __config_loaded: null, - __auto_save_config_timer: null, - - // Disable localStorage in child, preventing two viewers from messing - // things up in one store... - clone: [function(){ - return function(res){ - res.config['config-local-storage-key'] = null - } - }], - - storeConfig: ['File/Store configuration', - function(key){ - var key = key || this.config['config-local-storage-key'] - - if(key != null){ - // build a diff... - if(this.config['config-local-storage-save-diff']){ - var base = this.__base_config || {} - var cur = this.config - var config = {} - Object.keys(cur) - .forEach(function(e){ - if(cur.hasOwnProperty(e) - && base[e] != cur[e] - // NOTE: this may go wrong for objects - // if key order is different... - // ...this is no big deal as false - // positives are not lost data... - || JSON.stringify(base[e]) != JSON.stringify(cur[e])){ - config[e] = cur[e] - } - }) - - // full save... - } else { - var config = this.config - } - - // store... - localStorage[key] = JSON.stringify(config) - } - }], - loadStoredConfig: ['File/Load stored configuration', - function(key){ - key = key || this.config['config-local-storage-key'] - - if(key && localStorage[key]){ - // get the original (default) config and keep it for - // reference... - // NOTE: this is here so as to avoid creating 'endless' - // config inheritance chains... - base = this.__base_config = this.__base_config || this.config - - var loaded = JSON.parse(localStorage[key]) - loaded.__proto__ = base - - this.config = loaded - } - }], - // XXX need to load the reset config, and not just set it... - resetConfig: ['File/Reset settings', - function(){ - this.config = this.__base_config || this.config - }], - - toggleAutoStoreConfig: ['File/Store configuration', - toggler.Toggler(null, - function(_, state){ - if(state == null){ - return this.__auto_save_config_timer || 'none' - - } else { - var that = this - var interval = this.config['config-auto-save-local-storage-interval'] - - // no timer interval set... - if(!interval){ - return false - } - - // this cleans up before 'on' and fully handles 'off' action... - if(this.__auto_save_config_timer != null){ - clearTimeout(this.__auto_save_config_timer) - delete this.__auto_save_config_timer - } - - if(state == 'running' - && interval - && this.__auto_save_config_timer == null){ - - var runner = function(){ - clearTimeout(that.__auto_save_config_timer) - - //that.logger && that.logger.emit('config', 'saving to local storage...') - that.storeConfig() - - var interval = that.config['config-auto-save-local-storage-interval'] - if(!interval){ - delete that.__auto_save_config_timer - return - } - interval *= 1000 - - that.__auto_save_config_timer = setTimeout(runner, interval) - } - - runner() - } - } - }, - 'running')], -}) - -var ConfigLocalStorage = -module.ConfigLocalStorage = core.ImageGridFeatures.Feature({ - title: '', - doc: '', - - tag: 'config-local-storage', - depends: [ - 'ui', - ], - priority: 80, - - isApplicable: function(){ - return typeof(localStorage) != 'undefined' - && localStorage != null }, - - actions: ConfigLocalStorageActions, - - handlers: [ - // NOTE: considering that allot depends on this it must be - // first to run... - ['start.pre', - function(){ - this.logger && this.logger - .push('Startup') - .emit('loaded', 'config') - this - .loadStoredConfig() - .toggleAutoStoreConfig('on') - }], - ['stop.pre', - function(){ - this.logger && this.logger - .push('Shutdown') - .emit('stored', 'config') - this - .storeConfig() - .toggleAutoStoreConfig('off') - }], - ], -}) - - - -//--------------------------------------------------------------------- - // XXX make this work for external links in a stable manner... // ...a bit unpredictable when working in combination with history // feature -- need to stop them from competing...