reworked the action framework...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2014-10-06 02:25:57 +04:00
parent f4abf42763
commit dc53204eef
2 changed files with 194 additions and 49 deletions

View File

@ -42,14 +42,111 @@ var SCREEN_IMAGES = null
/*********************************************************************/
//
// Contexts:
// Browser node node-webkit PhoneGap
// UI o x o o
// navigation o o o o
// edit o, x o o, x o, x
//
//
// The basic inheritance tree should be something like this:
//
// .
// Data . UI
// .
// MetaActions .
// ^ .
// | .
// | .
// BaseActions .
// ^ ^ .
// | +------------------ UIActions
// | . ^
// | . | -+
// BaseMarks . | |
// ^ . | | Plugin
// + - - - - -???- - - UIMarks |
// . -+
// .
//
//
// XXX Need a way to combine a set of features into a view, preferably
// in runtime...
// - turn feature on/off at load
// - turn feature on/off in runtime
//
// XXX without multiple inheritance we'll need to either:
//
// - build inheritance chains per task -- i.e. connect blocks in
// a custom way depending on use
// ...this makes it really hard to have two systems configured
// differently in the same runtime...
//
// - build a multiple inheritance system
// ...without the ability to hook into attribute access this is
// not trivial... (feasibility unknown)
//
// - make the UI versions by copying methods from the base and UI
// into a single object, effectively creating two separate chains
// ...auto-creating proxy methods is another way to implement this
// + solves the problem
// - not dynamic -- changes have to be applied to both chains
// rather than to a single relevant object.
//
// - encapsulate and proxy?
//
// - static mixin...
//
//
// XXX actions should be split by feature
// - basic navigation
// - basic editing
// - cropping
// - marking
// - bookmarking
// - tagging
// - image editing
// - url loading
// - url saving
// - fs loading
// - fs saving
// Features can be organized into contexts:
// - browser viewer
// - browser editor
// - app viewer
// - app editor
//
// XXX each plugin must be split into:
// - UI view -- display only
// - UI controls -- edit
// - base actions -- usable without UI
//
// XXX think about life-cycle...
//
//
/*********************************************************************/
// XXX need a way to define compound actions...
// - compound action is like a normal action with a set of other
// actions chanined to it's main event.
// - actions should accept arguments, both optional and required
var BASE_ACTIONS =
module.BASE_ACTIONS = {
var BaseActions =
module.BaseActions = actions.Actions({
// state props...
get current(){
// XXX should this return a gid or a jQuery-like object for
// image-oriented operations???
return this.data.current
},
set current(val){
return this.focusImage(val)
},
// life-cycle / state...
// XXX
// actions...
focusImage: '',
focusRibbon: '',
// basic editing...
shiftImageUp:
'Shift image to the ribbon above current, creating one if '
@ -100,16 +197,14 @@ module.BASE_ACTIONS = {
exportImages: '',
exit: '',
}
})
/*********************************************************************/
var UI_ACTIONS =
module.UI_ACTIONS = {
focusImage: '',
focusRibbon: '',
var UIActions =
module.UIActions = {
// basic navigation...
nextImage: ['Focus next image in current ribbon', { focusImage: 'next' }],

View File

@ -59,6 +59,7 @@ define(function(require){ var module = {}
//
// // event-like callbacks for actions...
// O.on('m', function(){...})
// O.on('m.pre', function(){...})
//
//
// Comparing to the native system:
@ -88,12 +89,21 @@ function _collect_handlers(obj, name){
var handlers = []
var cur = obj
while(cur.__proto__ != null){
if(obj._action_handlers == null){
break
// get action "event" handlers...
if(cur.hasOwnProperty('_action_handlers')
&& name in cur._action_handlers){
handlers.splice.apply(handlers,
[handlers.length, 0].concat(cur._action_handlers[name]))
}
if(cur.hasOwnProperty('_action_handlers') && name in cur._action_handlers){
handlers.splice.apply(handlers, [0, 0].concat(cur._action_handlers[name]))
// get the overloading action...
// NOTE: this will get all the handlers including the root
// and the current handlers...
// NOTE: this will ignore "shadows" that are not actions...
if(cur.hasOwnProperty(name) && cur[name] instanceof Action){
handlers.push(cur[name].func)
}
cur = cur.__proto__
}
return handlers
@ -105,6 +115,30 @@ function _collect_handlers(obj, name){
// Construct an action object...
//
// Action format:
// function(){
// ... // pre code
// }
//
// function(){
// ... // pre code
// return function(){
// ... // post code
// }
// }
//
// function(){
// ... // pre code
// return $.Deferred()
// .done(function(){
// ... // post code
// })
// }
//
//
// NOTE: it is not possible to auto-generate Class.__proto__.meth(..) calls
// without explicitly knowing the Class, thus using the overloading
// mechanism is not feasible until this is solved...
var Action =
module.Action =
function Action(name, doc, ldoc, func){
@ -126,30 +160,30 @@ function Action(name, doc, ldoc, func){
var that = this
var args = args2array(arguments)
// normal handlers -- pre phase...
// get and call handlers -- pre phase...
var handlers = _collect_handlers(this, name)
.map(function(h){ return h.apply(that, args) })
// call the action...
var res = func.apply(this, args)
// NOTE: this action will get included and called by the code
// above and below...
// simple post callback...
//_collect_handlers(this, name)
// .forEach(function(h){ return h.apply(that, args) })
// normal handlers -- post phase...
handlers.forEach(function(h){
// pre-callback returned a function...
// call handlers -- post phase...
// NOTE: post handlers need to get called last run pre first run post...
handlers.reverse().forEach(function(h){
// function...
if(h instanceof Function){
h.call(that, res)
//h.call(that, res)
h.call(that)
// pre-callback returned an object with a .resolve(..) method...
// deferred...
} else if(h != null && h.resolve instanceof Function){
h.resolve(res)
//h.resolve(res)
h.resolve()
}
})
return res
//return res
return this
}
meth.__proto__ = this.__proto__
@ -158,6 +192,8 @@ function Action(name, doc, ldoc, func){
meth.doc = doc
meth.long_doc = ldoc
meth.func = func
return meth
}
@ -235,7 +271,8 @@ module.MetaActions = {
}
// register a handler only once...
if(this._action_handlers[action].indexOf(handler) < 0){
this._action_handlers[action].push(handler)
// NOTE: last registered is first...
this._action_handlers[action].splice(0, 0, handler)
}
return this
},
@ -276,6 +313,7 @@ module.MetaActions = {
}
// Define an action set...
//
// Actions(<object>)
@ -331,8 +369,8 @@ module.MetaActions = {
// when the action is done.
//
// NOTE: the action, action pre-callback and post-callbacks will be
// called with the same context as the original callback and the
// action, i.e. the action set.
// called with the same context (this) as the original callback
// and the action, i.e. the action set.
//
//
var Actions =
@ -354,18 +392,8 @@ function Actions(a, b){
var func = args.pop()
// if an action already exists then register the function as its
// callback...
if(proto != null
&& k in proto
&& proto[k] instanceof Action){
delete obj[k]
proto.on.call(obj, k+'.pre', func)
// create a new action...
} else {
obj[k] = new Action(k, args[0], args[0], func)
}
obj[k] = new Action(k, args[0], args[1], func)
})
if(proto != null){
@ -377,7 +405,6 @@ function Actions(a, b){
/*********************************************************************/
var test =
@ -386,15 +413,17 @@ function test(){
var TestActions =
module.TestActions =
Actions({
testActionGen1: ['baisc test action...',
'some extra info',
function(){
console.log(' test!')
console.log(' test 1!')
return function(){
console.log(' test 2!')
}
}],
testActionGen2: ['baisc 2\'nd gen test action...',
'some extra info',
// no extra info...
function(){
console.log(' test gen 2!')
this.testActionGen1()
@ -404,7 +433,6 @@ function test(){
var TestActions2 =
module.TestActions2 =
Actions(TestActions, {
// NOTE: this looks like an action and feels like an action but
// actually this is a callback as an action with this name
// already exists...
@ -417,6 +445,29 @@ function test(){
})
// XXX the main question here is that there is no way to know if a
// particular action is going to be a root action or an action
// callback because we do not know if the action in the parent
// will be available at mix time or not, and the two models
// are different...
// XXX one way to do this is to make all code a callback and
// just use the root as an event trigger...
//
// ...but this effectively means we are implementing
// inheritance ourselves as the traditional name resolution
// will no longer be used, and as in the case we implement
// MRO why not go the whole way and implement multiple
// inheritance in the first place...
//
// ...let's try and avoid this...
/*
var TestActionMixin =
module.TestActionMixin =
ActionMixin({
// XXX
})
*/
console.log('TestActions.testActionGen1()')
TestActions.testActionGen1()
@ -439,7 +490,6 @@ function test(){
/**********************************************************************
* vim:set ts=4 sw=4 : */
return module })