reworking the event.js module, still in progress...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-11-15 00:24:22 +03:00
parent 871e4a6c0b
commit 5a7841423b
5 changed files with 254 additions and 93 deletions

View File

@ -2,7 +2,9 @@
*
*
*
* XXX is types/events the right place for this???
* XXX should we have .pre/.post events???
* XXX should we propogate event handling to parent/overloaded events???
*
**********************************************/ /* c8 ignore next 2 */
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
@ -15,32 +17,35 @@ var object = require('ig-object')
/*********************************************************************/
var bareEventMethod = function(name, func, options={}){
var bareEventMethod =
module.bareEventMethod =
function(name, func, options={}){
var hidden
var method
options = typeof(func) != 'function' ?
options = func && typeof(func) != 'function' ?
func
: options
return object.mixinFlat(
method = function(func, mode){
method = function(...args){
var handlers =
// hidden...
options.handlerLocation == 'hidden' ?
(hidden = hidden || [])
(hidden || [])
// function...
: options.handlerLocation == 'method' ?
(method.__event_handlers__ = method.__event_handlers__ || [])
(method.__event_handlers__ || [])
// context (default)...
: ((this.__event_handlers__ = this.__event_handlers__ || {})[name] =
this.__event_handlers__[name] || [])
: ((this.__event_handlers__ || {})[name] || [])
var args = [...arguments]
// NOTE: this will stop event handling if one of the handlers
// explicitly returns false...
var handle = function(){
handlers
.forEach(function(handler){
handler(...args) }) }
return handlers
.reduce(function(res, handler){
return res === true
&& handler(...args) !== false }, true) }
var res
func ?
(res = func.call(this, handle, ...args))
@ -48,12 +53,11 @@ var bareEventMethod = function(name, func, options={}){
return res },
{
__event__: true,
__event__: 'bare',
get __event_handler_location__(){
return ['hidden', 'method'].includes(options.handlerLocation) ?
options.handlerLocation
: 'context' },
// XXX revise nameing...
__event_handler_add__: function(context, func){
var handlers =
// hidden...
@ -63,11 +67,12 @@ var bareEventMethod = function(name, func, options={}){
: options.handlerLocation == 'method' ?
(method.__event_handlers__ = method.__event_handlers__ || [])
// context (default)...
: ((this.__event_handlers__ = this.__event_handlers__ || {})[name] =
this.__event_handlers__[name] || [])
: ((context.__event_handlers__ = context.__event_handlers__ || {})[name] =
context.__event_handlers__[name] || [])
// add handler...
handlers.push(args[1])
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' ?
@ -83,6 +88,8 @@ var bareEventMethod = function(name, func, options={}){
}) }
// Extends bareEventMethod(..) adding ability to bind events via the
// resulting method directly...
//
// eventMethod(name, func)
// -> method
@ -106,7 +113,9 @@ var bareEventMethod = function(name, func, options={}){
// func(handle, ...args)
//
//
var eventMethod = function(name, func, options={}){
var eventMethod =
module.eventMethod =
function(name, func, options={}){
var method
options = typeof(func) != 'function' ?
func
@ -122,11 +131,13 @@ var eventMethod = function(name, func, options={}){
// call the action...
} else {
func.call(handle, ...args) }
func
&& func.call(this, handle, ...args) }
return this },
options),
{
__event__: 'full',
// NOTE: this is a copy of bareEventMethod's .toString() as we
// still need to base the doc on the user's func...
toString: function(){
@ -135,9 +146,13 @@ var eventMethod = function(name, func, options={}){
}) }
var EventHandlerMixin = {
// XXX might be nice to add support to pre/post handlers...
// XXX still not sure about the builtin-local event control flow...
var EventHandlerMixin =
module.EventHandlerMixin = {
__event_handlers__: null,
// XXX do we need to be able to force global handler???
on: function(evt, func){
// event...
if(evt in this
@ -149,6 +164,7 @@ var EventHandlerMixin = {
this.__event_handlers__[evt] || [])
.push(func) }
return this },
// XXX do we need .off(evt, 'all')
off: function(evt, func){
// event...
if(evt in this
@ -156,16 +172,50 @@ var EventHandlerMixin = {
this[evt].__event_handler_remove__(this, func)
// non-event...
} else {
// XXX
}
var handlers = this.__event_handlers__
&& (this.__event_handlers__[evt] || [])
handlers
&& handlers.splice(handlers.indexOf(func), 1) }
return this },
// XXX add support for stopping handler execution...
trigger: function(evt, ...args){
// XXX trigger both the context and the method event handlers...
// XXX
},
// 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 },
}
// 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 =
object.mixinFlat(
EventHandlerMixin,
EventDocMixin)
/**********************************************************************

122
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "ig-types",
"version": "3.0.3",
"version": "3.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -41,23 +41,6 @@
"dev": true,
"requires": {
"color-convert": "^2.0.1"
},
"dependencies": {
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
}
}
},
"balanced-match": {
@ -77,9 +60,9 @@
}
},
"c8": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/c8/-/c8-7.3.1.tgz",
"integrity": "sha512-GXddMEWjNkw6OHQx0bRc+4Sk65beqQq9gBHLJxEYlmu3q+yIIBQpOjBApPyRcFjLTJHsWdWtrOMjLJkX7q7irQ==",
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/c8/-/c8-7.3.5.tgz",
"integrity": "sha512-VNiZoxnInBJLW8uUuyLkiqMKWh1OAsYS+DjWsMhvcrfGPrVx3vwqD9627/7ZhFSF86MCBINDi+PD6Midw0KHRg==",
"dev": true,
"requires": {
"@bcoe/v8-coverage": "^0.2.3",
@ -92,15 +75,15 @@
"istanbul-reports": "^3.0.2",
"rimraf": "^3.0.0",
"test-exclude": "^6.0.0",
"v8-to-istanbul": "^5.0.0",
"v8-to-istanbul": "^7.0.0",
"yargs": "^16.0.0",
"yargs-parser": "^20.0.0"
}
},
"cliui": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.1.tgz",
"integrity": "sha512-rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ==",
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"requires": {
"string-width": "^4.2.0",
@ -109,34 +92,51 @@
}
},
"color": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
"integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
"integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
"dev": true,
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
"color-string": "^1.5.4"
},
"dependencies": {
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
}
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "1.1.3"
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz",
"integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==",
"dev": true,
"requires": {
"color-name": "^1.0.0",
@ -182,9 +182,9 @@
"dev": true
},
"escalade": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
"integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true
},
"find-up": {
@ -265,9 +265,9 @@
}
},
"ig-object": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.2.8.tgz",
"integrity": "sha512-EzT4CP6d6lI8bnknNgT3W8mUQhSVXflO0yPbKD4dKsFcINiC6npjoEBz+8m3VQmWJhc+36pXD4JLwNxUEgzi+Q=="
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.3.0.tgz",
"integrity": "sha512-8I3w0Gqv96cx1Eirqrvtuh4Pq8X4U3tAexj4nMwceNu4jzZF1wg9FP3Im5ZZ99kQSk7CWl4tjSGe/aS5IXEoMw=="
},
"ig-test": {
"version": "1.4.6",
@ -535,9 +535,9 @@
}
},
"v8-to-istanbul": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz",
"integrity": "sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz",
"integrity": "sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.1",
@ -572,30 +572,30 @@
"dev": true
},
"y18n": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.2.tgz",
"integrity": "sha512-CkwaeZw6dQgqgPGeTWKMXCRmMcBgETFlTml1+ZOO+q7kGst8NREJ+eWwFNPVUQ4QGdAaklbqCZHH6Zuep1RjiA==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
"integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==",
"dev": true
},
"yargs": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.0.3.tgz",
"integrity": "sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA==",
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.0.tgz",
"integrity": "sha512-upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g==",
"dev": true,
"requires": {
"cliui": "^7.0.0",
"escalade": "^3.0.2",
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.1",
"yargs-parser": "^20.0.0"
"y18n": "^5.0.2",
"yargs-parser": "^20.2.2"
}
},
"yargs-parser": {
"version": "20.2.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.1.tgz",
"integrity": "sha512-yYsjuSkjbLMBp16eaOt7/siKTjNVjMm3SoJnIg3sEh/JsvqVVDyjRKmaJV4cl+lNIgq6QEco2i3gDebJl7/vLA==",
"version": "20.2.4",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
"integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
"dev": true
}
}

View File

@ -23,12 +23,12 @@
},
"homepage": "https://github.com/flynx/types.js#readme",
"dependencies": {
"ig-object": "^5.2.8",
"ig-object": "^5.3.0",
"object-run": "^1.0.1"
},
"devDependencies": {
"c8": "*",
"color": "*",
"c8": "^7.3.5",
"color": "^3.1.3",
"ig-test": "^1.4.6"
}
}

View File

@ -20,6 +20,8 @@
var object = require('ig-object')
var events = require('./event')
/*********************************************************************/
@ -42,7 +44,7 @@ module.Queue = object.Constructor('Queue', Array, {
run: function(...tasks){
return this({ state: 'running' }, ...tasks) },
},{
}, {
// config...
//
pool_size: 8,
@ -89,9 +91,9 @@ module.Queue = object.Constructor('Queue', Array, {
// run the handlers...
} else {
;(this['__'+evt] || [])
.forEach(function(handler){
handler.call(that, evt, ...args) }) }
;(this['__'+evt] || [])
.forEach(function(handler){
handler.call(that, evt, ...args) }) }
return this },
on: function(evt, handler){
if(!handler){

111
test.js
View File

@ -13,9 +13,12 @@ var test = require('ig-test')
var object = require('ig-object')
var types = require('./main')
var containers = require('./containers')
var promise = require('./Promise')
var containers = require('./containers')
var generator = require('./generator')
var events = require('./event')
//---------------------------------------------------------------------
@ -357,6 +360,112 @@ UniqueKeyMap.tests({
//---------------------------------------------------------------------
// events...
var Events = test.TestSet()
test.Case('Events', Events)
Events.cases({
base: function(assert){
var called = {}
// object with events...
var ObjWithEvents =
assert(
object.mixinFlat({
bareEventBlank: assert(
events.bareEventMethod('bareEventBlank'),
'.bareEventMethod(..): blank'),
eventBlank: assert(
events.eventMethod('eventBlank'),
'.eventMethod(..): blank'),
// XXX test aborting handlers...
bareEvent: assert(events.bareEventMethod('bareEvent',
function(handle, ...args){
called['bareEvent-call'] = true
assert(handle(...args), '.bareEventMethod(..) -> handle(..)')
return 'bareEvent'
}), '.bareEventMethod(..)'),
event: assert(events.eventMethod('event',
function(handle, ...args){
called['event-call'] = true
assert(handle(...args), '.eventMethod(..) -> handle(..)')
}), '.eventMethod(..)'),
}, events.EventMixin),
'object with event mixin created.')
var obj = Object.create(ObjWithEvents)
// test event list...
assert.array(obj.events, ['event', 'eventBlank'], '.events')
assert.array(obj.eventfull, ['bareEvent', 'bareEventBlank'], '.eventfull')
// bind...
var bind = function(evt){
assert(obj.on(evt, function(evt, ...args){
called[evt +'-handle'] = true
}) === obj, 'bind: <obj-w-events>.on("'+ evt +'", ..)') }
;['moo',
...obj.events,
...obj.eventfull]
.forEach(bind)
assert(obj.event(function(evt, ...args){
called['event-handle-2'] = true
}) === obj, 'bind: <obj-w-events>.event(<func>)')
// trigger
var trigger = function(evt, triggerd=true, handled=true){
var res = assert(obj.trigger(evt), 'trigger: <obj-w-events>.trigger("'+ evt +'")')
triggerd
&& !evt.endsWith('Blank')
&& assert(called[evt +'-call'], 'trigger: "'+ evt +'" event triggered')
handled
&& assert(called[evt +'-handle'], 'trigger: "'+ evt +'" event handled')
delete called[evt +'-call']
delete called[evt +'-handle']
return res }
var call = function(evt, triggered=true, handled=true){
var res = assert(obj[evt](), 'trigger: <obj-w-events>.'+ evt +'(..)')
triggered
&& !evt.endsWith('Blank')
&& assert(called[evt +'-call'], 'trigger: "'+ evt +'" event triggered')
handled
&& assert(called[evt +'-handle'], 'trigger: "'+ evt +'" event handled')
delete called[evt +'-call']
delete called[evt +'-handle']
return res }
trigger('foo', false, false)
trigger('moo', false)
obj.events
.forEach(function(e){
trigger(e)
call(e) })
assert(called['event-handle-2'], 'trigger: "event" event handled')
delete called['event-handle-2']
assert(call('event') === obj, '<obj-w-events>.event(..) return value.')
assert(call('bareEvent') == 'bareEvent', '<obj-w-events>.bareEvent(..) return value.')
// unbind...
// trigger...
// re-bind...
// trigger...
},
})
//---------------------------------------------------------------------
typeof(__filename) != 'undefined'