2020-11-12 17:35:41 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*
|
2020-11-15 00:24:22 +03:00
|
|
|
* XXX is types/events the right place for this???
|
2020-11-13 13:37:10 +03:00
|
|
|
* XXX should we have .pre/.post events???
|
2020-11-15 00:24:22 +03:00
|
|
|
* XXX should we propogate event handling to parent/overloaded events???
|
2020-11-13 13:37:10 +03:00
|
|
|
*
|
2020-11-12 17:35:41 +03:00
|
|
|
**********************************************/ /* c8 ignore next 2 */
|
|
|
|
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
|
|
|
|
(function(require){ var module={} // make module AMD/node compatible...
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
var object = require('ig-object')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
2020-11-15 02:50:39 +03:00
|
|
|
// Event method wrappers...
|
2020-11-12 17:35:41 +03:00
|
|
|
|
2020-11-15 02:50:39 +03:00
|
|
|
// Create an "eventfull" method...
|
|
|
|
|
//
|
|
|
|
|
// The resulting method can be either called directly or via .trigger(..).
|
|
|
|
|
// Handlrs can be bound to it via .on(..) and unbound via .off(..) and
|
|
|
|
|
// calling it will trigger the handlers either after the user func(..)
|
|
|
|
|
// return or when the user calles the passed handler(..) function.
|
|
|
|
|
//
|
|
|
|
|
// bareEventMethod(name[, options])
|
|
|
|
|
// -> method
|
|
|
|
|
//
|
|
|
|
|
// bareEventMethod(name, func[, options])
|
|
|
|
|
// -> method
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// method(...args)
|
|
|
|
|
// -> ..
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// func(handle, ...args)
|
|
|
|
|
// -> ..
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// NOTE: calling handle(false) will exiplicitly disable calling the
|
|
|
|
|
// handlers for that call...
|
2020-11-15 00:24:22 +03:00
|
|
|
var bareEventMethod =
|
|
|
|
|
module.bareEventMethod =
|
|
|
|
|
function(name, func, options={}){
|
2020-11-12 17:35:41 +03:00
|
|
|
var hidden
|
|
|
|
|
var method
|
|
|
|
|
|
2020-11-15 00:24:22 +03:00
|
|
|
options = func && typeof(func) != 'function' ?
|
2020-11-12 17:35:41 +03:00
|
|
|
func
|
|
|
|
|
: options
|
|
|
|
|
|
|
|
|
|
return object.mixinFlat(
|
2020-11-15 00:24:22 +03:00
|
|
|
method = function(...args){
|
2020-11-12 17:35:41 +03:00
|
|
|
var handlers =
|
|
|
|
|
// hidden...
|
|
|
|
|
options.handlerLocation == 'hidden' ?
|
2020-11-15 00:24:22 +03:00
|
|
|
(hidden || [])
|
2020-11-12 17:35:41 +03:00
|
|
|
// function...
|
|
|
|
|
: options.handlerLocation == 'method' ?
|
2020-11-15 00:24:22 +03:00
|
|
|
(method.__event_handlers__ || [])
|
2020-11-12 17:35:41 +03:00
|
|
|
// context (default)...
|
2020-11-15 00:24:22 +03:00
|
|
|
: ((this.__event_handlers__ || {})[name] || [])
|
2020-11-12 17:35:41 +03:00
|
|
|
|
2020-11-15 00:24:22 +03:00
|
|
|
// NOTE: this will stop event handling if one of the handlers
|
|
|
|
|
// explicitly returns false...
|
2020-11-15 02:50:39 +03:00
|
|
|
// NOTE: if the user does not call handle() it will be called
|
|
|
|
|
// after the event action is done but before it returns...
|
2020-11-15 02:27:20 +03:00
|
|
|
// NOTE: to explicitly disable calling the handlers func must
|
|
|
|
|
// call handle(false)
|
|
|
|
|
// XXX should he user be able to control the args passed to
|
|
|
|
|
// the handlers???
|
|
|
|
|
var did_handle = false
|
|
|
|
|
var handle = function(skip=false){
|
|
|
|
|
did_handle = skip === true
|
|
|
|
|
return skip ?
|
|
|
|
|
undefined
|
|
|
|
|
: handlers
|
|
|
|
|
.reduce(function(res, handler){
|
|
|
|
|
return res === true
|
|
|
|
|
&& handler(name, ...args) !== false }, true) }
|
|
|
|
|
|
|
|
|
|
var res = func ?
|
|
|
|
|
func.call(this, handle, ...args)
|
|
|
|
|
: undefined
|
|
|
|
|
|
2020-11-15 02:50:39 +03:00
|
|
|
// call the handlers if the user either didn't call handle()
|
|
|
|
|
// or explicitly called handle(false)...
|
2020-11-15 02:27:20 +03:00
|
|
|
!did_handle
|
|
|
|
|
&& handle()
|
2020-11-12 17:35:41 +03:00
|
|
|
|
|
|
|
|
return res },
|
|
|
|
|
{
|
2020-11-15 00:24:22 +03:00
|
|
|
__event__: 'bare',
|
2020-11-12 17:35:41 +03:00
|
|
|
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)...
|
2020-11-15 00:24:22 +03:00
|
|
|
: ((context.__event_handlers__ = context.__event_handlers__ || {})[name] =
|
|
|
|
|
context.__event_handlers__[name] || [])
|
2020-11-12 17:35:41 +03:00
|
|
|
// add handler...
|
2020-11-15 00:24:22 +03:00
|
|
|
handlers.push(func)
|
2020-11-12 17:35:41 +03:00
|
|
|
return this },
|
2020-11-15 00:24:22 +03:00
|
|
|
// XXX should this support the 'all' key -- remove all handlers???
|
2020-11-12 17:35:41 +03:00
|
|
|
__event_handler_remove__: function(context, func){
|
|
|
|
|
var handlers =
|
|
|
|
|
(options.handlerLocation == 'hidden' ?
|
|
|
|
|
hidden
|
|
|
|
|
: options.handlerLocation == 'method' ?
|
|
|
|
|
method.__event_handlers__
|
|
|
|
|
: (context.__event_handlers__ || {})[name]) || []
|
2020-11-15 02:50:39 +03:00
|
|
|
handlers.splice(0, handlers.length,
|
|
|
|
|
...handlers.filter(function(h){
|
|
|
|
|
return h !== func
|
|
|
|
|
&& h.__event_original_handler__ !== func }))
|
2020-11-12 17:35:41 +03:00
|
|
|
return this },
|
|
|
|
|
toString: function(){
|
|
|
|
|
return func.toString()
|
|
|
|
|
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1') },
|
|
|
|
|
}) }
|
|
|
|
|
|
|
|
|
|
|
2020-11-15 00:24:22 +03:00
|
|
|
// Extends bareEventMethod(..) adding ability to bind events via the
|
2020-11-15 02:50:39 +03:00
|
|
|
// resulting method directly by passing it a function...
|
2020-11-12 17:35:41 +03:00
|
|
|
//
|
2020-11-15 02:50:39 +03:00
|
|
|
// eventMethod(name[, options])
|
2020-11-12 17:35:41 +03:00
|
|
|
// -> method
|
2020-11-15 02:50:39 +03:00
|
|
|
//
|
|
|
|
|
// eventMethod(name, func[, options])
|
2020-11-12 17:35:41 +03:00
|
|
|
// -> method
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Bind handler...
|
|
|
|
|
// method(handler)
|
|
|
|
|
// -> this
|
|
|
|
|
//
|
|
|
|
|
// Unbind handler...
|
|
|
|
|
// method(handler, false)
|
|
|
|
|
// -> this
|
|
|
|
|
//
|
|
|
|
|
// Trigger handlers...
|
|
|
|
|
// method(...args)
|
|
|
|
|
// -> this
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// func(handle, ...args)
|
|
|
|
|
//
|
|
|
|
|
//
|
2020-11-15 00:24:22 +03:00
|
|
|
var eventMethod =
|
|
|
|
|
module.eventMethod =
|
|
|
|
|
function(name, func, options={}){
|
2020-11-12 17:35:41 +03:00
|
|
|
var method
|
|
|
|
|
options = typeof(func) != 'function' ?
|
|
|
|
|
func
|
|
|
|
|
: options
|
|
|
|
|
|
|
|
|
|
return Object.assign(
|
|
|
|
|
method = bareEventMethod(name,
|
|
|
|
|
function(handle, ...args){
|
|
|
|
|
// add handler...
|
|
|
|
|
// XXX handle handler tags...
|
|
|
|
|
if(typeof(args[0]) == 'function'){
|
|
|
|
|
method.__event_handler_add__(this, args[0])
|
|
|
|
|
|
|
|
|
|
// call the action...
|
|
|
|
|
} else {
|
2020-11-15 00:24:22 +03:00
|
|
|
func
|
|
|
|
|
&& func.call(this, handle, ...args) }
|
2020-11-12 17:35:41 +03:00
|
|
|
|
|
|
|
|
return this },
|
|
|
|
|
options),
|
|
|
|
|
{
|
2020-11-15 00:24:22 +03:00
|
|
|
__event__: 'full',
|
2020-11-12 17:35:41 +03:00
|
|
|
// NOTE: this is a copy of bareEventMethod's .toString() as we
|
|
|
|
|
// still need to base the doc on the user's func...
|
|
|
|
|
toString: function(){
|
|
|
|
|
return func.toString()
|
|
|
|
|
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1') },
|
|
|
|
|
}) }
|
|
|
|
|
|
|
|
|
|
|
2020-11-15 02:50:39 +03:00
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
// Mixins...
|
|
|
|
|
|
2020-11-15 00:24:22 +03:00
|
|
|
// XXX might be nice to add support to pre/post handlers...
|
|
|
|
|
// XXX still not sure about the builtin-local event control flow...
|
2020-11-15 02:53:05 +03:00
|
|
|
// XXX do we need to be able to force global handler???
|
2020-11-15 00:24:22 +03:00
|
|
|
var EventHandlerMixin =
|
|
|
|
|
module.EventHandlerMixin = {
|
2020-11-13 13:37:10 +03:00
|
|
|
__event_handlers__: null,
|
|
|
|
|
|
2020-11-12 17:35:41 +03:00
|
|
|
on: function(evt, func){
|
|
|
|
|
// event...
|
2020-11-13 13:37:10 +03:00
|
|
|
if(evt in this
|
|
|
|
|
&& this[evt].__event_handler_add__){
|
|
|
|
|
this[evt].__event_handler_add__(this, func)
|
2020-11-12 17:35:41 +03:00
|
|
|
// non-event...
|
|
|
|
|
} else {
|
|
|
|
|
;((this.__event_handlers__ = this.__event_handlers__ || {})[evt] =
|
|
|
|
|
this.__event_handlers__[evt] || [])
|
|
|
|
|
.push(func) }
|
|
|
|
|
return this },
|
2020-11-15 02:50:39 +03:00
|
|
|
one: function(evt, func){
|
|
|
|
|
var handler
|
|
|
|
|
this.on(evt,
|
|
|
|
|
handler = Object.assing(
|
|
|
|
|
function(handle, ...args){
|
|
|
|
|
this.off(evt, handler)
|
|
|
|
|
return func.call(this, handle, ...args) },
|
|
|
|
|
{__event_original_handler__: func}))
|
|
|
|
|
return this },
|
2020-11-15 00:24:22 +03:00
|
|
|
// XXX do we need .off(evt, 'all')
|
2020-11-13 13:37:10 +03:00
|
|
|
off: function(evt, func){
|
|
|
|
|
// event...
|
|
|
|
|
if(evt in this
|
2020-11-15 02:50:39 +03:00
|
|
|
&& this[evt].__event_handler_remove__){
|
2020-11-13 13:37:10 +03:00
|
|
|
this[evt].__event_handler_remove__(this, func)
|
|
|
|
|
// non-event...
|
|
|
|
|
} else {
|
2020-11-15 00:24:22 +03:00
|
|
|
var handlers = this.__event_handlers__
|
|
|
|
|
&& (this.__event_handlers__[evt] || [])
|
|
|
|
|
handlers
|
2020-11-15 02:50:39 +03:00
|
|
|
&& handlers.splice(0, handlers.length,
|
|
|
|
|
...handlers.filter(function(h){
|
|
|
|
|
return h !== func
|
|
|
|
|
&& h.__event_original_handler__ !== func })) }
|
2020-11-13 13:37:10 +03:00
|
|
|
return this },
|
|
|
|
|
trigger: function(evt, ...args){
|
2020-11-15 00:24:22 +03:00
|
|
|
// local handler...
|
|
|
|
|
evt in this
|
|
|
|
|
&& this[evt](...args)
|
|
|
|
|
// global events...
|
|
|
|
|
this.__event_handlers__
|
|
|
|
|
&& (this.__event_handlers__[evt] || [])
|
|
|
|
|
.forEach(function(h){ h(evt, ...args) })
|
|
|
|
|
return this },
|
2020-11-12 17:35:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-11-15 00:24:22 +03:00
|
|
|
// NOTE: this can't be added via Object.assign(..), use object.mixinFlat(..)
|
|
|
|
|
// instead...
|
|
|
|
|
var EventDocMixin =
|
|
|
|
|
module.EventDocMixin = {
|
|
|
|
|
get eventfull(){
|
|
|
|
|
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__ == 'bare'}.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)) },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var EventMixin =
|
|
|
|
|
module.EventMixin =
|
2020-11-15 02:53:05 +03:00
|
|
|
object.mixinFlat({},
|
2020-11-15 00:24:22 +03:00
|
|
|
EventHandlerMixin,
|
|
|
|
|
EventDocMixin)
|
|
|
|
|
|
|
|
|
|
|
2020-11-12 17:35:41 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
|
* vim:set ts=4 sw=4 : */ return module })
|