var closet = closet || {}; (function($) { var Maker = function() { this.options = []; }; Maker.Boolean = function(args) { $.extend(this, args); }; Maker.Boolean.prototype.build = function(ctx, html) { ctx.add(this, this.name, this.value); html.push(''); html.push(''); html.push(''); html.push(''); }; Maker.Boolean.prototype.coerce = function(value) { this.value = value === 'true' || value === '1' || value === true || value === 1; }; Maker.prototype.boolean = function(name, value, args) { args = args || {}; this.options.push(new Maker.Boolean({ type: 'boolean', name: name, label: args.label || name, value: value })); }; Maker.Choice = function(args) { $.extend(this, args); }; Maker.Choice.prototype.build = function(ctx, html) { var self = this; ctx.add(this, this.name, this.value); html.push(''); html.push('' + this.label + ''); html.push(''); }; Maker.Choice.prototype.coerce = function(value) { var self = this; $.each(this.values, function(_, v) { if (typeof v === 'string' && v === value) { self.value = value; } else if (typeof v === 'number' && v.toString() === value) { self.value = parseInt(value, 10); } }); }; Maker.prototype.choice = function(name, value, args) { args = args || {}; this.options.push(new Maker.Choice({ type: 'choice', name: name, label: args.label || name, value: value, values: args.values || [ value ] })); }; Maker.Group = function(args) { $.extend(this, args); }; Maker.Group.prototype.build = function(ctx, html) { html.push(''); html.push('' + this.label + ''); html.push(''); ctx.push(this.name); $.each(this.options, function(i, v) { v.build(ctx, html); }); ctx.pop(); }; // Intentionally no coerce function here, since group is not a leaf entity Maker.prototype.group = function(name, args, cb) { args = args || {}; var group = new Maker.Group({ type: 'group', name: name, label: args.label || name, options: [] }); this.options.push(group); var _opts = this.options; this.options = group.options; cb(maker); this.options = _opts; }; Maker.Number = function(args) { $.extend(this, args); }; Maker.Number.prototype.build = function(ctx, html) { ctx.add(this, this.name, this.value); html.push(''); html.push('' + this.label + ''); html.push(''); html.push(''); }; Maker.Number.prototype.coerce = function(value) { if (/^\d+$/.test(value)) { this.value = parseInt(value, 10); } }; Maker.prototype.number = function(name, value, args) { args = args || {}; this.options.push(new Maker.Number({ type: 'number', name: name, label: args.label || name, value: value, required: args.required || true, min: args.min, max: args.max })); }, Maker.Select = function(args) { $.extend(this, args); }; Maker.Select.prototype.build = function(ctx, html) { var self = this; ctx.push(this.name); $.each(this.values, function(i, v) { ctx.add(self, 'select', i); return false; }); html.push(''); html.push('' + this.label + ''); html.push(''); html.push(''); html.push(''); html.push(''); html.push(''); var count = 0; $.each(this.values, function(i, v) { html.push(''); ctx.push(i); $.each(v, function(i_, v_) { v_.build(ctx, html); }); ctx.pop(); html.push(''); }); ctx.pop(); html.push(''); }; Maker.Select.prototype.coerce = function(value) { var self = this; $.each(self.values, function(_, v) { if (v === value) { self.value = value; return false; } }); }; Maker.prototype.select = function(name, values, args) { var self = this; args = args || {}; var select = new Maker.Select({ type: 'select', name: name, label: args.label || name, values: {} }); this.options.push(select); $.each(values, function(key, value) { select.values[key] = []; if (value) { var _opts = self.options; self.options = select.values[key]; value(self); self.options = _opts; } }); }; Maker.String = function(args) { $.extend(this, args); }; Maker.String.prototype.build = function(ctx, element) { ctx.add(e, this.name, this.value); html.push(''); html.push('' + this.label + ''); html.push(''); html.push(''); }; Maker.String.prototype.coerce = function(value) { this.value = value; }; Maker.prototype.string = function(name, value, args) { args = args || {}; this.options.push(new Maker.String({ type: 'string', name: name, label: args.label || name, value: value, required: args.required || false })); }; Maker.Text = function(args) { $.extend(this, args); }; Maker.String.prototype.build = function(ctx, element) { ctx.add(this, this.name, this.value); html.push(''); html.push('' + this.label + ''); html.push(''); html.push(''); }; Maker.Text.prototype.coerce = function(value) { this.value = value; }; Maker.prototype.text = function(name, value, args) { args = args || {}; this.options.push(new Maker.Text({ type: 'text', name: name, label: args.label || name, value: value, rows: args.rows || 8, cols: args.cols || 64 })); }; Maker.prototype.build = function(kvs, changecb) { this.reconcile(kvs); var ctx = (function() { var nextId = 0; var map = {}; var opts = {}; var stack = [ opts ]; return { add: function(e, key, value) { e.id = nextId++; e.opts = stack[stack.length-1]; e.opts[key] = value; map[e.id.toString()] = e; }, push: function(name) { var _opts = stack[stack.length-1]; _opts[name] = {}; stack.push(_opts[name]); }, pop: function() { stack.pop(); }, opts: opts, map: map }; }()); var html = [ '' ]; html.push(''); $.each(this.options, function(_, e) { e.build(ctx, html); }); html.push(''); html.push('
'); return $(html.join('')). // opt_boolean find('input.boolean').click(function() { var id = $(this).attr('id'); var e = ctx.map[id]; e.opts[e.name] = $(this).is(':checked'); changecb(ctx.opts); }).end(). // opt_choice find('select.choice').change(function() { var id = $(this).attr('id'); var e = ctx.map[id]; e.opts[e.name] = $(this).val(); changecb(ctx.opts); }).end(). // opt_number find('input.number').change(function() { var id = $(this).attr('id'); var e = ctx.map[id]; var value = parseInt($(this).val(), 0); value = Math.max(e.min || value, value); value = Math.min(e.max || value, value); e.opts[e.name] = value; changecb(ctx.opts); }).end(). // opt_select find('select.select').change(function() { var id = $(this).attr('id'); var when = $(this).find('option:selected').val(); ctx.map[id].opts.select = when; $(this).parents('tbody').nextAll('tbody[select=' + id + ']'). css('display', 'none'). filter('#' + when + '.when'). css('display', 'table-row-group'); changecb(ctx.opts); }).end(). // opt_string, opt_text find('input.string,textarea.text').change(function() { var id = $(this).attr('id'); var e = ctx.map[id]; e.opts[e.name] = $(this).val(); changecb(ctx.opts); }).end(); }; Maker.prototype.reconcile = function(kvs) { kvs = kvs || {}; var _flatten = function(obj, prefix, opts) { prefix = (prefix && prefix.length > 0) ? (prefix + '.') : ''; $.each(opts, function(_, opt) { if (opt.type === 'group') { _keys(obj, prefix + opt.name, opts); } else { obj[prefix + opt.name] = opt; } }); return obj; }; var _options = _flatten({}, null, this.options); var coerced = {}; for (var key in _options) { if (_options.hasOwnProperty(key)) { if (kvs.hasOwnProperty(key)) { _options[key].coerce(kvs[key]); } coerced[key] = _options[key].value; } } return coerced; }; closet.options = (function(cb) { return { create: function(cb) { var maker = new Maker(); cb(maker); return maker; } }; }()); }(jQuery));