var Y = require('Y').Y
-, Emitter = require('Y/modules/y.event').Emitter
+, getNested = require('Y/types/object').getNested
+, Emitter = require('Y/modules/y.event').Emitter
,
Config =
-exports.Config =
-Y.YObject.subclass('Config', function(){
+exports['Config'] =
+Y.YObject.subclass('Config', function(Config){
Y.core.extend(this, {
init : function initConfig(defaults){
- this._defaults = defaults;
- this._o = Y({}, 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).getNested(key, def, true)
+ var meta = this.ensure(key).getNestedMeta(key, def)
, old = meta.value ;
def = (def === undefined ? old : def);
value = (value === undefined ? def : value);
remove : function remove(key){
if ( key !== undefined ){
var sentinel = {}
- , meta = this.getNested(key, sentinel, true)
+ , meta = this.getNestedMeta(key, sentinel)
, old = (meta.value === sentinel ? undefined : old);
if ( meta.obj ) {
context = context || this;
var ret = Y.reduce(this._o, this._reducer, {
+ 'fn' : fn,
'acc' : acc,
'path' : new Y.YArray(),
'cxt' : context
// Normal value -- invoke iterator
else
- state.acc = fn.call(state.cxt, state.acc, v, chain, this);
+ 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;
--- /dev/null
+//#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('<div/>')
+ .addClass('field');
+ this.elLabel =
+ jQuery('<label/>')
+ .attr('for', this.id)
+ .text(this.label)
+ .appendTo(el);
+ this.elField =
+ jQuery('<input/>')
+ .attr({
+ 'id' : this.id,
+ 'type' : this.type,
+ 'name' : this.key
+ })
+ .val(this.val)
+ .appendTo(el);
+ return this;
+ },
+
+ update : function update(val){
+ var el = this.elField;
+ if (val !== this.val) {
+ this.old = this.val;
+ this.val = val;
+ this.fire('change', this, {
+ 'key' : this.id,
+ 'oldval' : this.old,
+ 'newval' : this.val,
+ 'el' : this.elField
+ });
+ el.val(val);
+ }
+ if (this.type === 'checkbox')
+ el.attr('checked', !!this.val);
+ return this;
+ },
+
+ onChange : function onChange(evt){
+ this.update( this.cast(this.elField.val()) );
+ }
+
+})
+,
+
+
+create =
+exports['create'] =
+function create(config, el){
+ config.parts(
+ function createGroup(oldGroup, value, chain){
+ return jQuery('<fieldset/>')
+ .attr('id', chain)
+ .addClass('group')
+ .append( jQuery('<legend/>').text(chain.split('.').pop()) )
+ .appendTo(el);
+ },
+ function createField(group, value, chain){
+ var def = config.getDefault(chain)
+ , field = new Field(chain, def, value);
+
+ group.append(field.el);
+ config.addEventListener('set:'+chain, function onConfigSet(evt){
+ field.update(evt.data.newval);
+ });
+ field.addEventListener('change', function onFieldChange(evt){
+ config.set(evt.data.key, evt.data.newval);
+ });
+
+ return group;
+ },
+ el);
+ return el;
+}
+;
+
+Y.YString.fn['camelToSpaces'] = Y(camelToSpaces).methodize()
+
+
+Y['scaffold'] = exports;
};
},
- end : function end(o){ return ((o && o.__y__) ? o.end() : o); }
+ // misc
+ end : function end(o){ return ((o && o.__y__) ? o.end() : o); },
+ parseBool : function(s){
+ var i = parseInt(s);
+ return isNaN(i) ? (s && s.toLowerCase() !== 'false') : i;
+ }
};
, _Array = globals.Array
, _String = globals.String
, _Number = globals.Number
+, _Boolean = globals.Boolean
, FN = "constructor"
, PT = "prototype"
core.forEach({
- 'ensure' : ensure,
- 'metaGetter' : metaGetter,
- 'getNested' : getNested,
- 'setNested' : setNested
+ 'ensure' : ensure,
+ 'getNestedMeta' : getNestedMeta,
+ 'getNested' : getNested,
+ 'setNested' : setNested
}, function(fn, name){
fn = exports[name] = YFunction(fn);
YObject.fn[name] = fn.methodize();
, NUM_SAMPLES = 33
,
-methods = {
- // framerate : 0, // Target framerate
- // frametime : 0, // 1000 / framerate
- // samples : NUM_SAMPLES, // Number of frames to track for effective fps
- //
- // now : 0, // Last tick time (ms)
- // fps : 0, // Effective framerate
- // ticks : 0, // Number of ticks since start
- //
- // timer : null,
- // running : false,
- // times : null, // Last `samples` frame durations
+
+EventLoop =
+exports['EventLoop'] =
+Emitter.subclass('EventLoop', {
+ samples : NUM_SAMPLES, // Number of frames to track for effective fps
+ dilation : 1.0,
+
+ framerate : 0, // Target framerate
+ frametime : 0, // 1000 / framerate
+
+ now : 0, // Last tick time (ms)
+ fps : 0, // Effective framerate
+ ticks : 0, // Number of ticks since start
+
+ timer : null,
+ running : false,
+ times : null, // Last `samples` frame durations
/**
this.framerate = framerate;
this.targetTime = 1000 / framerate;
- this.samples = samples || NUM_SAMPLES;
- this.dilation = dilation || 1.0;
+
+ if (samples !== undefined)
+ this.samples = samples;
+ if (dilation !== undefined)
+ this.dilation = dilation;
this.reset();
},
return (this.realtimes.reduce(Y.op.add,0) / this.realtimes.length);
}
-},
-
-EventLoop =
-exports['EventLoop'] =
-Emitter.subclass('EventLoop', methods)
+})
;
+
function decorate(delegate){
if (!delegate) return;
- Emitter.prototype.decorate.call(this, delegate);
+ Emitter.fn.decorate.call(this, delegate);
['start', 'stop', 'reset']
.forEach(function(k){
- delegate[k] = methods[k].bind(this);
+ delegate[k] = EventLoop.fn[k].bind(this);
}, this);
return delegate;
}
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(){
+exports['Config'] =
+Y.YObject.subclass('Config', function(Config){
Y.core.extend(this, {
init : function initConfig(defaults){
- this._defaults = defaults;
- this._o = Y({}, 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).getNested(key, def, true)
state.path.pop();
return state;
+ },
+
+ getDefault : function getDefault(k, def){
+ return getNested(this._defaults, k, def);
}
});
+ this['get'] = this.getNested;
+
});
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
var Y = require('Y').Y
+, Config = require('y/modules/y.config').Config
+,
-, defaults =
+defaults =
exports['defaults'] = {
game : {
timeDilation : 1.0,
gameoverDelay : 1000
},
ui : {
- createGridCanvas : 1,
- createGridTable : 0,
- showGridCoords : 0,
- showAttackCooldown : 0,
+ createGridCanvas : true,
+ createGridTable : false,
+ showGridCoords : false,
+ showAttackCooldown : true,
showCountdown : (document.location.host.toString() !== 'tanks.woo')
},
pathing : {
- overlayAIPaths : 0,
- overlayPathmap : 0,
- traceTrajectories : 0
+ overlayAiPaths : false,
+ overlayPathmap : false,
+ traceTrajectories : false
}
};
-exports['values'] = Y(defaults).clone().end();
+exports['values'] = new Config(defaults);
Game =
exports['Game'] =
Y.subclass('Game', {
- overlayPathmap : config.pathing.overlayPathmap,
- overlayAIPaths : config.pathing.overlayAIPaths,
- timeDilation : config.game.timeDilation,