diff --git a/actions.js b/actions.js index 6032e25..d95022e 100755 --- a/actions.js +++ b/actions.js @@ -11,334 +11,10 @@ var object = require('ig-object') -/*********************************************************************/ - -// Return value wrapper... -// -// Wrapping a value in this and returning it from an action will force -// the action to return the value as-is... -// This is mainly usefull for specially handled values. -var ASIS = -module.ASIS = -object.Constructor('ASIS', { - __init__: function(obj){ this.value = obj } }) - -// undefined wrapper... -var UNDEFINED = -module.UNDEFINED = ASIS(undefined) - - - -/*********************************************************************/ -// XXX should we maintain two sets of docs, here and in README.md??? -// -// Actions -// -// Actions are an extension to the JavaScript object model tailored for -// a set of specific tasks. -// -// Goals: -// - provide a unified mechanism to define and manage user API's for -// use in UI-hooks, keyboard mappings, scripting, ... etc. -// - a means to generate configuration UI's -// - a means to generate documentation -// -// -// The main entities: -// -// Action set -// - an object containing a number of actions, -// - optionally, directly or indirectly inherited from MetaActions -// and/or other action sets, -// - the action handlers are bound relative to it (._action_handlers) -// -// Action -// -// + pre + pre + + post + post + -// Action event handler: o-------x o-------x -// v ^ -// Actions o-------x o-------x -// v ^ -// Root Action o---|---x -// -// - a method, created by Action(..), -// - calls all the shadowed/overloaded actions in the inheritance -// chain in sequence implicitly, -// NOTE: there is no way to prevent an action in the chain from -// running, this is by design, i.e. no way to fully shadow. -// - actions that do not shadow anything are called root actions. -// - returns the action set by default (for call chaining), -// - the base/root action can return any value. -// NOTE: if undefined is returned, it will be replaced by the -// action context/action set. -// NOTE: there is no distinction between root and other actions -// other than that root action's return values are not -// ignored. -// - if the root action returns a Promise, the post phase is run -// when that promise is resolved or rejected. -// This can be disabled by setting the 'await' action attribute -// to false (default: true). -// - can consist of two parts: the first is called before the -// shadowed action (pre-callback) and the second after (post-callback). -// - post-callback has access to the return value and can modify it -// but not replace it. -// - can be bound to, a-la an event, calling the handlers when it is -// called, -// -// Action (event) handler -// - a function, -// - can be bound to run before and/or after the action itself, -// - is local to an action set it was bound via, -// - when an action is triggered from an action set, all the pre -// handlers in its inheritance chain will be called before the -// respective actions they are bound to and all the post handlers -// are called directly after. -// - pre handlers are passed the same arguments the original actions -// got when it was called. -// - post action handlers will get the root action result as first -// argument succeeded by the action arguments. -// -// Alias -// - an action created by Alias(..), -// - identical to an action with one key difference: instead of a -// function Alias(..) expects a string/code, -// - code syntax is configurable, defaulting to the defined by -// parseActionCall(..) -// - aliases are designed to be defined and managed in runtime while -// actions are mainly load-time entities. -// -// -// The action system main protocols: -// -// 1) Documentation generation and introspection (MetaActions) -// -// .toString() -// -> code of original action function -// -// .getDoc() -// .getDoc([, ..]) -// -> dict of action-name, doc -// -// .a.getHandlerDocStr() -// -> formated string of action handlers -// -// .actions -// -> list of action names -// -// .length -// -> number of actions -// -// -// 2) Event-like callbacks for actions (MetaActions, Action) -// -// .on('action', function(){ ... }) -// .on('action.post', function(){ ... }) -// -// .on('action.pre', function(){ ... }) -// -// -// 3) A mechanism to define and extend already defined actions -// This replaces / complements the standard JavaScript overloading -// mechanisms (Action, Actions) -// -// // Actions... -// var X = Actions({ -// m: [function(){ console.log('m') }] -// }) -// var O = Actions(X, { -// m: [function(){ -// console.log('pre') -// return function(res){ -// console.log('post') -// } -// }] -// }) -// -// NOTE: what is done here is similar to calling O.__proto__.m.call(..) -// but is implicit, and not dependant on the original containing -// object name/reference ('O'), thus enabling an action to be -// referenced and called from any object and still chain correctly. -// NOTE: if a normal method is encountered in the inheritance chain, it -// will shadow all the actions beyond it. -// -// -// -// Secondary action protocols: -// -// 1) A mechanism to manually call the pre/post stages of an action -// -// Pre phase... -// .pre() -// .pre(, [, ..]) -// -> -// -// Post phase... -// .post(, ) -// -> -// -// This is internally used to implement the action call as well as the -// chaining callbacks (see below). -// -// All action protocol details apply. -// -// NOTE: there is not reliable way to call the post phase without first -// calling the pre phase due to how the pre phase is defined (i.e. -// pre phase functions can return post phase functions). -// -// -// 2) A mechanism to chain/wrap actions or an action and a function. -// This enables us to call a callback or another action (inner) between -// the root action's (outer) pre and post stages. -// -// Outer action o-------x o-------x -// v ^ -// Inner action/callback o---|---x -// -// A trivial example: -// -// actionSet.someAction.chainApply(actionsSet, -// function(){ -// // this gets run between someAction's pre and post -// // stages... -// }, -// args) -// -// This is intended to implement protocols where a single action is -// intended to act as a hook point (outer) and multiple different -// implementations (inner) within a single action set can be used as -// entry points. -// -// // Protocol root action (outer) definition... -// protocolAction: [function(){}], -// -// // Implementation actions (inner)... -// implementationAction1: [function(){ -// return this.protocolAction.chainApply(this, function(){ -// // ... -// }, ..) -// }] -// -// implementationAction2: [function(){ -// return this.protocolAction.chainApply(this, function(){ -// // ... -// }, ..) -// }] -// -// Now calling any of the 'implementation' actions will execute code -// in the following order: -// 1) pre phase of protocol action (outer) -// 2) implementation action (inner) -// 3) post phase of protocol action (outer) -// -// Differences form the base action protocol: -// - returning undefined from inner will keep the outer return value. -// ...this is different from the base action protocol where returning -// undefined will get replaces by the context (this), here to -// guarantee returning the context, it should be returned explicitly, -// otherwise the responsibility is shifted to the outer action. -// - returning anything else will override the outer return -// -// NOTE: this will not affect to protocol/signature of the outer action -// in any way other than the ability to override the return value. -// NOTE: both the inner and outer actions will get passed the same -// arguments. -// NOTE: another use-case is testing/debugging actions. -// NOTE: this is effectively the inside-out of normal action overloading. -// NOTE: there is intentionally no shorthand for this feature, to avoid -// confusion and to discourage the use of this feature unless -// really necessary. -// -// -// 3) .__actioncall__ action / handler -// This action if defined is called for every action called. It behaves -// like any other action but with a fixed signature, it always receives -// the action name as first argument and a list of action arguments as -// the second arguments, and as normal a result on the post phase. -// -// NOTE: it is not necessary to define the actual action, binding to a -// handler will also work. -// NOTE: one should not call actions directly from within a __actioncall__ -// handler as that will result in infinite recursion. -// XXX need a way to prevent this... -// NOTE: one should use this with extreme care as this will introduce -// an overhead on all the actions if not done carefully. -// -// -// 4) Action attributes -// XXX -// -// -// 5) After action calls... -// This enables the action code to schedule a call after the current -// action level or the root action is done. -// -// .afterAction(func) -// .afterAction('top', func) -// -> this -// -// .afterAction('local', func) -// -> this -// -// Example: -// someAction: [ -// function(){ -// ... -// -// // the function passed will get called after the root action -// // and all it's handlers are done. -// this.afterAction(function(){ ... }) -// -// ... -// }], -// -// NOTE: the functions are executed in order of registration. -// XXX EXPERIMENTAL (handler cache)... -// -// -// -// Alias protocols: -// -// 1) Defining aliases in runtime (MetaActions) -// -// .alias('alias', 'action: args') -// .alias('alias', .., 'action: args') -// -> -// -// To enable extending in runtime .alias(..) itself is implemented as -// an action, thus all action protocols also apply. -// -// NOTE: .alias(..) is signature compatible to Action(..) / Alias(..), -// supporting all the documentation and attribute definition. -// -// -// 2) Deleting aliases in runtime (MetaActions) -// -// .alias('alias', null) -// .alias('alias', false) -// -> -// -// NOTE: only own aliases can be deleted via .alias(.., null|false) -// -// -// 3) Documentation generation and introspection (MetaActions, Alias) -// -// Alias code... -// .alias -// .toString() -// -> -// -// List own aliases... -// .aliases -// -> -// -// -// /*********************************************************************/ // helpers... -var normalizeIndent = object.normalizeIndent - - +// XXX doc... var doWithRootAction = module.doWithRootAction = function(func){ @@ -347,12 +23,19 @@ function(func){ var handlers = (this.getHandlerList || MetaActions.getHandlerList) .apply(this, args) - return func.apply(this, [handlers.pop()].concat(args)) } } + +//--------------------------------------------------------------------- // String action parser/runner... +// +// Examples: +// 'actionName' +// 'actionName: attr 123 "string" -- comment...' +// 'actionName: ...' // +// // Syntax: // ALIAS ::= // @@ -387,6 +70,7 @@ function(func){ // code: txt, // } // +// // NOTE: identifiers are resolved as attributes of the context... // NOTE: this is a stateless object... // XXX this is the same as ImageGrid's keyboard.parseActionCall(..), reuse @@ -533,6 +217,22 @@ module.isStringAction = /*********************************************************************/ +// Action... + +// Return value wrapper... +// +// Wrapping a value in this and returning it from an action will force +// the action to return the value as-is... +// This is mainly usefull for specially handled values. +var ASIS = +module.ASIS = +object.Constructor('ASIS', { + __init__: function(obj){ this.value = obj } }) + +// undefined wrapper... +var UNDEFINED = +module.UNDEFINED = ASIS(undefined) + // Construct an action object... // @@ -997,7 +697,7 @@ object.Constructor('Action', { // make introspection be a bit better... meth.toString = function(){ - return normalizeIndent(func.toString()) } + return object.normalizeIndent(func.toString()) } // setup attrs... Object.assign(meth, attrs) @@ -1561,6 +1261,7 @@ module.MetaActions = { // NOTE: the object result must be compatible with Action.pre(..) // return value... // NOTE: this is mostly a stub, here for documentation reasons... + // XXX doc / revise... //preActionHandler: doWithRootAction( // function(action, name, handlers, args){ return null }), @@ -2183,19 +1884,19 @@ module.MetaActions = { var getTags = function(handler, p){ return (handler.event_tag ? - normalizeIndent('// Event tag: ' + handler.event_tag) + p + object.normalizeIndent('// Event tag: ' + handler.event_tag) + p : '') + (handler.source_tag ? - normalizeIndent('// Source tag: ' + handler.source_tag) + p + object.normalizeIndent('// Source tag: ' + handler.source_tag) + p : '') } var getDoc = function(cur, p){ return (cur.doc ? '// --- .doc ---'+p - +'// '+ normalizeIndent(cur.doc).replace(/\n/g, p+'// ') +p + +'// '+ object.normalizeIndent(cur.doc).replace(/\n/g, p+'// ') +p : '') + (cur.long_doc ? '// --- .long_doc ---'+p - +'// '+ normalizeIndent(cur.long_doc).replace(/\n/g, p+'// ') + p + +'// '+ object.normalizeIndent(cur.long_doc).replace(/\n/g, p+'// ') + p : '') } var handler = function(p){ @@ -2214,7 +1915,7 @@ module.MetaActions = { + getTags(cur.pre, p) + getDoc(cur, p) // code... - + normalizeIndent(cur.pre.toString()).replace(/\n/g, p) + + object.normalizeIndent(cur.pre.toString()).replace(/\n/g, p) + p } @@ -2227,7 +1928,7 @@ module.MetaActions = { + getTags(cur.post, p) + getDoc(cur, p) // code... - + normalizeIndent(cur.post.toString()).replace(/\n/g, p) + + object.normalizeIndent(cur.post.toString()).replace(/\n/g, p) } } @@ -2252,11 +1953,11 @@ module.MetaActions = { p.append($('
').html(
 					// meta...
 					(cur.pre.event_tag ? 
-						normalizeIndent('// Event tag: ' + cur.pre.event_tag) + p : '')
+						object.normalizeIndent('// Event tag: ' + cur.pre.event_tag) + p : '')
 					+ (cur.pre.source_tag ? 
-						normalizeIndent('// Source tag: ' + cur.pre.source_tag) + p : '')
+						object.normalizeIndent('// Source tag: ' + cur.pre.source_tag) + p : '')
 					// code...
-					+ normalizeIndent(cur.pre.toString())
+					+ object.normalizeIndent(cur.pre.toString())
 						.replace(/return/g, 'return')))
 			}
 
@@ -2266,11 +1967,11 @@ module.MetaActions = {
 				p.append($('
').html(
 					// meta...
 					(cur.post.event_tag ? 
-						normalizeIndent('// Event source tag: ' + cur.post.event_tag) + p : '')
+						object.normalizeIndent('// Event source tag: ' + cur.post.event_tag) + p : '')
 					+ (cur.post.source_tag ? 
-						normalizeIndent('// Source tag: ' + cur.post.source_tag) + p : '')
+						object.normalizeIndent('// Source tag: ' + cur.post.source_tag) + p : '')
 					// code...
-					+ normalizeIndent(cur.post.toString())))
+					+ object.normalizeIndent(cur.post.toString())))
 			}
 		}
 
diff --git a/package.json b/package.json
index 6f2a9ae..d35b239 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "ig-actions",
-  "version": "3.24.7",
+  "version": "3.24.8",
   "description": "",
   "main": "actions.js",
   "scripts": {