From 693c3ded68a712f277990a3d6ed369ac2f586d19 Mon Sep 17 00:00:00 2001 From: dsc Date: Fri, 17 Dec 2010 04:00:33 -0800 Subject: [PATCH] Config UI scaffold. --- src/Y/modules/y.config.cjs | 143 +++++++++++++++++++++++++++++++++++++ src/Y/modules/y.scaffold.cjs | 151 ++++++++++++++++++++++++++++++++++++++++ src/Y/op.cjs | 7 ++- src/Y/type.cjs | 1 + src/Y/types/object.cjs | 8 +- src/ezl/loop/eventloop.cjs | 47 +++++++------ src/ezl/util/configuration.cjs | 114 ++++++++++++++++++++++++++++++ src/tanks/config.cjs | 20 +++--- src/tanks/game.cjs | 11 ++-- src/tanks/map/level.cjs | 4 +- src/tanks/map/pathmap.cjs | 12 ++- src/tanks/thing/bullet.cjs | 3 +- src/tanks/thing/thing.cjs | 2 +- src/tanks/ui/config.cjs | 88 ----------------------- src/tanks/ui/configui.cjs | 41 +++++++++++ src/tanks/ui/grid.cjs | 4 +- src/tanks/ui/index.cjs | 2 +- src/tanks/ui/main.cjs | 25 ++++--- src/tanks/util/config.cjs | 100 -------------------------- www/css/lttl.css | 35 ++++++--- www/debug.html | 28 ++++---- www/deps.html | 8 ++- www/favicon.ico | 2 +- www/panes.html | 1 + www/welcome.html | 2 +- 25 files changed, 578 insertions(+), 281 deletions(-) create mode 100644 src/Y/modules/y.config.cjs create mode 100644 src/Y/modules/y.scaffold.cjs create mode 100644 src/ezl/util/configuration.cjs delete mode 100644 src/tanks/ui/config.cjs create mode 100644 src/tanks/ui/configui.cjs delete mode 100644 src/tanks/util/config.cjs create mode 100644 www/panes.html diff --git a/src/Y/modules/y.config.cjs b/src/Y/modules/y.config.cjs new file mode 100644 index 0000000..ff41a55 --- /dev/null +++ b/src/Y/modules/y.config.cjs @@ -0,0 +1,143 @@ +var Y = require('Y').Y +, getNested = require('Y/types/object').getNested +, Emitter = require('Y/modules/y.event').Emitter +, + + +Config = +exports['Config'] = +Y.YObject.subclass('Config', function(Config){ + + Y.core.extend(this, { + + init : function initConfig(defaults){ + this._defaults = defaults || {}; + this._o = Y({}, this._defaults); + this.__emitter__ = new Emitter(this); + }, + + clone : function clone(){ + var c = new Config(); + c._defaults = this._defaults; + c._o = c._o.clone(); + return c; + }, + + set : function set(key, value, def){ + if ( key !== undefined ){ + var meta = this.ensure(key).getNestedMeta(key, def) + , old = meta.value ; + def = (def === undefined ? old : def); + value = (value === undefined ? def : value); + + meta.obj[meta.key] = value; + this._fireMutation('set', key, old, value, meta.obj); + } + return this; + }, + + remove : function remove(key){ + if ( key !== undefined ){ + var sentinel = {} + , meta = this.getNestedMeta(key, sentinel) + , old = (meta.value === sentinel ? undefined : old); + + if ( meta.obj ) { + delete meta.obj[meta.key]; + this._fireMutation('remove', key, old, undefined, meta.obj); + } + } + return this; + }, + + /** + * @private + */ + _fireMutation : function _fireMutation(evt, key, oldval, newval, obj){ + if (newval !== oldval){ + var data = { + 'key' : key, + 'oldval' : oldval, + 'newval' : newval, + 'obj' : obj, + 'root' : this + }; + this.fire(evt+':'+key, this, data); + this.fire(evt, this, data); + } + return this; + }, + + /** + * Recursively iterates the hierarchy of the Config, depth-first. + */ + reduce : function reduce(fn, acc, context){ + context = context || this; + + var ret = Y.reduce(this._o, this._reducer, { + 'fn' : fn, + 'acc' : acc, + 'path' : new Y.YArray(), + 'cxt' : context + }, this); + + return ret.acc; + }, + + /** + * @private + * XXX: Not going to call the iterator on root objects. ok? + * XXX: Always passing the Config object as obj to iterator. ok? + */ + _reducer : function _reducer(state, v, k, o){ + var chain = state.path.push(k).join('.'); + + // Nested object -- recurse + if ( Y.isPlainObject(v) ) + state = Y.reduce(v, this._reducer, state, this); + + // Normal value -- invoke iterator + else + state.acc = state.fn.call(state.cxt, state.acc, v, chain, this); + + state.path.pop(); + return state; + }, + + /** + * Iterates over both items and groups of the config object. + */ + parts : function parts(groupFn, itemFn, acc, context){ + context = context || this; + + var path = new Y.YArray(); + return Y.reduce(this._o, + function _parts(acc, v, k, o){ + var chain = path.push(k).join('.'); + + // Nested object -- recurse + if ( Y.isPlainObject(v) ){ + acc = groupFn.call(context, acc, v, chain, this); + acc = Y.reduce(v, _parts, acc, this); + + // Normal value -- invoke iterator + } else + acc = itemFn.call(context, acc, v, chain, this); + + path.pop(); + return acc; + }, + acc, this); + }, + + getDefault : function getDefault(k, def){ + return getNested(this._defaults, k, def); + } + + }); + + this['get'] = this.getNested; + +}); + +Y['config'] = exports; diff --git a/src/Y/modules/y.scaffold.cjs b/src/Y/modules/y.scaffold.cjs new file mode 100644 index 0000000..45a9d5c --- /dev/null +++ b/src/Y/modules/y.scaffold.cjs @@ -0,0 +1,151 @@ +//#ensure "jquery" +var Y = require('Y').Y +, Emitter = require('Y/modules/y.event').Emitter +, op = Y.op + + +, upperPat = /^[A-Z]+$/ +, symbolPat = /^[^a-zA-Z]+$/ +, +camelToSpaces = +exports['camelToSpaces'] = +function camelToSpaces(s){ + return Y(s).reduce( + function(acc, ch, i){ + return acc + ( + symbolPat.test(ch) ? '' : + (upperPat.test(ch) ? ' '+ch : ch) ); + }, ''); +} +, + +type2parser = { + 'Boolean' : op.parseBool, + 'Number' : parseFloat, + 'String' : String +} +, + +type2el = { + 'Boolean' : 'checkbox', + 'Number' : 'text', + 'String' : 'text' + // 'Array' : 'select', + // 'Date' : 'datepicker', + // 'RegExp' : 'text' +} +, + + + +Field = +exports['Field'] = +Y.subclass('Field', { + + init : function initField(chain, def, val, options){ + options = options || {}; + this.__emitter__ = new Emitter(this); + + this.id = chain; + this.def = def; + this.val = this.old = val === undefined ? def : val; + this.key = chain.split('.').pop(); + this.label = camelToSpaces(this.key); + + var T = Y(Y.type(val)).getName(); + this.cast = options.cast || type2parser[T]; + this.type = options.type || type2el[T]; + + if (!this.cast) + throw new Error('No parser defined for type "'+T+'"'); + if (!this.type) + throw new Error('No field element defined for type "'+T+'"'); + + this.build() + .update(this.val); + + this.elField.bind('change', this.onChange.bind(this)); + }, + + build : function build(){ + var el = + this.el = + jQuery('
') + .addClass('field'); + this.elLabel = + jQuery('