Config UI scaffold.
authordsc <david.schoonover@gmail.com>
Fri, 17 Dec 2010 12:00:33 +0000 (04:00 -0800)
committerdsc <david.schoonover@gmail.com>
Fri, 17 Dec 2010 12:00:33 +0000 (04:00 -0800)
24 files changed:
src/Y/modules/y.config.cjs [copied from src/tanks/util/config.cjs with 59% similarity]
src/Y/modules/y.scaffold.cjs [new file with mode: 0644]
src/Y/op.cjs
src/Y/type.cjs
src/Y/types/object.cjs
src/ezl/loop/eventloop.cjs
src/ezl/util/configuration.cjs [moved from src/tanks/util/config.cjs with 84% similarity]
src/tanks/config.cjs
src/tanks/game.cjs
src/tanks/map/level.cjs
src/tanks/map/pathmap.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/thing.cjs
src/tanks/ui/config.cjs [deleted file]
src/tanks/ui/configui.cjs [new file with mode: 0644]
src/tanks/ui/grid.cjs
src/tanks/ui/index.cjs
src/tanks/ui/main.cjs
www/css/lttl.css
www/debug.html
www/deps.html
www/favicon.ico
www/panes.html [new file with mode: 0644]
www/welcome.html

similarity index 59%
copy from src/tanks/util/config.cjs
copy to src/Y/modules/y.config.cjs
index 2b90bba..ff41a55 100644 (file)
@@ -1,23 +1,31 @@
 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);
@@ -31,7 +39,7 @@ Y.YObject.subclass('Config', function(){
         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 ) {
@@ -67,6 +75,7 @@ Y.YObject.subclass('Config', function(){
             context = context || this;
             
             var ret = Y.reduce(this._o, this._reducer, {
+                'fn'   : fn,
                 'acc'  : acc,
                 'path' : new Y.YArray(),
                 'cxt'  : context
@@ -89,12 +98,46 @@ Y.YObject.subclass('Config', function(){
                 
             // 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;
diff --git a/src/Y/modules/y.scaffold.cjs b/src/Y/modules/y.scaffold.cjs
new file mode 100644 (file)
index 0000000..45a9d5c
--- /dev/null
@@ -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('<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;
index 1acfcbe..408e640 100644 (file)
@@ -70,7 +70,12 @@ var core = require('Y/core')
         };
     },
     
-    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;
+    }
     
 };
 
index 1b04091..18c7cb8 100644 (file)
@@ -8,6 +8,7 @@ var undefined
 ,   _Array    = globals.Array
 ,   _String   = globals.String
 ,   _Number   = globals.Number
+,   _Boolean  = globals.Boolean
 
 ,   FN = "constructor"
 ,   PT = "prototype"
index 42a2a60..e3a57ba 100644 (file)
@@ -96,10 +96,10 @@ YCollection.subclass('YObject', function setupYObject(YObject){
 
 
 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();
index aaccdd5..d744531 100644 (file)
@@ -7,18 +7,23 @@ var Y = require('Y').Y
 ,   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
     
     
     /**
@@ -37,8 +42,11 @@ methods = {
         
         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();
     },
@@ -118,19 +126,16 @@ methods = {
         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;
 }
similarity index 84%
rename from src/tanks/util/config.cjs
rename to src/ezl/util/configuration.cjs
index 2b90bba..539bd0a 100644 (file)
@@ -1,20 +1,28 @@
 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)
@@ -93,8 +101,14 @@ Y.YObject.subclass('Config', function(){
             
             state.path.pop();
             return state;
+        },
+        
+        getDefault : function getDefault(k, def){
+            return getNested(this._defaults, k, def);
         }
         
     });
     
+    this['get'] = this.getNested;
+    
 });
index aa180cf..2a1250b 100644 (file)
@@ -1,24 +1,26 @@
 //  -*- 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);
index 4d0787d..0926019 100644 (file)
@@ -19,10 +19,9 @@ var Y         = require('Y').Y
 Game =
 exports['Game'] = 
 Y.subclass('Game', {
-    overlayPathmap : config.pathing.overlayPathmap,
-    overlayAIPaths : config.pathing.overlayAIPaths,
-    timeDilation   : config.game.timeDilation,
-    gameoverDelay  : config.game.gameoverDelay,
+    overlayPathmap : config.get('pathing.overlayPathmap'),
+    overlayAiPaths : config.get('pathing.overlayAiPaths'),
+    gameoverDelay  : config.get('game.gameoverDelay'),
     
     gameover      : false,
     
@@ -37,7 +36,7 @@ Y.subclass('Game', {
         this.animations = new Y.YArray();
         
         this.viewport = $(viewport || GRID_ELEMENT);
-        this.loop = new EventLoop(FRAME_RATE, this, this.timeDilation);
+        this.loop = new EventLoop(FRAME_RATE, this);
         
         this.root = 
         this.grid = 
@@ -92,7 +91,7 @@ Y.subclass('Game', {
         SECONDTH = ELAPSED / 1000;
         SQUARETH = REF_SIZE * SECONDTH
         
-        if (!this.overlayAIPaths)
+        if (!this.overlayAiPaths)
             this.pathmap.hidePaths();
         
         this.active.invoke('updateCooldowns', ELAPSED, NOW);
index db6d81f..ac86997 100644 (file)
@@ -38,12 +38,12 @@ Rect.subclass('Level', {
         
         P = 
         game.player = game.addUnit(new PlayerTank(1), 5,9);
-        game.addUnit(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
+        // game.addUnit(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
         
         E = 
         game.addUnit(new Tank(2), 0,1);
         game.addUnit(new Tank(2), 1,0);
-        game.addUnit(new Tank(2), 8,1);
+        // game.addUnit(new Tank(2), 8,1);
     },
     
     addWall : function addWall(x,y, w,h, isBoundary){
index c107629..5e29503 100644 (file)
@@ -1,16 +1,20 @@
 //  -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
 var Y        = require('Y').Y
 ,   math     = require('ezl/math')
-,   Vec      = math.Vec
-,   Line     = math.Line
 ,   QuadTree = require('ezl/util/tree/quadtree').QuadTree
-,   Bullet   = require('tanks/thing/bullet').Bullet
 ,   astar    = require('ezl/util/astar')
+,   Bullet   = require('tanks/thing/bullet').Bullet
+,   config   = require('tanks/config').values
+,   Vec      = math.Vec
+,   Line     = math.Line
 ,
 
 PathMap =
 exports['PathMap'] =
 QuadTree.subclass('PathMap', {
+    overlayAiPaths : config.get('pathing.overlayAiPaths'),
+    overlayPathmap : config.get('pathing.overlayPathmap'),
+    
     gridSquareSize  : REF_SIZE,
     gridSquareMidPt : new Vec(REF_SIZE/2, REF_SIZE/2),
     
@@ -220,7 +224,7 @@ QuadTree.subclass('PathMap', {
         ,   path = Y(astar.search(grid, startN, endN))
         ;
         
-        if (tanks.config.values.pathing.overlayAIPaths)
+        if (this.overlayAiPaths)
             this.drawPath(id, startN, path);
         
         return path
index 2f2b155..cf341d6 100644 (file)
@@ -6,6 +6,7 @@ var Y          = require('Y').Y
 ,   Thing      = require('tanks/thing/thing').Thing
 ,   Trajectory = require('tanks/map/trajectory').Trajectory
 ,   Explosion  = require('tanks/fx/explosion').Explosion
+,   config     = require('tanks/config').values
 ,   Line       = shape.Line
 ,   Circle     = shape.Circle
 ,
@@ -131,7 +132,7 @@ Thing.subclass('Bullet', {
     render : function render(parent){
         this.remove();
         
-        if (tanks.config.values.pathing.traceTrajectories) {
+        if (config.get('pathing.traceTrajectories')) {
             var t = this.trajectory;
             this.tline = Line.fromPoints(t.x1,t.y1, t.x2,t.y2)
                 .attr('drawDefinitionPoints', true)
index 24cf897..e0e634a 100644 (file)
@@ -14,7 +14,7 @@ var Y           = require('Y').Y
 Thing =
 exports['Thing'] =
 new evt.Class('Thing', {
-    showAttackCooldown : config.ui.showAttackCooldown,
+    showAttackCooldown : config.get('ui.showAttackCooldown'),
     
     // Attributes
     stats: {
diff --git a/src/tanks/ui/config.cjs b/src/tanks/ui/config.cjs
deleted file mode 100644 (file)
index 0f544e3..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-//#ensure "jquery"
-
-
-// TODO
-// ====
-// - store at tanks.config.values, but clone at start to t.c.defaults
-// - need Config class to run triggers on changes
-// - button to clear saved config
-// 
-// init
-// - For each item in tanks.config (recursive):
-// - Create a UI box based on type, with a name based on its dotted path
-// - Check cookies -- fill it with the value from cookies or default
-// 
-// update
-// - For each config element:
-// - Extract value, typecast based on default's type
-// - If different from default, set cookie
-// - Update tanks.config.values
-
-var Y = require('Y').Y
-,   config = require('tanks/config')
-,   c = config.values
-,   p = c.pathing
-;
-
-
-
-
-exports['init'] = function initConfigUi(){
-    $('#config [name=pathmap]').attr('checked', p.overlayPathmap);
-    $('#config [name=aipaths]').attr('checked', p.overlayAIPaths);
-    $('#config [name=trajectories]').attr('checked', p.traceTrajectories);
-    
-    $('#config [name=gridCoords]').attr('checked', c.ui.showGridCoords);
-    $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
-};
-
-exports['update'] = function updateConfigUi(evt){
-    p.overlayPathmap    = $('#config [name=pathmap]').attr('checked');
-    p.overlayAIPaths    = $('#config [name=aipaths]').attr('checked');
-    p.traceTrajectories = $('#config [name=trajectories]').attr('checked');
-    
-    c.ui.showGridCoords = $('#config [name=gridCoords]').attr('checked');
-    $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
-};
-
-// var templates = {
-//     'label'    : { html:'<label for="{id}">{label}</label>' },
-//     'input'    : { html:'<input id="{id}" type="{type}" name="{key}" value="{value}">' },
-//     'text'     : { include:'input', type:'text' },
-//     'checkbox' : { include:'input', type:'checkbox', props:{ 'checked':false } },
-//     'radio'    : { include:'input', type:'radio' }
-//     // 'select'   : '',
-//     // 'textarea' : '',
-//     // 'hidden'   : '',
-//     // 'button'   : ''
-// };
-// 
-// var type2el = {
-//     'Boolean'
-//     'Number' : 
-//     'String' : 'text'
-//     // 'Array'
-//     // 'Date'
-//     // 'RegExp'
-//     // 'Function'
-//     // 'Object'
-// };
-// exports['makeField'] = function makeField(key, value){
-//     
-// };
-
-var upperPat = /^[A-Z]+$/
-,   symbolPat = /^[^a-zA-Z]+$/
-;
-
-Y.YString.prototype.splitCamel =
-    function splitCamel(s){
-        return this.reduce(
-                function(acc, ch, i){
-                    return acc + (
-                        symbolPat.test(ch) ? '' : 
-                        (upperPat.test(ch) ? ' '+ch : ch) );
-                }, '')
-            .split(' ');
-    };
-
diff --git a/src/tanks/ui/configui.cjs b/src/tanks/ui/configui.cjs
new file mode 100644 (file)
index 0000000..87df7b6
--- /dev/null
@@ -0,0 +1,41 @@
+//#ensure "jquery"
+var Y = require('Y').Y
+,   EventLoop = require('ezl/loop/eventloop').EventLoop
+,   scaffold  = require('Y/modules/y.scaffold')
+,   config    = require('tanks/config').values
+,   configUI
+;
+
+exports['init'] =
+function initConfigUi(){
+    configUI = scaffold.create(config, jQuery('#config .box'));
+    configUI.append( jQuery('<div class="clearer"></div>') );
+    
+    config.addEventListener('set:game.timeDilation', function(evt){
+        EventLoop.fn.dilation = evt.data.newval;
+    });
+};
+
+
+// exports['init'] =
+// function initConfigUi(){
+//     $('#config [name=pathmap]').attr('checked',         config.get('pathing.overlayPathmap'));
+//     $('#config [name=aipaths]').attr('checked',         config.get('pathing.overlayAiPaths'));
+//     $('#config [name=trajectories]').attr('checked',    config.get('pathing.traceTrajectories'));
+//     
+//     $('#config [name=gridCoords]').attr('checked',      config.get('ui.showGridCoords'));
+//     $('#viewport .layer.grid')[(config.get('ui.showGridCoords') ? 'add' : 'remove')+'Class']('showGridCoords');
+// };
+// 
+// exports['update'] =
+// function updateConfigUi(evt){
+//     config.set('pathing.overlayPathmap',        $('#config [name=pathmap]').attr('checked'));
+//     config.set('pathing.overlayAiPaths',        $('#config [name=aipaths]').attr('checked'));
+//     config.set('pathing.traceTrajectories',     $('#config [name=trajectories]').attr('checked'));
+//     
+//     config.set('ui.showGridCoords',             $('#config [name=gridCoords]').attr('checked'));
+//     $('#viewport .layer.grid')[(config.get('ui.showGridCoords') ? 'add' : 'remove')+'Class']('showGridCoords');
+// };
+
+
+
index 7424f5c..624ae5f 100644 (file)
@@ -11,8 +11,8 @@ Rect.subclass('Grid', {
     strokeStyle : '#6E6E6E',
     lineWidth   : 0.5,
     
-    createGridTable  : config.ui.createGridTable,
-    createGridCanvas : config.ui.createGridCanvas,
+    createGridTable  : config.get('ui.createGridTable'),
+    createGridCanvas : config.get('ui.createGridCanvas'),
     
     
     
index 9fe937d..d2a9874 100644 (file)
@@ -1,3 +1,3 @@
 var Y = require('Y').Y;
-Y.extend(exports, require('tanks/ui/config'));
+Y.extend(exports, require('tanks/ui/configui'));
 exports['main'] = require('tanks/ui/main');
index b9043d0..105a4e3 100644 (file)
@@ -4,14 +4,15 @@
 require("Y/modules/y.kv");
 
 var Y            = require('Y').Y
-,   config       = require('tanks/config')
-,   uiconfig     = require('tanks/ui/config')
+,   configui     = require('tanks/ui/configui')
 ,   Game         = require('tanks/game').Game
 ,   Tank         = require('tanks/thing').Tank
 ,   FpsSparkline = require('ezl/loop').FpsSparkline
+,   config       = require('tanks/config').values
 
 ,   updateTimer  = null
 ;
+function stopProp(evt){ evt.stopPropagation(); }
 
 qkv = Y(window.location.search.slice(1)).fromKV();
 
@@ -25,7 +26,7 @@ function main(){
         $('#ai').toggle();
         $('#ai textarea')[0].focus();
     }
-    if (qkv.debug) $('#debug').toggle();
+    if (qkv.debug) $('#info').toggle();
     
     $('#ai .ready').bind('click', function(evt){
         try {
@@ -34,18 +35,21 @@ function main(){
             $('#ai').hide();
             startGame();
         } catch(e) {
-            alert('AI Error! '+e);
+            alert('AI Error!\n'+e);
         }
     });
     
-    // Show debug
-    $(document).bind('keydown', 'ctrl+c', function(evt){ $('#debug').toggle(); });
+    // Show debug menus
+    $(document).bind('keydown', 'ctrl+d', function(evt){ $('.debug').toggle(); });
+    $(document).bind('keydown', 'ctrl+i', function(evt){ $('#info').toggle(); });
+    $(document).bind('keydown', 'ctrl+c', function(evt){ $('#config').toggle(); });
     
     // Don't fire on clicks in the debug menu
-    $('#debug').bind('mousedown', Y.op.K(false));
+    $('.debug').bind('mousedown', stopProp);
+    $('.debug').bind('click',     stopProp);
     
-    // Update config when changed
-    $('#debug input').bind('change', uiconfig.update);
+    // Build and bind config
+    configui.init();
     
     
     // Create #pause box
@@ -105,7 +109,6 @@ function setupUI(){
     
     if (!qkv.ai) $('#welcome').show();
     LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
-    uiconfig.init();
     LBT.root.draw();
     
     // Start button (click or return key)
@@ -141,7 +144,7 @@ function startGame(evt){
     
     $('#welcome').hide();
     
-    if ( config.values.ui.showCountdown )
+    if ( config.get('ui.showCountdown') )
         countdown(3, readyToStart);
     else
         readyToStart();
index 6854074..13df1ac 100644 (file)
@@ -1,14 +1,13 @@
-html { width:100%; height:100%; top:0; left:0; margin:0; padding:0; background-color:#3F3F3F; }
-body { position:absolute; width:100%; top:0; left:0; margin:0; padding:0;
-    font-family:Geogrotesque,Helvetica; font-size:12pt; color:#fff; }
+html, body { width:100%; height:100%; top:0; left:0; margin:0; padding:0; background-color:#3F3F3F; }
+body { position:absolute; font-family:Geogrotesque,Helvetica; font-size:12pt; color:#fff; }
 h1, h2, h3, h4, h5 { margin:0.75em 0; font-weight:normal;  }
 ul, ol, li { list-style: none ! important; margin:0; padding:0; }
 
 .clearer { clear:both !important; float:none !important;
     margin:0 !important; padding:0 !important; }
 .rounded { border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
-.box {
-    padding:0.5em; background-color:rgba(0,0,0, 0.1); color:#787878;
+.hud {
+    padding:0.5em; background-color:rgba(0,0,0, 0.5); color:#787878;
     border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
 
 body > h1 { position:fixed; top:0; right:0; margin:0; padding:0; font-size:3em; color:#000; opacity:0.25; z-index:100; }
@@ -57,21 +56,33 @@ table.grid td { /* outline:1px solid rgba(255,255,255,0.1); */
 #welcome legend { padding:0.5em; text-transform:uppercase; }
 */
 
+/* 
 #debug { position:relative; top:1em; right:1em; z-index:500; }
     #debug .inner { position:absolute; top:0; right:0; padding:1em; }
     #debug .inner > * { float:right; margin-right:1em; }
+*/
 
-#config { position:relative; width:250px; }
-    #config > div { position:relative; line-height:1.5em; }
-    #config label { position:relative; width:69%; display:inline-block; }
-    #config input { position:absolute; width:30%; top:0; right:0; text-align:center;
-        border:0; background-color:rgba(0,0,0, 0.1) ! important; color:#5c5c5c; }
-    #config input[type=checkbox] { top:0.33em; }
+.debug { z-index:500; }
+    .debug .inner { top:0; right:0; padding:1em; }
 
-#info { position:relative; width:250px; }
+#info { position:absolute; top:1em; right:1em; }
+    #info .box { width:250px; }
     #info #state { font-weight:bold; }
     #info label { display:block; float:left; width:5em; margin-right:0.5em; color:#787878; }
     #info input { border:0; background-color:transparent; min-width:5em; width:5em; color:#5c5c5c; }
     #info .sep { opacity:0.1; background-color:#999; margin:5px 0; height:1px; }
     #info .fps-sparkline { width:100%; height:1.5em; margin-top:0.5em; }
 
+#config { position:absolute; bottom:1em; left:1em; }
+    #config .box { }
+    #config .group { width:250px; float:left; margin-right:1em; }
+    #config .group:last-of-type { margin-right:0; }
+    #config h3 { font-weight:bold; margin:0; position:absolute; bottom:1em; left:1em; }
+    #config fieldset { border:1px solid #999; padding:0.5em; }
+    #config legend { padding:0.5em; text-transform:uppercase; color:#787878; }
+    #config .field { position:relative; line-height:1.5em; }
+    #config label { position:relative; width:69%; display:inline-block; text-transform:lowercase; }
+    #config input { position:absolute; width:30%; top:0; right:0; text-align:center;
+        border:0; background-color:rgba(0,0,0, 0.1) ! important; color:#5c5c5c; }
+    #config input[type=checkbox] { top:0.33em; }
+
index 5f3371f..15fd901 100644 (file)
@@ -1,23 +1,13 @@
 <div id="ai" style="display:none" class="bigblue">
-    <div class="box">
+    <div class="box hud">
         <h2>Add Tank AI</h2>
         <textarea id="customtank"></textarea>
         <div class="ready pinkbutton rounded">Ready</div>
     </div>
 </div>
 
-<div id="debug" style="display:none"><div class="inner box">
-    
-    <div id="config">
-        <h3>config</h3>
-        <!--<div><label for="bullets">bullets</label> <input id="bullets" name="bullets" value="10" type="text"></div>-->
-        <div><label for="pathmap">overlay pathmap</label> <input id="pathmap" name="pathmap" value="1" type="checkbox"></div>
-        <div><label for="aipaths">overlay ai paths</label> <input id="aipaths" name="aipaths" value="1" type="checkbox"></div>
-        <div><label for="trajectories">trace trajectories</label> <input id="trajectories" name="trajectories" value="1" type="checkbox"></div>
-        <div><label for="gridCoords">show coords on hover</label> <input id="gridCoords" name="gridCoords" value="1" type="checkbox"></div>
-    </div>
-    
-    <ul id="info">
+<div id="info" class="debug" style="display:none"><div class="inner hud">
+    <ul class="box">
         <li id="state"></li>
         <li><div class="fps-sparkline"></div></li>
         <li><label for="info_fps"   >fps</label>       <input id="info_fps"     name="fps"     value="" type="text"></li>
         <li><label for="info_bullets">bullets</label>  <input id="info_bullets" name="bullets" value="" type="text"></li>
         <li><label for="info_anims">animations</label> <input id="info_anims"   name="anims"   value="" type="text"></li>
     </ul>
-    
     <div class="clearer"></div>
 </div></div>
+
+<div id="config" class="debug" style="display:none"><div class="inner hud">
+    <h3>config</h3>
+    <div class="box"></div>
+    <!--
+    <div><label for="pathmap">overlay pathmap</label> <input id="pathmap" name="pathmap" value="1" type="checkbox"></div>
+    <div><label for="aipaths">overlay ai paths</label> <input id="aipaths" name="aipaths" value="1" type="checkbox"></div>
+    <div><label for="trajectories">trace trajectories</label> <input id="trajectories" name="trajectories" value="1" type="checkbox"></div>
+    <div><label for="gridCoords">show coords on hover</label> <input id="gridCoords" name="gridCoords" value="1" type="checkbox"></div>-->
+</div></div>
+
index 49e9edf..e1bea90 100644 (file)
 <script src="build/ezl.js" type="text/javascript"></script>
 <script src="build/tanks/globals.js" type="text/javascript"></script>
 <script src="build/jquery.hotkeys.js" type="text/javascript"></script>
+<script src="build/mustache.js" type="text/javascript"></script>
+<script src="build/jquery.mustache.js" type="text/javascript"></script>
 <script src="build/ezl/util/binaryheap.js" type="text/javascript"></script>
 <script src="build/ezl/util/astar.js" type="text/javascript"></script>
-<script src="build/tanks/config.js" type="text/javascript"></script>
+<script src="build/Y/modules/y.scaffold.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.kv.js" type="text/javascript"></script>
 <script src="build/ezl/util/tree/quadtree.js" type="text/javascript"></script>
-<script src="build/tanks/ui/config.js" type="text/javascript"></script>
+<script src="build/y/modules/y.config.js" type="text/javascript"></script>
+<script src="build/tanks/config.js" type="text/javascript"></script>
+<script src="build/tanks/ui/configui.js" type="text/javascript"></script>
 <script src="build/tanks/thing/thing.js" type="text/javascript"></script>
 <script src="build/tanks/map/trajectory.js" type="text/javascript"></script>
 <script src="build/tanks/ui/grid.js" type="text/javascript"></script>
index 22478ad..42d05e1 120000 (symlink)
@@ -1 +1 @@
-img/favicon-pink.ico
\ No newline at end of file
+img/favicon-pink2.ico
\ No newline at end of file
diff --git a/www/panes.html b/www/panes.html
new file mode 100644 (file)
index 0000000..4b6f8d4
--- /dev/null
@@ -0,0 +1 @@
+<div id="panes"></div>
index 7c07c34..819ffa9 100644 (file)
@@ -1,5 +1,5 @@
 <div id="welcome" class="bigblue">
-    <div class="box">
+    <div class="box hud">
         <h1>The Littlest Battletank</h1>
         
         <h2>How To Play</h2>