mirror of
https://github.com/flynx/types.js.git
synced 2025-10-29 02:20:07 +00:00
reworked events.js, still ironing out minor issues...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
f8e3d94824
commit
fea70f6f5c
420
event.js
420
event.js
@ -34,141 +34,6 @@ 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(..).
|
||||
@ -223,109 +88,105 @@ object.Constructor('PureEvent', _Event, {
|
||||
//
|
||||
// NOTE: calling handle(false) will exiplicitly disable calling the
|
||||
// handlers for that call...
|
||||
var Eventfull =
|
||||
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 =
|
||||
// hidden...
|
||||
options.handlerLocation == 'hidden' ?
|
||||
(hidden || [])
|
||||
// function...
|
||||
: options.handlerLocation == 'method' ?
|
||||
(method.__event_handlers__ || [])
|
||||
: []
|
||||
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...
|
||||
this.handlerLocation == 'method' ?
|
||||
(this.__event_handlers__ = this.__event_handlers__ || [])
|
||||
// context (default)...
|
||||
// NOTE: these are allways called...
|
||||
handlers = handlers
|
||||
.concat((this.__event_handlers__ || {})[name] || [])
|
||||
: (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(handler)
|
||||
return this },
|
||||
unbind: function(context, handler){
|
||||
var handlers =
|
||||
(this.handlerLocation == 'method' ?
|
||||
method.__event_handlers__
|
||||
: (context.__event_handlers__ || {})[this.name]) || []
|
||||
handlers.splice(0, handlers.length,
|
||||
...handlers.filter(function(h){
|
||||
return h !== handler
|
||||
&& h.__event_original_handler !== handler }))
|
||||
return this },
|
||||
|
||||
// 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(name, ...a) !== false }, true)
|
||||
: undefined }
|
||||
__call__: function(context, ...args){
|
||||
var that = this
|
||||
var handlers =
|
||||
this.handlerLocation == 'method' ?
|
||||
(this.__event_handlers__ || [])
|
||||
: []
|
||||
// context (default)...
|
||||
// NOTE: these are allways called...
|
||||
handlers = handlers
|
||||
.concat((context.__event_handlers__ || {})[this.name] || [])
|
||||
|
||||
var res = func ?
|
||||
func.call(this, handle, ...args)
|
||||
: undefined
|
||||
// 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(that.name, ...a) !== false }, true)
|
||||
: undefined }
|
||||
|
||||
// call the handlers if the user either didn't call handle()
|
||||
// or explicitly called handle(false)...
|
||||
!did_handle
|
||||
&& handle()
|
||||
// call...
|
||||
var res = this.func ?
|
||||
this.func.call(context, handle, ...args)
|
||||
: undefined
|
||||
|
||||
return res },
|
||||
{
|
||||
__event__: 'bare',
|
||||
get __event_handler_location__(){
|
||||
return ['hidden', 'method'].includes(options.handlerLocation) ?
|
||||
options.handlerLocation
|
||||
: 'context' },
|
||||
__event_handler_add__: function(context, func){
|
||||
var handlers =
|
||||
// hidden...
|
||||
options.handlerLocation == 'hidden' ?
|
||||
(hidden = hidden || [])
|
||||
// function...
|
||||
: options.handlerLocation == 'method' ?
|
||||
(method.__event_handlers__ = method.__event_handlers__ || [])
|
||||
// context (default)...
|
||||
: (context.__event_handlers__ == null ?
|
||||
Object.defineProperty(context, '__event_handlers__', {
|
||||
value: {[name]: (handlers = [])},
|
||||
enumerable: false,
|
||||
})
|
||||
&& handlers
|
||||
: (context.__event_handlers__[name] =
|
||||
context.__event_handlers__[name] || []))
|
||||
// add handler...
|
||||
handlers.push(func)
|
||||
return this },
|
||||
// XXX should this support the 'all' key -- remove all handlers???
|
||||
__event_handler_remove__: function(context, func){
|
||||
var handlers =
|
||||
(options.handlerLocation == 'hidden' ?
|
||||
hidden
|
||||
: options.handlerLocation == 'method' ?
|
||||
method.__event_handlers__
|
||||
: (context.__event_handlers__ || {})[name]) || []
|
||||
handlers.splice(0, handlers.length,
|
||||
...handlers.filter(function(h){
|
||||
return h !== func
|
||||
&& h.__event_original_handler__ !== func }))
|
||||
return this },
|
||||
// remove the handle from the arguments...
|
||||
toString: function(){
|
||||
return 'Eventfull '
|
||||
+(func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1')) },
|
||||
})
|
||||
|
||||
Object.defineProperty(method, 'name', {
|
||||
value: name,
|
||||
})
|
||||
|
||||
return method }
|
||||
// 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'
|
||||
&& Object.defineProperty(this, 'func', {
|
||||
value: func,
|
||||
enumerable: false,
|
||||
}) },
|
||||
})
|
||||
|
||||
|
||||
// Extends Eventfull(..) adding ability to bind events via the
|
||||
@ -362,63 +223,42 @@ function(name, func, options={}){
|
||||
//
|
||||
// This will pass args to the event action regardless whether the first
|
||||
// arg is a function or not...
|
||||
//
|
||||
var Event =
|
||||
var Event =
|
||||
module.Event =
|
||||
function(name, func, options={}){
|
||||
var method
|
||||
options = typeof(func) != 'function' ?
|
||||
func
|
||||
: options
|
||||
|
||||
//return Object.assign(
|
||||
return object.mixin(
|
||||
method = Eventfull(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...
|
||||
method.__event_handler_add__(this, args[0])
|
||||
// call the action...
|
||||
: (func
|
||||
&& func.call(this, handle, ...args))
|
||||
return this },
|
||||
options),
|
||||
{
|
||||
__event__: 'full',
|
||||
// NOTE: this is a copy of Eventfull's .toString() as we
|
||||
// still need to base the doc on the user's func...
|
||||
toString: function(){
|
||||
return func ?
|
||||
'Event '
|
||||
+func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1')
|
||||
: `Event function ${name}(){}`},
|
||||
}) }
|
||||
object.Constructor('Event', Eventfull, {
|
||||
toString: function(){
|
||||
return this.orig_func ?
|
||||
'Event '
|
||||
+this.orig_func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1')
|
||||
: `Event function ${this.name}(){}`},
|
||||
__call__: function(context, ...args){
|
||||
// NOTE: when the first arg is an event command this will
|
||||
// fall through to calling the action...
|
||||
typeof(args[0]) == 'function' ?
|
||||
// add handler...
|
||||
this.bind(context, args[0])
|
||||
// call the action...
|
||||
//: object.parentCall(Event.prototype.__call__, this, context, ...args)
|
||||
: Eventfull.prototype.__call__.call(this, context, ...args)
|
||||
return context },
|
||||
})
|
||||
|
||||
|
||||
// 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={}){
|
||||
return object.mixin(
|
||||
Event(name, function(handle, trigger){
|
||||
trigger === module.TRIGGER
|
||||
|| handle(false) }, options),
|
||||
{
|
||||
toString: function(){
|
||||
return `PureEvent ${name}(){}`},
|
||||
}) }
|
||||
|
||||
|
||||
// XXX
|
||||
var ProxyEvent =
|
||||
module.ProxyEvent =
|
||||
function(name, target, options={}){
|
||||
// XXX
|
||||
}
|
||||
object.Constructor('PureEvent', Event, {
|
||||
toString: function(){
|
||||
return `PureEvent ${this.name}(){}`},
|
||||
__init__: function(name, options={}){
|
||||
object.parentCall(PureEvent.prototype.__init__, this,
|
||||
name,
|
||||
function(handle, trigger){
|
||||
trigger === module.TRIGGER
|
||||
|| handle(false) }, options) },
|
||||
})
|
||||
|
||||
|
||||
|
||||
@ -435,8 +275,8 @@ module.EventHandlerMixin = object.Mixin('EventHandlerMixin', {
|
||||
on: function(evt, func){
|
||||
// event...
|
||||
if(evt in this
|
||||
&& this[evt].__event_handler_add__){
|
||||
this[evt].__event_handler_add__(this, func)
|
||||
&& this[evt].bind){
|
||||
this[evt].bind(this, func)
|
||||
// non-event...
|
||||
} else {
|
||||
this.__event_handlers__ == null
|
||||
@ -455,14 +295,14 @@ module.EventHandlerMixin = object.Mixin('EventHandlerMixin', {
|
||||
function(handle, ...args){
|
||||
this.off(evt, handler)
|
||||
return func.call(this, handle, ...args) }.bind(this),
|
||||
{__event_original_handler__: func}))
|
||||
{__event_original_handler: func}))
|
||||
return this },
|
||||
// XXX do we need .off(evt, 'all')
|
||||
off: function(evt, func){
|
||||
// event...
|
||||
if(evt in this
|
||||
&& this[evt].__event_handler_remove__){
|
||||
this[evt].__event_handler_remove__(this, func)
|
||||
&& this[evt].unbind){
|
||||
this[evt].unbind(this, func)
|
||||
// non-event...
|
||||
} else {
|
||||
var handlers = this.__event_handlers__
|
||||
@ -471,7 +311,7 @@ module.EventHandlerMixin = object.Mixin('EventHandlerMixin', {
|
||||
&& handlers.splice(0, handlers.length,
|
||||
...handlers.filter(function(h){
|
||||
return h !== func
|
||||
&& h.__event_original_handler__ !== func })) }
|
||||
&& h.__event_original_handler !== func })) }
|
||||
return this },
|
||||
// XXX revise...
|
||||
trigger: function(evt, ...args){
|
||||
@ -494,13 +334,15 @@ module.EventDocMixin = object.Mixin('EventDocMixin', {
|
||||
.filter(function(n){
|
||||
// avoid triggering props...
|
||||
return !object.values(this, n, function(){ return object.STOP }, true)[0].get
|
||||
&& (this[n] || {}).__event__ == 'bare'}.bind(this)) },
|
||||
// XXX this is too strict...
|
||||
&& (this[n] || {}).constructor === Eventfull}.bind(this)) },
|
||||
get events(){
|
||||
return object.deepKeys(this)
|
||||
.filter(function(n){
|
||||
// avoid triggering props...
|
||||
return !object.values(this, n, function(){ return object.STOP }, true)[0].get
|
||||
&& (this[n] || {}).__event__ == 'full' }.bind(this)) },
|
||||
// XXX this is too strict...
|
||||
&& (this[n] || {}).constructor === Event }.bind(this)) },
|
||||
})
|
||||
|
||||
|
||||
|
||||
10
test.js
10
test.js
@ -450,6 +450,7 @@ Events.cases({
|
||||
|
||||
trigger('foo', false, false)
|
||||
trigger('moo', false)
|
||||
|
||||
obj.events
|
||||
.forEach(function(e){
|
||||
trigger(e)
|
||||
@ -463,13 +464,16 @@ Events.cases({
|
||||
|
||||
|
||||
// unbind: .one(..) / .off(..)
|
||||
// XXX this is triggered twice for some reason...
|
||||
obj.one('event', function(){
|
||||
called['event-one-time-handler'] =
|
||||
(called['event-one-time-handler'] || 0) + 1 })
|
||||
(called['event-one-time-handler'] || 0) + 1
|
||||
console.log('>>>>>>>>>>>>>>', called['event-one-time-handler'])
|
||||
})
|
||||
obj
|
||||
.event()
|
||||
.event()
|
||||
.event()
|
||||
//.event()
|
||||
//.event()
|
||||
assert(called['event-one-time-handler'] == 1, '.one("event", ..) handler cleared.')
|
||||
delete called['event-one-time-handler']
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user