mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 02:10:08 +00:00
moved .debounce(..) to core + some refactoring and tweaking...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
ac20bb957e
commit
50e523718f
@ -847,6 +847,30 @@ module.Cache = ImageGridFeatures.Feature({
|
||||
//---------------------------------------------------------------------
|
||||
// Timers...
|
||||
|
||||
var debounce =
|
||||
module.debounce =
|
||||
function(timeout, func){
|
||||
func = timeout instanceof Function ? timeout : func
|
||||
var f = function(...args){
|
||||
return this.debounceActionCall({
|
||||
action: func,
|
||||
args: args,
|
||||
tag: func instanceof Function ?
|
||||
(func.name || f.name)
|
||||
: func,
|
||||
timeout: timeout instanceof Function ? null : timeout,
|
||||
returns: 'cached',
|
||||
retrigger: true,
|
||||
})
|
||||
}
|
||||
f.toString = function(){
|
||||
return `// debounced...\n${doc([func.toString()])}`
|
||||
}
|
||||
// NOTE: this will force Action(..) to set the .name to the action name...
|
||||
Object.defineProperty(f, 'name', { value: '<action-name>' })
|
||||
return f
|
||||
}
|
||||
|
||||
var TimersActions = actions.Actions({
|
||||
config: {
|
||||
//
|
||||
@ -861,6 +885,11 @@ var TimersActions = actions.Actions({
|
||||
// ...
|
||||
// }
|
||||
'persistent-intervals': null,
|
||||
|
||||
|
||||
// A timeout to wait between calls to actions triggered via
|
||||
// .debounce(..)
|
||||
'debounce-action-timeout': 200,
|
||||
},
|
||||
|
||||
// XXX should we store more metadata (ms?) and provide introspection
|
||||
@ -1068,6 +1097,174 @@ var TimersActions = actions.Actions({
|
||||
Event(function(){
|
||||
// XXX
|
||||
})],
|
||||
|
||||
// Action debounce...
|
||||
//
|
||||
debounceActionCall: ['- System/',
|
||||
doc`Debounce the action call...
|
||||
|
||||
.debounceActionCall(call)
|
||||
-> result
|
||||
|
||||
Call format:
|
||||
{
|
||||
// action name of function to be called...
|
||||
action: <name> | <function>,
|
||||
|
||||
// arguments to be passed to action/function...
|
||||
args: <array>,
|
||||
|
||||
// tag to identify the call...
|
||||
//
|
||||
// Defaults to action name, optional for actions and
|
||||
// required for functions...
|
||||
tag: <tag> | null,
|
||||
|
||||
// timeout to drop calls within (optional).
|
||||
//
|
||||
// defaults to .config['debounce-action-timeout'] then
|
||||
// to 200.
|
||||
timeout: <number> | null,
|
||||
|
||||
// controls how action return value is handled:
|
||||
//
|
||||
// Values:
|
||||
// 'cached' - cache the value and return it for
|
||||
// every call within the timeout.
|
||||
// 'dropped' - ignore return values.
|
||||
//
|
||||
// NOTE: this is designed to produce uniform results
|
||||
// without and exceptions.
|
||||
returns: 'cached' | 'dropped',
|
||||
|
||||
// if true re trigger the action after timeout if it was
|
||||
// called after the initial call but before the timeout
|
||||
// ended...
|
||||
retrigger: <bool>,
|
||||
}
|
||||
|
||||
NOTE: this does not affect actions called directly in any way.
|
||||
`,
|
||||
function(call){
|
||||
var action = call.action
|
||||
var args = call.args || []
|
||||
var tag = call.tag || call.action
|
||||
var timeout = call.timeout
|
||||
|| this.config['debounce-action-timeout']
|
||||
|| 200
|
||||
var returns = call.returns || 'cached'
|
||||
var retrigger = call.retrigger || false
|
||||
|
||||
// when debouncing a function a tag is required...
|
||||
if(tag instanceof Function){
|
||||
throw new TypeError('debounce: when passing a function a tag is required.')
|
||||
}
|
||||
|
||||
var attr = '__debounce_'+ tag
|
||||
|
||||
// repeated call...
|
||||
if(this[attr]){
|
||||
if(retrigger){
|
||||
this[attr +'_retriggered'] = true
|
||||
}
|
||||
|
||||
var res = returns == 'cached' ?
|
||||
this[attr +'_return']
|
||||
: undefined
|
||||
|
||||
// setup and first call...
|
||||
} else {
|
||||
// NOTE: we are ignoring the return value here so as to
|
||||
// make the first and repeated call uniform...
|
||||
var context = this
|
||||
var res = (action instanceof Function ?
|
||||
action
|
||||
: action.split('.')
|
||||
.reduce(function(res, e){
|
||||
context = res
|
||||
return res[e]
|
||||
}, this))
|
||||
.call(context, ...args)
|
||||
|
||||
// cache the return value...
|
||||
if(returns == 'cached'){
|
||||
this[attr +'_return'] = res
|
||||
|
||||
// drop the return value...
|
||||
} else {
|
||||
res = undefined
|
||||
}
|
||||
|
||||
this[attr] = setTimeout(function(){
|
||||
delete this[attr]
|
||||
delete this[attr +'_return']
|
||||
|
||||
// retrigger...
|
||||
if(this[attr +'_retriggered']){
|
||||
delete this[attr +'_retriggered']
|
||||
|
||||
tag == action ?
|
||||
this.debounce(timeout, action, ...args)
|
||||
: this.debounce(timeout, tag, action, ...args)
|
||||
}
|
||||
}.bind(this), timeout)
|
||||
}
|
||||
|
||||
return res
|
||||
}],
|
||||
|
||||
// shorthand...
|
||||
debounce: ['- System/',
|
||||
doc`Debounce action call...
|
||||
|
||||
Debounce call an action...
|
||||
.debounce(action, ...)
|
||||
.debounce(timeout, action, ...)
|
||||
.debounce(tag, action, ...)
|
||||
.debounce(timeout, tag, action, ...)
|
||||
|
||||
Debounce call a function...
|
||||
.debounce(tag, func, ...)
|
||||
.debounce(timeout, tag, func, ...)
|
||||
|
||||
Protocol:
|
||||
- call
|
||||
- start timeout timer
|
||||
- trigger target action
|
||||
- drop
|
||||
- call (within timeout)
|
||||
- drop
|
||||
- re-trigger when timer ends
|
||||
|
||||
|
||||
NOTE: when using a tag, it must not resolve to and action, i.e.
|
||||
this[tag] must not be callable...
|
||||
NOTE: this ignores action return value and returns this...
|
||||
NOTE: this is a shorthand to .debounceActionCall(..)
|
||||
`,
|
||||
function(...args){
|
||||
// parse the args...
|
||||
var timeout = typeof(args[0]) == typeof(123) ?
|
||||
args.shift()
|
||||
: (this.config['debounce-action-timeout'] || 200)
|
||||
// NOTE: this[tag] must not be callable, otherwise we treat it
|
||||
// as an action...
|
||||
var tag = (args[0] instanceof Function
|
||||
|| this[args[0]] instanceof Function) ?
|
||||
args[0]
|
||||
: args.shift()
|
||||
var action = args.shift()
|
||||
|
||||
return this.debounceActionCall({
|
||||
action: action,
|
||||
args: args,
|
||||
tag: tag,
|
||||
timeout: timeout,
|
||||
// XXX
|
||||
returns: 'dropped',
|
||||
retrigger: true,
|
||||
})
|
||||
}],
|
||||
})
|
||||
|
||||
var Timers =
|
||||
|
||||
@ -48,6 +48,18 @@ var ExampleActions = actions.Actions({
|
||||
// XXX
|
||||
}],
|
||||
|
||||
exampleActionDebounced: ['Test/Action (debounced)',
|
||||
core.doc`This is .exampleAction(..) debounced.
|
||||
`,
|
||||
core.debounce('exampleAction')],
|
||||
exampleDebouncedAction: ['Test/Custom debounced action',
|
||||
core.debounce(1000, function(...args){
|
||||
console.log('exampleDebouncedAction: This can\'t be called more often than once per 1 second.')
|
||||
console.log('exampleDebouncedAction: note that within this second only the original return value is returned.')
|
||||
console.log(' <', args)
|
||||
return args
|
||||
})],
|
||||
|
||||
// a normal method...
|
||||
exampleMethod: function(){
|
||||
console.log('example method:', [].slice.call(arguments))
|
||||
|
||||
@ -487,11 +487,6 @@ var KeyboardActions = actions.Actions({
|
||||
// The amount of keyboard "quiet" time to wait for when
|
||||
// .pauseKeyboardRepeat(..) is called...
|
||||
'keyboard-repeat-pause-check': 100,
|
||||
|
||||
|
||||
// A timeout to wait between calls to actions triggered via
|
||||
// .debounce(..)
|
||||
'debounce-action-timeout': 200,
|
||||
},
|
||||
|
||||
get keybindings(){
|
||||
@ -512,76 +507,6 @@ var KeyboardActions = actions.Actions({
|
||||
function(){ return that.dom })
|
||||
return kb },
|
||||
|
||||
debounce: ['- Interface/',
|
||||
core.doc`Debounce action call...
|
||||
|
||||
Debounce call an action...
|
||||
.debounce(action, ...)
|
||||
.debounce(timeout, action, ...)
|
||||
.debounce(tag, action, ...)
|
||||
.debounce(timeout, tag, action, ...)
|
||||
|
||||
Debounce call a function...
|
||||
.debounce(tag, func, ...)
|
||||
.debounce(timeout, tag, func, ...)
|
||||
|
||||
NOTE: when using a tag, it must not resolve to and action, i.e.
|
||||
this[tag] must not be callable...
|
||||
NOTE: this ignores action return value and returns this...
|
||||
`,
|
||||
function(...args){
|
||||
// parse the args...
|
||||
var timeout = typeof(args[0]) == typeof(123) ?
|
||||
args.shift()
|
||||
: (this.config['debounce-action-timeout'] || 200)
|
||||
// NOTE: this[tag] must not be callable, otherwise we treat it
|
||||
// as an action...
|
||||
var tag = (args[0] instanceof Function
|
||||
|| this[args[0]] instanceof Function) ?
|
||||
args[0]
|
||||
: args.shift()
|
||||
var action = args.shift()
|
||||
|
||||
// when debouncing a function a tag is required...
|
||||
if(tag instanceof Function){
|
||||
throw new TypeError('debounce: when passing a function a tag is required.')
|
||||
}
|
||||
|
||||
var attr = '__debounce_'+ tag
|
||||
|
||||
// repeated call...
|
||||
if(this[attr]){
|
||||
this[attr +'_retriggered'] = true
|
||||
|
||||
// setup and first call...
|
||||
} else {
|
||||
// NOTE: we are ignoring the return value here so as to
|
||||
// make the first and repeated call uniform...
|
||||
var context = this
|
||||
;(action instanceof Function ?
|
||||
action
|
||||
: action.split('.')
|
||||
.reduce(function(res, e){
|
||||
context = res
|
||||
return res[e]
|
||||
}, this))
|
||||
.call(context, ...args)
|
||||
|
||||
this[attr] = setTimeout(function(){
|
||||
delete this[attr]
|
||||
|
||||
// retrigger...
|
||||
if(this[attr +'_retriggered']){
|
||||
delete this[attr +'_retriggered']
|
||||
|
||||
tag == action ?
|
||||
this.debounce(timeout, action, ...args)
|
||||
: this.debounce(timeout, tag, action, ...args)
|
||||
}
|
||||
}.bind(this), timeout)
|
||||
}
|
||||
}],
|
||||
|
||||
// Add debounce support to keyboard handling...
|
||||
//
|
||||
// Syntax:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user