mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 18:30:09 +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...
|
// 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({
|
var TimersActions = actions.Actions({
|
||||||
config: {
|
config: {
|
||||||
//
|
//
|
||||||
@ -861,6 +885,11 @@ var TimersActions = actions.Actions({
|
|||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
'persistent-intervals': null,
|
'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
|
// XXX should we store more metadata (ms?) and provide introspection
|
||||||
@ -1068,6 +1097,174 @@ var TimersActions = actions.Actions({
|
|||||||
Event(function(){
|
Event(function(){
|
||||||
// XXX
|
// 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 =
|
var Timers =
|
||||||
|
|||||||
@ -48,6 +48,18 @@ var ExampleActions = actions.Actions({
|
|||||||
// XXX
|
// 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...
|
// a normal method...
|
||||||
exampleMethod: function(){
|
exampleMethod: function(){
|
||||||
console.log('example method:', [].slice.call(arguments))
|
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
|
// The amount of keyboard "quiet" time to wait for when
|
||||||
// .pauseKeyboardRepeat(..) is called...
|
// .pauseKeyboardRepeat(..) is called...
|
||||||
'keyboard-repeat-pause-check': 100,
|
'keyboard-repeat-pause-check': 100,
|
||||||
|
|
||||||
|
|
||||||
// A timeout to wait between calls to actions triggered via
|
|
||||||
// .debounce(..)
|
|
||||||
'debounce-action-timeout': 200,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get keybindings(){
|
get keybindings(){
|
||||||
@ -512,76 +507,6 @@ var KeyboardActions = actions.Actions({
|
|||||||
function(){ return that.dom })
|
function(){ return that.dom })
|
||||||
return kb },
|
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...
|
// Add debounce support to keyboard handling...
|
||||||
//
|
//
|
||||||
// Syntax:
|
// Syntax:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user