mirror of
https://github.com/flynx/types.js.git
synced 2025-10-29 02:20:07 +00:00
reworking event.js to be more extensible...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
6e39dc9d3b
commit
f8e3d94824
169
event.js
169
event.js
@ -29,6 +29,146 @@ object.Constructor('EventCommand', {
|
||||
Object.assign(this, data, {name}) },
|
||||
})
|
||||
|
||||
|
||||
module.TRIGGER = module.EventCommand('TRIGGER')
|
||||
|
||||
|
||||
|
||||
// XXX would be nice to have an event object...
|
||||
var _Eventfull =
|
||||
module._Eventfull =
|
||||
object.Constructor('Eventfull', {
|
||||
|
||||
handlerLocation: 'context',
|
||||
|
||||
name: null,
|
||||
func: null,
|
||||
|
||||
toString: function(){
|
||||
return this.func ?
|
||||
`${this.constructor.name} `
|
||||
+(this.func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1'))
|
||||
: `${this.constructor.name} function ${this.name}(){}` },
|
||||
|
||||
__event_handlers__: null,
|
||||
bind: function(context, handler){
|
||||
var handlers =
|
||||
// local...
|
||||
options.handlerLocation == 'method' ?
|
||||
(this.__event_handlers__ = this.__event_handlers__ || [])
|
||||
// context (default)...
|
||||
: (context.__event_handlers__ == null ?
|
||||
Object.defineProperty(context, '__event_handlers__', {
|
||||
value: {[this.name]: (handlers = [])},
|
||||
enumerable: false,
|
||||
})
|
||||
&& handlers
|
||||
: (context.__event_handlers__[this.name] =
|
||||
context.__event_handlers__[this.name] || []))
|
||||
// add handler...
|
||||
handlers.push(func)
|
||||
return this },
|
||||
unbind: function(context, handler){
|
||||
var handlers =
|
||||
(options.handlerLocation == 'method' ?
|
||||
method.__event_handlers__
|
||||
: (context.__event_handlers__ || {})[this.name]) || []
|
||||
handlers.splice(0, handlers.length,
|
||||
...handlers.filter(function(h){
|
||||
return h !== this.func
|
||||
&& h.__event_original_handler__ !== func }))
|
||||
return this },
|
||||
|
||||
__call__: function(context, ...args){
|
||||
var handlers =
|
||||
this.handlerLocation == 'method' ?
|
||||
(this.__event_handlers__ || [])
|
||||
: []
|
||||
// context (default)...
|
||||
// NOTE: these are allways called...
|
||||
handlers = handlers
|
||||
.concat((context.__event_handlers__ || {})[this.name] || [])
|
||||
|
||||
// NOTE: this will stop event handling if one of the handlers
|
||||
// explicitly returns false...
|
||||
// NOTE: if the user does not call handle() it will be called
|
||||
// after the event action is done but before it returns...
|
||||
// NOTE: to explicitly disable calling the handlers func must
|
||||
// call handle(false)
|
||||
var did_handle = false
|
||||
var handle = function(run=true){
|
||||
did_handle = run === false
|
||||
var a = args[0] instanceof EventCommand ?
|
||||
args.slice(1)
|
||||
: args
|
||||
return run ?
|
||||
handlers
|
||||
.reduce(function(res, handler){
|
||||
return res === true
|
||||
&& handler(this.name, ...a) !== false }, true)
|
||||
: undefined }
|
||||
|
||||
// call...
|
||||
var res = this.func ?
|
||||
this.func.call(context, handle, ...args)
|
||||
: undefined
|
||||
|
||||
// call the handlers if the user either didn't call handle()
|
||||
// or explicitly called handle(false)...
|
||||
!did_handle
|
||||
&& handle()
|
||||
return res },
|
||||
|
||||
__init__: function(name, func, options={}){
|
||||
options = func && typeof(func) != 'function' ?
|
||||
func
|
||||
: options
|
||||
Object.assign(this, options)
|
||||
Object.defineProperty(this, 'name', { value: name })
|
||||
func && typeof(func) == 'function'
|
||||
&& (this.func = func) },
|
||||
})
|
||||
|
||||
|
||||
// XXX test...
|
||||
// XXX rename???
|
||||
var _Event =
|
||||
module._Event =
|
||||
object.Constructor('Event', _Eventfull, {
|
||||
toString: function(){
|
||||
return this.orig_func ?
|
||||
'Event '
|
||||
+this.orig_func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1')
|
||||
: `Event function ${this.name}(){}`},
|
||||
__init__: function(name, func, options={}){
|
||||
var that = this
|
||||
this.orig_func = func
|
||||
object.parentCall(_Event.prototype.__init__, this,
|
||||
name,
|
||||
function(handle, ...args){
|
||||
// NOTE: when the first arg is an event command this will
|
||||
// fall through to calling the action...
|
||||
typeof(args[0]) == 'function' ?
|
||||
// add handler...
|
||||
that.bind(this, args[0])
|
||||
// call the action...
|
||||
: (func
|
||||
&& func.call(this, handle, ...args))
|
||||
return this },
|
||||
options) }
|
||||
})
|
||||
|
||||
|
||||
var _PureEvent =
|
||||
module._PureEvent =
|
||||
object.Constructor('PureEvent', _Event, {
|
||||
// XXX
|
||||
})
|
||||
|
||||
|
||||
|
||||
// Create an "eventfull" method...
|
||||
//
|
||||
// The resulting method can be either called directly or via .trigger(..).
|
||||
@ -87,11 +227,9 @@ var Eventfull =
|
||||
module.Eventfull =
|
||||
function(name, func, options={}){
|
||||
var hidden
|
||||
|
||||
options = func && typeof(func) != 'function' ?
|
||||
func
|
||||
: options
|
||||
|
||||
var method = object.mixin(
|
||||
function(...args){
|
||||
var handlers =
|
||||
@ -101,8 +239,11 @@ function(name, func, options={}){
|
||||
// function...
|
||||
: options.handlerLocation == 'method' ?
|
||||
(method.__event_handlers__ || [])
|
||||
// context (default)...
|
||||
: ((this.__event_handlers__ || {})[name] || [])
|
||||
: []
|
||||
// context (default)...
|
||||
// NOTE: these are allways called...
|
||||
handlers = handlers
|
||||
.concat((this.__event_handlers__ || {})[name] || [])
|
||||
|
||||
// NOTE: this will stop event handling if one of the handlers
|
||||
// explicitly returns false...
|
||||
@ -187,8 +328,6 @@ function(name, func, options={}){
|
||||
|
||||
|
||||
|
||||
module.TRIGGER = module.EventCommand('TRIGGER')
|
||||
|
||||
// Extends Eventfull(..) adding ability to bind events via the
|
||||
// resulting method directly by passing it a function...
|
||||
//
|
||||
@ -259,8 +398,8 @@ function(name, func, options={}){
|
||||
}) }
|
||||
|
||||
|
||||
// Like Event(..) but produces an event that can only be triggered via
|
||||
// .trigger(name, ...), calling this is a no-op...
|
||||
// Like Event(..) but produces an event method that can only be triggered
|
||||
// via .trigger(name, ...), calling this is a no-op...
|
||||
var PureEvent =
|
||||
module.PureEvent =
|
||||
function(name, options={}){
|
||||
@ -334,14 +473,14 @@ module.EventHandlerMixin = object.Mixin('EventHandlerMixin', {
|
||||
return h !== func
|
||||
&& h.__event_original_handler__ !== func })) }
|
||||
return this },
|
||||
// XXX revise...
|
||||
trigger: function(evt, ...args){
|
||||
// local handler...
|
||||
evt in this
|
||||
&& this[evt](module.TRIGGER, ...args)
|
||||
// global events...
|
||||
this.__event_handlers__
|
||||
&& (this.__event_handlers__[evt] || [])
|
||||
.forEach(function(h){ h(evt, ...args) })
|
||||
evt in this ?
|
||||
// XXX add a better check...
|
||||
this[evt](module.TRIGGER, ...args)
|
||||
: this.__event_handlers__
|
||||
&& (this.__event_handlers__[evt] || [])
|
||||
.forEach(function(h){ h(evt, ...args) })
|
||||
return this },
|
||||
})
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ig-types",
|
||||
"version": "5.0.22",
|
||||
"version": "5.0.23",
|
||||
"description": "Generic JavaScript types and type extensions...",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
86
runner.js
86
runner.js
@ -44,6 +44,15 @@ module.STOP = object.STOP
|
||||
// release their spot in the pool.
|
||||
//
|
||||
// XXX need to configure to run a specific amount of jobs on each start...
|
||||
// XXX triggering .tasksAdded(..)...
|
||||
// there are two ways to go here:
|
||||
// - wrap the queue in a proxy
|
||||
// + transparent-ish
|
||||
// - reported in a confusing way by node/chrome...
|
||||
// - force the user to use specific API
|
||||
// - not consistent -- a task can be added via .add(..) and
|
||||
// the Array interface and only .add(..) will trigger the
|
||||
// proper events...
|
||||
var Queue =
|
||||
module.Queue =
|
||||
object.Constructor('Queue', Array, {
|
||||
@ -113,10 +122,12 @@ object.Constructor('Queue', Array, {
|
||||
|
||||
// events...
|
||||
//
|
||||
// .tasksAdded(func(evt, [task, ..]))
|
||||
// .taskStarting(func(evt, task))
|
||||
// .taskCompleted(func(evt, task))
|
||||
// .queueEmpty(func(evt))
|
||||
//
|
||||
tasksAdded: events.PureEvent('tasksAdded'),
|
||||
taskStarting: events.PureEvent('taskStarting'),
|
||||
taskCompleted: events.PureEvent('taskCompleted'),
|
||||
queueEmpty: events.PureEvent('queueEmpty'),
|
||||
@ -124,22 +135,13 @@ object.Constructor('Queue', Array, {
|
||||
|
||||
// NOTE: each handler will get called once when the next time the
|
||||
// queue is emptied...
|
||||
// XXX should this trigger on empty or on stop???
|
||||
then: function(func){
|
||||
var that = this
|
||||
return new Promise(function(resolve, reject){
|
||||
that.one('queueEmpty', function(){
|
||||
resolve(func()) }) }) },
|
||||
|
||||
|
||||
// helpers...
|
||||
//
|
||||
// move tasks to head/tail of queue resp.
|
||||
prioritize: function(...tasks){
|
||||
return this.sortAs(tasks) },
|
||||
delay: function(...tasks){
|
||||
return this.sortAs(tasks, true) },
|
||||
|
||||
|
||||
// Runner API...
|
||||
//
|
||||
// Run the given task type...
|
||||
@ -210,9 +212,7 @@ object.Constructor('Queue', Array, {
|
||||
run()
|
||||
: setTimeout(run, 0))
|
||||
return this },
|
||||
|
||||
// run one task from queue...
|
||||
//
|
||||
// NOTE: this does not care about .state...
|
||||
runTask: function(next){
|
||||
var that = this
|
||||
@ -246,8 +246,6 @@ object.Constructor('Queue', Array, {
|
||||
res = res instanceof module.STOP ?
|
||||
res.value
|
||||
: res
|
||||
stop
|
||||
&& this.stop()
|
||||
|
||||
// handle task results...
|
||||
//
|
||||
@ -277,8 +275,7 @@ object.Constructor('Queue', Array, {
|
||||
// one post handler is enough...
|
||||
&& !running.includes(res)){
|
||||
running.push(res)
|
||||
res.finally(function(){
|
||||
runningDone() })
|
||||
res.finally(runningDone)
|
||||
|
||||
// re-queue tasks...
|
||||
} else if(typeof(res) == 'function'){
|
||||
@ -292,9 +289,64 @@ object.Constructor('Queue', Array, {
|
||||
this.length == 0
|
||||
&& this.trigger('queueEmpty')
|
||||
|
||||
stop
|
||||
&& this.stop()
|
||||
|
||||
return this },
|
||||
|
||||
|
||||
// helpers...
|
||||
//
|
||||
// move tasks to head/tail of queue resp.
|
||||
prioritize: function(...tasks){
|
||||
return this.sortAs(tasks) },
|
||||
delay: function(...tasks){
|
||||
return this.sortAs(tasks, true) },
|
||||
|
||||
|
||||
// trigger .tasksAdded(..)...
|
||||
//
|
||||
// NOTE: adding tasks via the [..] notation will not trigger the
|
||||
// event...
|
||||
push: function(...tasks){
|
||||
res = object.parentCall(Queue.prototype.push, this, ...tasks)
|
||||
this.trigger('tasksAdded', tasks)
|
||||
return res },
|
||||
unsift: function(...tasks){
|
||||
res = object.parentCall(Queue.prototype.unshift, this, ...tasks)
|
||||
this.trigger('tasksAdded', tasks)
|
||||
return res },
|
||||
splice: function(...args){
|
||||
res = object.parentCall(Queue.prototype.splice, this, ...args)
|
||||
var tasks = args.slise(2)
|
||||
tasks.length > 0
|
||||
&& this.trigger('tasksAdded', tasks)
|
||||
return res },
|
||||
// convenience...
|
||||
add: function(...tasks){
|
||||
this.push(...tasks)
|
||||
return this },
|
||||
|
||||
|
||||
/*/ trigger .tasksAdded(..) when stuff is added to queue...
|
||||
// XXX EXPERIMENTAL...
|
||||
// XXX this messes up how devtools/node print this object...
|
||||
// ...everything works as expected but looks ugly...
|
||||
// XXX another potential issue here is the opposite of the overload
|
||||
// approach, this is too strong and we will need to go around
|
||||
// it if we need for instance to shift around around...
|
||||
__new__: function(context, ...args){
|
||||
return new Proxy(
|
||||
Reflect.construct(Queue.__proto__, args, Queue),
|
||||
{
|
||||
set: function(queue, prop, value, receiver){
|
||||
parseInt(prop) == prop
|
||||
&& queue.trigger('tasksAdded', [value])
|
||||
return Reflect.set(...arguments) },
|
||||
}) },
|
||||
//*/
|
||||
|
||||
|
||||
// constructor argument handling...
|
||||
//
|
||||
// Queue()
|
||||
@ -314,6 +366,8 @@ object.Constructor('Queue', Array, {
|
||||
&& typeof(this[0]) != 'function'
|
||||
&& typeof(this[0].finally) != 'function'){
|
||||
Object.assign(this, this.shift()) }
|
||||
this.length > 0
|
||||
&& this.trigger('tasksAdded', [...this])
|
||||
// see if we need to start...
|
||||
this.__run_tasks__() },
|
||||
}))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user