toggler now support accessor function on all args and uniformly (backwards incompatible) + some tweaking and cleanup...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2015-12-12 03:36:55 +03:00
parent 987a3f0ddb
commit 7c7777d745
3 changed files with 125 additions and 56 deletions

View File

@ -92,25 +92,40 @@
// state_accessor signature: // state_accessor signature:
// //
// Get current state: // Get current state:
// state_accessor() // state_accessor(<elem>)
// -> <current-state> // -> <current-state>
// //
// Set new state: // Set new state:
// state_accessor(<new-state>) // state_accessor(<elem>, <new-state>)
// -> <new-state> // -> <new-state>
// //
// `this' within <state_accessor> is set to toggler's context.
//
// NOTE: for single state toggling, 'none' will get passed to // NOTE: for single state toggling, 'none' will get passed to
// state_accessor to indicate an "empty" state... // state_accessor to indicate an "empty" state...
// NOTE: if elem is a function it will be called in the same context as // NOTE: if elem is a function it will be called in the same context as
// the toggler and is expected to return the element. // the toggler and is expected to return the element.
// //
//
// states can be:
// <state> - state string, equivalent to ['none', <state>]
// this will produce a bool toggler that will toggle
// a single state on and off.
// [<state>, ...] - 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 // 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 // later is enough, but as strict mode is not stable enough (sometimes
// works and sometimes does not), we can not reliably pass the element // works and sometimes does not), we can not reliably pass the element
// via 'this'. // via 'this'.
function Toggler(elem, state_accessor, states, callback_a, callback_b){ function Toggler(elem, state_accessor, states, callback_a, callback_b){
// normalize states... // 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... // normalize the callbacks...
if(callback_b === undefined){ if(callback_b === undefined){
var callback_pre = null var callback_pre = null
@ -120,8 +135,6 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){
var callback_post = 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 // NOTE: this needs to be strict so as to be able to distinguish
// between a method and a root context in a simple manner... // between a method and a root context in a simple manner...
var func = function(a, b){ 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 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??? // XXX is this correct???
var args = args2array(arguments).slice(2) 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... // we need to get the current state...
if(action == null || action == '?' || action == '!'){ if(action == null || action == '?' || action == '!'){
// get current state... // get current state...
var cur = state_accessor.call(e) var cur = state_accessor.call(this, e)
// just asking for info... // just asking for info...
if(action == '?'){ if(action == '?'){
@ -204,7 +228,7 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){
} }
// update the element... // update the element...
state_accessor.call(e, state) state_accessor.call(this, e, state)
// post callback... // post callback...
if(callback_post != null){ if(callback_post != null){
@ -217,21 +241,48 @@ function Toggler(elem, state_accessor, states, callback_a, callback_b){
return action return action
} }
func.states = states // XXX these are broken...
if(bool_action){ //func.states = states
func.doc = 'With no arguments this will toggle between "on" and '+ Object.defineProperty(func, 'states', {
'"off".\n'+ get: function(){
'If either "on" or "off" are given then this will switch '+ return typeof(states_getter) == typeof(function(){}) ?
'to that mode.\n'+ states_getter.apply(this)
'If "?" is given, this will return either "on" or "off" '+ : state_set
'depending on the current state.' },
}else{ set: function(value){
func.doc = 'With no arguments this will toggle between '+ state_set = states_getter = value
states +' in cycle.\n' + },
'if any of the state names or its number is given then that '+ })
'state is switched on.'+ Object.defineProperty(func, 'doc', {
'If "?" is given, this will return the current state.' 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.__proto__ = Toggler.prototype
func.constructor = Toggler func.constructor = Toggler
@ -244,13 +295,20 @@ Toggler.prototype.__proto__ = Function.prototype
// XXX this should be drop-in compatible with createCSSClassToggler(..) // XXX this should be drop-in compatible with createCSSClassToggler(..)
// test and replace... // test and replace...
function CSSClassToggler(elem, classes, callback_a, callback_b){ function CSSClassToggler(elem, classes, callback_a, callback_b){
// normalize the states... 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 classes = typeof(classes) == typeof('str') ? ['none', classes] : classes
// remove the dot from class names... // remove the dot from class names...
// NOTE: this is here because I've made the error of including a // NOTE: this is here because I've made the error of including a
// leading "." almost every time I use this after I forget // leading "." almost every time I use this after I forget
// the UI... // the UI...
classes = classes return classes
.map(function(e){ .map(function(e){
return e.split(' ') return e.split(' ')
.map(function(c){ .map(function(c){
@ -258,12 +316,25 @@ function CSSClassToggler(elem, classes, callback_a, callback_b){
return c[0] == '.' ? c.slice(1) : c return c[0] == '.' ? c.slice(1) : c
}).join(' ') }).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( var toggler = Toggler(
elem, elem,
function(state){ function(e, state){
'use strict' '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... // get the state...
if(state == null){ if(state == null){
var cur = 'none' 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_a,
callback_b) callback_b)

View File

@ -9,7 +9,7 @@
"height": 700, "height": 700,
"min_width": 400, "min_width": 400,
"min_height": 400, "min_height": 400,
"frame": false, "frame": true,
"toolbar": false, "toolbar": false,
"show": false "show": false
}, },

View File

@ -456,9 +456,9 @@ actions.Actions({
function(all){ this.focusImage(all == null ? 'last' : -1) }], function(all){ this.focusImage(all == null ? 'last' : -1) }],
// XXX these break if image at first/last position are not loaded (crop, group, ...) // XXX these break if image at first/last position are not loaded (crop, group, ...)
// XXX do we actually need these??? // XXX do we actually need these???
firstGlobalImage: ['Navigate/First globally image', firstGlobalImage: ['Navigate/First image globally',
function(){ this.firstImage(true) }], function(){ this.firstImage(true) }],
lastGlobalImage: ['Navigate/Last globally image', lastGlobalImage: ['Navigate/Last image globally',
function(){ this.lastImage(true) }], function(){ this.lastImage(true) }],
// XXX skip unloaded images... (groups?) // XXX skip unloaded images... (groups?)
@ -1160,14 +1160,8 @@ actions.Actions({
toggleTheme: ['Interface/Toggle viewer theme', toggleTheme: ['Interface/Toggle viewer theme',
CSSClassToggler( CSSClassToggler(
function(){ return this.ribbons.viewer }, function(){ return this.ribbons.viewer },
// XXX how do we get this live from config??? function(){ return this.config.themes },
//this.config.themes, function(state){ this.config.theme = state }) ],
[
'gray',
'dark',
'light',
],
function(state){ this.config['theme'] = state }) ],
setEmptyMsg: ['- Interface/Set message to be displayed when nothing is loaded.', setEmptyMsg: ['- Interface/Set message to be displayed when nothing is loaded.',
function(msg, help){ this.ribbons.setEmptyMsg(msg, help) }], function(msg, help){ this.ribbons.setEmptyMsg(msg, help) }],
@ -1717,7 +1711,11 @@ var ConfigLocalStorageActions = actions.Actions({
key = key || this.config['config-local-storage-key'] key = key || this.config['config-local-storage-key']
if(key && localStorage[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 // XXX add a symmetric equivalent to .prepareIndexForWrite(..) so as
// to enable features to load their data... // to enable features to load their data...
// XXX look inside... // XXX look inside...
loadIndex: ['File/Load index', loadIndex: ['- File/Load index',
function(path, logger){ function(path, logger){
var that = this var that = this
@ -3902,7 +3900,7 @@ var FileSystemLoaderActions = actions.Actions({
// XXX add a recursive option... // XXX add a recursive option...
// ...might also be nice to add sub-dirs to ribbons... // ...might also be nice to add sub-dirs to ribbons...
// XXX make image pattern more generic... // XXX make image pattern more generic...
loadImages: ['File/Load images', loadImages: ['- File/Load images',
function(path, logger){ function(path, logger){
if(path == null){ if(path == null){
return return
@ -3932,7 +3930,7 @@ var FileSystemLoaderActions = actions.Actions({
}], }],
// XXX auto-detect format or let the user chose... // XXX auto-detect format or let the user chose...
loadPath: ['File/Load path (STUB)', loadPath: ['- File/Load path (STUB)',
function(path, logger){ function(path, logger){
// XXX check if this.config['index-dir'] exists, if yes then // XXX check if this.config['index-dir'] exists, if yes then
// .loadIndex(..) else .loadImages(..) // .loadIndex(..) else .loadImages(..)
@ -4137,8 +4135,8 @@ var FileSystemLoaderUIActions = actions.Actions({
// to start from. // to start from.
// XXX should passing no path to this start browsing from the current // XXX should passing no path to this start browsing from the current
// path or from the root? // path or from the root?
loadIndex: [makeBrowseProxy('loadIndex')], loadIndex: ['File/Load index', makeBrowseProxy('loadIndex')],
loadImages: [makeBrowseProxy('loadImages')], loadImages: ['File/Load images', makeBrowseProxy('loadImages')],
}) })