diff --git a/event.js b/event.js index fcaf790..7c6ef90 100644 --- a/event.js +++ b/event.js @@ -18,6 +18,14 @@ var object = require('ig-object') /*********************************************************************/ // Event method wrappers... +var EventCommand = +module.EventCommand = +object.Constructor('EventCommand', { + name: null, + __init__: function(name, data={}){ + Object.assign(this, data, {name}) }, +}) + // Create an "eventfull" method... // // The resulting method can be either called directly or via .trigger(..). @@ -32,6 +40,7 @@ var object = require('ig-object') // -> method // // +// Trigger the event... // method(...args) // -> .. // @@ -40,6 +49,35 @@ var object = require('ig-object') // -> .. // // +// trigger event handlers... +// handle() +// handle(true) +// -> true +// -> false +// +// prevent event handlers from triggering... +// handle(false) +// -> undefined +// +// +// +// Special case: EventCommand... +// +// EventCommand instance can be passed as the first argument of method, +// in this case the event function will get it but the event handlers +// will not... +// This is done to be able to externally pass commands to event methods +// that get handled in a special way by the function but not passed to +// the event handlers... +// +// method(, ...args) +// -> .. +// +// func(handle, , ...args) +// -> .. +// +// +// // NOTE: calling handle(false) will exiplicitly disable calling the // handlers for that call... var Eventfull = @@ -69,17 +107,18 @@ function(name, func, options={}){ // after the event action is done but before it returns... // 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 + 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, ...args) !== false }, true) } + && handler(name, ...a) !== false }, true) + : undefined } var res = func ? func.call(this, handle, ...args) @@ -142,7 +181,8 @@ function(name, func, options={}){ return method } -module.TRIGGER = {doc: 'force event method to trigger'} + +module.TRIGGER = module.EventCommand('TRIGGER') // Extends Eventfull(..) adding ability to bind events via the // resulting method directly by passing it a function... @@ -179,9 +219,6 @@ module.TRIGGER = {doc: 'force event method to trigger'} // This will pass args to the event action regardless whether the first // arg is a function or not... // -// -// XXX might be a good idea to adde an event that can't be triggered by -// calling... var Event = module.Event = function(name, func, options={}){ @@ -194,18 +231,14 @@ function(name, func, options={}){ return object.mixin( method = Eventfull(name, function(handle, ...args){ - // add handler... - if(typeof(args[0]) == 'function'){ + // 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... - } else { - // special case: force trigger -> remove TRIGGER from args... - args[0] === module.TRIGGER - && args.shift() - func - && func.call(this, handle, ...args) } - + // call the action... + : (func + && func.call(this, handle, ...args)) return this }, options), { @@ -220,6 +253,16 @@ function(name, func, options={}){ }) } +// Like Event(..) but produces an event that can only be triggered via +// .trigger(name, ...), calling this is a no-op... +var PureEvent = +module.PureEvent = +function(name, options={}){ + return Event(name, function(handle, trigger){ + trigger === module.TRIGGER + || handle(false) }, options)} + + //--------------------------------------------------------------------- // Mixins... diff --git a/package.json b/package.json index 74687e8..1b47cec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ig-types", - "version": "5.0.9", + "version": "5.0.10", "description": "Generic JavaScript types and type extensions...", "main": "main.js", "scripts": { diff --git a/runner.js b/runner.js index 1a43daf..62a121b 100644 --- a/runner.js +++ b/runner.js @@ -337,6 +337,38 @@ object.Mixin('TaskMixin', 'soft', { }) +// Make the ticket more usable... +var TaskTicket = +module.TaskTicket = +object.Constructor('TaskTicket', { + __data: null, + + title: null, + + resolve: function(...args){ + this.__data.resolve(...args) + return this }, + reject: function(...args){ + this.__data.reject(...args) + return this }, + onmessage: function(msg, func){ + this.__data.onmessage( + typeof(msg) == 'function' ? + msg + : function(m, ...args){ + m == msg + && func(...args) }) + return this }, + + __init__: function(title, resolve, reject, onmessage){ + this.title = title + Object.defineProperty(this, '__data', { + __data: {resolve, reject, onmessage}, + enumerable: false, + }) }, +}) + + // XXX we should keep the API here similar to Queue... // ...but this is no a queue in principle (internal vs. external // management) so we'll also need to keep them different enough to @@ -359,6 +391,8 @@ object.Constructor('TaskManager', Array, events.EventMixin('flat', { .filter(function(task){ return titles.has(task.title) }) }, + // actions... + // send: function(title, ...args){ if(title == 'all' || title == '*'){ this.forEach(function(task){ @@ -369,7 +403,6 @@ object.Constructor('TaskManager', Array, events.EventMixin('flat', { title : [title]) .send('all', ...args) }, - // XXX each task should also trigger this when stopping and this // should not result in this and tasks infinitely playing // ping-pong... @@ -380,8 +413,11 @@ object.Constructor('TaskManager', Array, events.EventMixin('flat', { function(handlers, title='all'){ this.send(title, 'stop') }), - done: events.Event('done'), - error: events.Event('error'), + // events... + // + done: events.PureEvent('done'), + tasksDone: events.PureEvent('tasksDone'), + error: events.PureEvent('error'), // @@ -416,10 +452,6 @@ object.Constructor('TaskManager', Array, events.EventMixin('flat', { // // NOTE: the args are passed only if // NOTE: the task is started as soon as it is accepted. - // - // XXX seems that we have a dangling promise someplace... - // ...if a task rejects node/chrome report an unhandled promise - // reject... Task: function(title, task, ...args){ var that = this @@ -461,12 +493,9 @@ object.Constructor('TaskManager', Array, events.EventMixin('flat', { // the context is ready... run = function(){ var res = - task({ - title, - resolve, - reject, - onmessage, - }, ...args) + task( + TaskTicket(title, resolve, reject, onmessage), + ...args) // NOTE: if the client calls resolve(..) this // second resolve(..) call has no effect, // and the same is true with reject...