From 55fcab441a72f8fd39303eeea512f860314dbc83 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Wed, 8 Oct 2014 04:52:41 +0400 Subject: [PATCH] added a more generic version of he toggler (jli.js), not tested yet... Signed-off-by: Alex A. Naanou --- ui (gen4)/lib/actions.js | 100 +++++++++++++-------- ui (gen4)/lib/jli.js | 184 +++++++++++++++++++++++++++++++++++++++ ui (gen4)/viewer.js | 13 +++ 3 files changed, 262 insertions(+), 35 deletions(-) diff --git a/ui (gen4)/lib/actions.js b/ui (gen4)/lib/actions.js index e6ba8735..a4e40b6e 100755 --- a/ui (gen4)/lib/actions.js +++ b/ui (gen4)/lib/actions.js @@ -15,38 +15,67 @@ define(function(require){ var module = {} // Actions are an extension to the JavaScript object model tailored for // a set of specific tasks. // -// The action system consists of these parts: +// Goals: +// - provide a unified mechanism to define and manage user API's for +// use in UI-hooks, keyboard mappings, scripting, ... +// - a means to generate configuration UI's +// - a means to generate documentation // -// 1) documentation generation and introspection -// XXX not all helpers are defined at this point... +// +// 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 +// - a method, created by Action(..), +// - calls all the shadowed 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 full shadow. +// - returns the action set (for call chaining), +// - can consist of two parts: the first is called before the +// shadowed action (pre-callback) and the second after (post-callback). +// - 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. +// +// +// +// The action system provides three components: +// +// 1) Documentation generation and introspection (MetaActions) +// +// .getDoc() +// .getDoc([, ..]) +// -> dict of action-name, doc +// +// .actions +// -> list of action names // // -// 2) event-like callbacks for actions +// 2) Event-like callbacks for actions (MetaActions, Action) // -// MyActions.on('action', function(){ ... }) -// MyActions.on('action.post', function(){ ... }) +// .on('action', function(){ ... }) +// .on('action.post', function(){ ... }) // -// MyActions.on('action.pre', function(){ ... }) +// .on('action.pre', function(){ ... }) // // -// 3) a mechanism to extend already defined actions +// 3) A mechanism to define and extend already defined actions // This replaces / complements the standard JavaScript overloading -// mechanisms, here is a direct comparison: -// -// -// // Native... -// var X = { -// m: function(){ console.log('m') } -// } -// var O = { -// m: function(){ -// console.log('pre') -// B.__proto__.m.call(this) -// console.log('post') -// } -// } -// O.__proto__ = X -// +// mechanisms (Action, Actions) // // // Actions... // var X = Actions({ @@ -61,15 +90,10 @@ define(function(require){ var module = {} // }] // }) // -// -// Comparing to the native system: -// + no need to chain overloaded calls by hand (automatic) -// +/- more restrictive -- no way to prevent original actions from -// running, i.e. no way to shadow. -// +/- hidden the internals (.__proto__ assignment) -// - more structural code (returning a callback vs. B.__proto__.m.call) -// NOTE: that the Actions(..) call and lists containing functions -// is not added complexity as they are mainly used for docs. +// 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. // // // @@ -246,6 +270,7 @@ module.MetaActions = { var res = {} var that = this actions = actions == null ? this.actions() + : arguments.length > 1 ? args2array(arguments) : typeof(actions) == typeof('str') ? [actions] : actions // get the first defined set of docs in the inheritance chain... @@ -262,9 +287,14 @@ module.MetaActions = { return res }, - // Collect all the handlers from the inheritance chain and arrange - // them up-down, first defined to last... + // Get action handlers from the inheritance chain... // + // NOTE: this collects both the event handlers (in order of hierarchy, + // then order of definition) and actions (in order of hierarchy) + // NOTE: this is the correct order for 'pre' calling, but is the + // reverse of how the 'post' handlers must be called. + // + // For more docs on handler sequencing and definition see: .on(..) getHandlers: function(name){ var handlers = [] var cur = this diff --git a/ui (gen4)/lib/jli.js b/ui (gen4)/lib/jli.js index 0e3287f9..40d20c43 100755 --- a/ui (gen4)/lib/jli.js +++ b/ui (gen4)/lib/jli.js @@ -231,6 +231,189 @@ function createCSSClassToggler(elem, class_list, callback_a, callback_b){ } +// Make a generic toggler function/method... +// +// state_accessor signature: +// +// Get current state: +// state_accessor() +// -> +// +// Set new state: +// state_accessor() +// -> +// +// NOTE: for single state toggling, 'none' will get passed to +// state_accessor to indicate an "empty" state... +// +function makeToggler(state_accessor, states, callback_a, callback_b){ + // normalize states... + states = typeof(states) == typeof('str') ? ['none', states] : states + // normalize the callbacks... + if(callback_b == null){ + var callback_pre = null + var callback_post = callback_a + } else { + var callback_pre = callback_a + var callback_post = callback_b + } + + var bool_action = (states.length == 2 && states[0] == 'none') + + var func = function(a, b){ + // so as to be able to distinguish between a method and a root + // context in a simple manner... + 'use strict' + + // parse arguments... + if(b == null){ + var action = a == 'next' ? null : a + // XXX is this correct??? + var e = this + } else { + var e = a + var action = b == 'next' ? null : b + } + + // option number... + if(typeof(action) == typeof(1)){ + // range check... + if(action < 0 || action >= states.length){ + return null + } + if(bool_action){ + action = action == 0 ? 'off' : 'on' + } else { + action = states[action] + } + } + + // we need to get the current state... + if(action == null || action == '?' || action == '!'){ + // get current state... + var cur = state_accessor.call(e) + + // just asking for info... + if(action == '?'){ + return bool_action ? (cur == 'none' ? 'off' : 'on') : cur + } + + // force reload of current state... + if(action == '!'){ + action = bool_action ? (cur == 'none' ? 'off' : 'on') : cur + } + + // invalid action... + } else if((bool_action && ['on', 'off'].indexOf(action) == -1) + || (!bool_action && states.indexOf(action) == -1)){ + return null + } + + var state = bool_action ? states[1] : action + // get the right class... + if(action == null){ + var i = states.indexOf(cur)+1 + i = i == -1 ? 0 : i + i = i == states.length ? 0 : i + state = states[i] + + if(bool_action){ + action = state == 'none' ? 'off' : 'on' + } else { + action = state + } + } + + // NOTE: the callbacks are passed the same this as the calling + // function, this will enable them to act as metods correctly + // pre callback... + if(callback_pre != null){ + if(callback_pre.apply(this, [action, e].concat(args)) === false){ + //return + return func('?') + } + } + + // update the element... + state_accessor.call(e, state) + + // post callback... + if(callback_post != null){ + callback_post.apply(this, [action, e].concat(args)) + } + + return action + } + + func.states = states + if(bool_action){ + func.doc = 'With no arguments this will toggle between "on" and '+ + '"off".\n'+ + 'If either "on" or "off" are given then this will switch '+ + 'to that mode.\n'+ + 'If "?" is given, this will return either "on" or "off" '+ + 'depending on the current state.' + }else{ + func.doc = 'With no arguments this will toggle between '+ + states +' in cycle.\n' + + 'if any of the state names or its number is given then that '+ + 'state is switched on.'+ + 'If "?" is given, this will return the current state.' + } + + return func +} + + +// XXX this should be drop-in compatible with createCSSClassToggler(..) +// test and replace... +function makeCSSClassToggler(elem, classes, callback_a, callback_b){ + // normalize the states... + classes = typeof(classes) == typeof('str') ? ['none', classes] : classes + // remove the dot from class names... + // NOTE: this is here because I've made the error of including a + // leading "." almost every time I use this after I forget + // the UI... + classes = classes + .map(function(e){ + return e.split(' ') + .map(function(c){ + c = c.trim() + return c[0] == '.' ? c.slice(1) : c + }).join(' ') + }) + + return makeToggler( + function(state){ + var e = $(this == null ? elem : this) + // get the state... + if(state == null){ + var cur = 'none' + for(var i=0; i < classes.length; i++){ + // XXX make this faster by getting the class list once + // and checking in that rather than doing a + // .hasClass(..) per iteration... + if(e.hasClass(classes[i])){ + cur = classes[i] + break + } + } + return cur + + // set the state... + } else { + e.removeClass(classes.join(' ')) + if(state != 'none' && action != 'off'){ + e.addClass(state) + } + } + }, + classes, + callback_a, + callback_b) +} + + /* // show a jQuary opject in viewer overlay... @@ -1121,6 +1304,7 @@ Array.prototype.compact = function(){ // like .length but for sparse arrays will return the element count... +// XXX make this a prop... Array.prototype.len = function(){ return this.compact().length } diff --git a/ui (gen4)/viewer.js b/ui (gen4)/viewer.js index 4d34f52d..af7eb053 100755 --- a/ui (gen4)/viewer.js +++ b/ui (gen4)/viewer.js @@ -11,7 +11,20 @@ console.log('>>> viewer') /*********************************************************************/ +// +// XXX Tasks to accomplish here: +// - life-cycle actions/events +// - setup +// - reset +// - "features" and the mechanism to turn them on or off (action-sets) +// +// +var Client = { +} + +var Viewer = { +}