From 5a7841423b17642aad14a6c193b3b0fb81362ea4 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 15 Nov 2020 00:24:22 +0300 Subject: [PATCH] reworking the event.js module, still in progress... Signed-off-by: Alex A. Naanou --- event.js | 98 ++++++++++++++++++++++++++++--------- package-lock.json | 122 +++++++++++++++++++++++----------------------- package.json | 6 +-- runner.js | 10 ++-- test.js | 111 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 254 insertions(+), 93 deletions(-) diff --git a/event.js b/event.js index 782b962..54f3ac3 100644 --- a/event.js +++ b/event.js @@ -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) + + /********************************************************************** diff --git a/package-lock.json b/package-lock.json index 818ef0b..659737f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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 } } diff --git a/package.json b/package.json index cb3fc8e..a7222f0 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/runner.js b/runner.js index b993d51..1f3d56e 100644 --- a/runner.js +++ b/runner.js @@ -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){ diff --git a/test.js b/test.js index dfdfbb1..7a76c21 100755 --- a/test.js +++ b/test.js @@ -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: .on("'+ evt +'", ..)') } + + ;['moo', + ...obj.events, + ...obj.eventfull] + .forEach(bind) + + assert(obj.event(function(evt, ...args){ + called['event-handle-2'] = true + }) === obj, 'bind: .event()') + + + // trigger + var trigger = function(evt, triggerd=true, handled=true){ + var res = assert(obj.trigger(evt), 'trigger: .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: .'+ 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, '.event(..) return value.') + assert(call('bareEvent') == 'bareEvent', '.bareEvent(..) return value.') + + + + // unbind... + + // trigger... + + // re-bind... + + // trigger... + }, +}) + + //--------------------------------------------------------------------- typeof(__filename) != 'undefined'