diff --git a/ui (gen4)/lib/toggler.js b/ui (gen4)/lib/toggler.js index f49e0dd2..1f8d4713 100755 --- a/ui (gen4)/lib/toggler.js +++ b/ui (gen4)/lib/toggler.js @@ -92,25 +92,40 @@ // state_accessor signature: // // Get current state: -// state_accessor() +// state_accessor() // -> // // Set new state: -// state_accessor() +// state_accessor(, ) // -> // +// `this' within is set to toggler's context. +// // NOTE: for single state toggling, 'none' will get passed to // state_accessor to indicate an "empty" state... // NOTE: if elem is a function it will be called in the same context as // the toggler and is expected to return the element. // +// +// states can be: +// - state string, equivalent to ['none', ] +// this will produce a bool toggler that will toggle +// a single state on and off. +// [, ...] - list of string states that will be toggled +// through, one special state 'none' is supported +// function(){ ... } - function that will return either a state or +// a list of states, `this' will be set to +// the toggler's context... +// +// // XXX technically we do not need both elem and state_accessor here, the // later is enough, but as strict mode is not stable enough (sometimes // works and sometimes does not), we can not reliably pass the element // via 'this'. function Toggler(elem, state_accessor, states, callback_a, callback_b){ // normalize states... - states = typeof(states) == typeof('str') ? ['none', states] : states + var states_getter = states + var state_set = typeof(states) == typeof('str') ? ['none', states] : states // normalize the callbacks... if(callback_b === undefined){ var callback_pre = null @@ -120,8 +135,6 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){ var callback_post = callback_b } - var bool_action = (states.length == 2 && states[0] == 'none') - // NOTE: this needs to be strict so as to be able to distinguish // between a method and a root context in a simple manner... var func = function(a, b){ @@ -141,6 +154,17 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){ e = e instanceof Function ? e.call(this) : e + // see if we got an explicit state list or need to use a getter... + var states = state_set + if(typeof(states_getter) == typeof(function(){})){ + // get the states... + var states = states_getter.call(this) + var states = typeof(states) == typeof('str') ? + ['none', states] + : states + } + var bool_action = (state_set.length == 2 && state_set[0] == 'none') + // XXX is this correct??? var args = args2array(arguments).slice(2) @@ -160,7 +184,7 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){ // we need to get the current state... if(action == null || action == '?' || action == '!'){ // get current state... - var cur = state_accessor.call(e) + var cur = state_accessor.call(this, e) // just asking for info... if(action == '?'){ @@ -204,7 +228,7 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){ } // update the element... - state_accessor.call(e, state) + state_accessor.call(this, e, state) // post callback... if(callback_post != null){ @@ -217,21 +241,48 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){ return action } - func.states = states - if(bool_action){ - func.doc = 'With no arguments this will toggle between "on" and '+ - '"off".\n'+ - 'If either "on" or "off" are given then this will switch '+ - 'to that mode.\n'+ - 'If "?" is given, this will return either "on" or "off" '+ - 'depending on the current state.' - }else{ - func.doc = 'With no arguments this will toggle between '+ - states +' in cycle.\n' + - 'if any of the state names or its number is given then that '+ - 'state is switched on.'+ - 'If "?" is given, this will return the current state.' - } + // XXX these are broken... + //func.states = states + Object.defineProperty(func, 'states', { + get: function(){ + return typeof(states_getter) == typeof(function(){}) ? + states_getter.apply(this) + : state_set + }, + set: function(value){ + state_set = states_getter = value + }, + }) + Object.defineProperty(func, 'doc', { + get: function(){ + if(func.__doc != null){ + return func.__doc + } + var states = typeof(states_getter) == typeof(function(){}) ? + states_getter.apply(this) + : state_set + + // bool_action... + if(states.length == 2 && states[0] == 'none'){ + return 'With no arguments this will toggle between "on" and ' + +'"off".\n' + +'If either "on" or "off" are given then this will switch ' + +'to that mode.\n' + +'If "?" is given, this will return either "on" or "off" ' + +'depending on the current state.' + + } else { + return 'With no arguments this will toggle between ' + + states +' in cycle.\n' + +'if any of the state names or its number is given then that ' + +'state is switched on.' + +'If "?" is given, this will return the current state.' + } + }, + set: function(value){ + func.__doc = value + }, + }) func.__proto__ = Toggler.prototype func.constructor = Toggler @@ -244,26 +295,46 @@ Toggler.prototype.__proto__ = Function.prototype // XXX this should be drop-in compatible with createCSSClassToggler(..) // test and replace... function CSSClassToggler(elem, classes, callback_a, callback_b){ - // normalize the states... - classes = typeof(classes) == typeof('str') ? ['none', classes] : classes - // remove the dot from class names... - // NOTE: this is here because I've made the error of including a - // leading "." almost every time I use this after I forget - // the UI... - classes = classes - .map(function(e){ - return e.split(' ') - .map(function(c){ - c = c.trim() - return c[0] == '.' ? c.slice(1) : c - }).join(' ') - }) + var classes_getter = classes + var classes_set = classes + + var getClasses = function(){ + var classes = typeof(classes_getter) == typeof(function(){}) ? + classes_getter.call(this) + : classes_set + classes = typeof(classes) == typeof('str') ? ['none', classes] : classes + + // remove the dot from class names... + // NOTE: this is here because I've made the error of including a + // leading "." almost every time I use this after I forget + // the UI... + return classes + .map(function(e){ + return e.split(' ') + .map(function(c){ + c = c.trim() + return c[0] == '.' ? c.slice(1) : c + }).join(' ') + }) + } + + // normalize... + // NOTE: this happens here once if we got explicit classes, and on + // each access if we get a getter function... + classes_set = typeof(classes_getter) != typeof(function(){}) ? + getClasses.call(this) + : classes_set var toggler = Toggler( elem, - function(state){ + function(e, state){ 'use strict' - var e = $(this == null ? elem : this) + + var classes = classes_set.constructor === Array ? + classes_set + : getClasses.call(this) + + e = $(e == null ? elem : e) // get the state... if(state == null){ var cur = 'none' @@ -286,7 +357,7 @@ function CSSClassToggler(elem, classes, callback_a, callback_b){ } } }, - classes, + typeof(classes_getter) == typeof(function(){}) ? getClasses : classes_set, callback_a, callback_b) diff --git a/ui (gen4)/package.json b/ui (gen4)/package.json index 261e39f2..fd2f0ff6 100755 --- a/ui (gen4)/package.json +++ b/ui (gen4)/package.json @@ -9,7 +9,7 @@ "height": 700, "min_width": 400, "min_height": 400, - "frame": false, + "frame": true, "toolbar": false, "show": false }, diff --git a/ui (gen4)/viewer.js b/ui (gen4)/viewer.js index b0ebece7..d187a216 100755 --- a/ui (gen4)/viewer.js +++ b/ui (gen4)/viewer.js @@ -456,9 +456,9 @@ actions.Actions({ function(all){ this.focusImage(all == null ? 'last' : -1) }], // XXX these break if image at first/last position are not loaded (crop, group, ...) // XXX do we actually need these??? - firstGlobalImage: ['Navigate/First globally image', + firstGlobalImage: ['Navigate/First image globally', function(){ this.firstImage(true) }], - lastGlobalImage: ['Navigate/Last globally image', + lastGlobalImage: ['Navigate/Last image globally', function(){ this.lastImage(true) }], // XXX skip unloaded images... (groups?) @@ -1160,14 +1160,8 @@ actions.Actions({ toggleTheme: ['Interface/Toggle viewer theme', CSSClassToggler( function(){ return this.ribbons.viewer }, - // XXX how do we get this live from config??? - //this.config.themes, - [ - 'gray', - 'dark', - 'light', - ], - function(state){ this.config['theme'] = state }) ], + function(){ return this.config.themes }, + function(state){ this.config.theme = state }) ], setEmptyMsg: ['- Interface/Set message to be displayed when nothing is loaded.', function(msg, help){ this.ribbons.setEmptyMsg(msg, help) }], @@ -1717,7 +1711,11 @@ var ConfigLocalStorageActions = actions.Actions({ key = key || this.config['config-local-storage-key'] if(key && localStorage[key]){ - this.config = JSON.parse(localStorage[key]) + var base = this.config + var loaded = JSON.parse(localStorage[key]) + loaded.__proto__ = base + + this.config = loaded } }], @@ -3796,7 +3794,7 @@ var FileSystemLoaderActions = actions.Actions({ // XXX add a symmetric equivalent to .prepareIndexForWrite(..) so as // to enable features to load their data... // XXX look inside... - loadIndex: ['File/Load index', + loadIndex: ['- File/Load index', function(path, logger){ var that = this @@ -3902,7 +3900,7 @@ var FileSystemLoaderActions = actions.Actions({ // XXX add a recursive option... // ...might also be nice to add sub-dirs to ribbons... // XXX make image pattern more generic... - loadImages: ['File/Load images', + loadImages: ['- File/Load images', function(path, logger){ if(path == null){ return @@ -3932,7 +3930,7 @@ var FileSystemLoaderActions = actions.Actions({ }], // XXX auto-detect format or let the user chose... - loadPath: ['File/Load path (STUB)', + loadPath: ['- File/Load path (STUB)', function(path, logger){ // XXX check if this.config['index-dir'] exists, if yes then // .loadIndex(..) else .loadImages(..) @@ -4137,8 +4135,8 @@ var FileSystemLoaderUIActions = actions.Actions({ // to start from. // XXX should passing no path to this start browsing from the current // path or from the root? - loadIndex: [makeBrowseProxy('loadIndex')], - loadImages: [makeBrowseProxy('loadImages')], + loadIndex: ['File/Load index', makeBrowseProxy('loadIndex')], + loadImages: ['File/Load images', makeBrowseProxy('loadImages')], })