// vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; /*@ Ox.Form Form Object () -> Form Object (options) -> Form Object (options, self) -> Form Object options Options object error error id id items [] submit on submit function self shared private variable @*/ Ox.Form = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ error: '', id: '', items: [], // FIXME: don't pass function, // listen to event instead! submit: null, validate: function(valid) { return Ox.every(valid); } }) .options(options || {}) // fixme: the || {} can be done once, in the options function .addClass('OxForm'); Ox.extend(self, { $items: [], $messages: [], itemIds: [], itemIsValid: [] }); self.options.items.forEach(function(item, i) { validateItem(i, function(valid) { self.itemIsValid[i] = valid; }); self.itemIds[i] = item.options('id') || item.id; self.$items[i] = Ox.FormItem({element: item}).appendTo(that); item.bindEvent({ /* blur: function(data) { validate(i, data.valid); if (data.valid) { self.$messages[i].html('').hide(); } else { self.$messages[i].html(data.message).show(); } }, */ autovalidate: function(data) { validateForm(i, data.valid); data.valid && self.$items[i].setMessage(''); }, /* // fixme: should't inputs also trigger a change event? blur: function(data) { that.triggerEvent('change', { id: self.itemIds[i], data: data }); }, */ change: function(data) { // fixme: shouldn't this be key/value instead of id/data? that.triggerEvent('change', { id: self.itemIds[i], data: data }); validateItem(i, function(valid) { validateForm(i, valid); }); }, submit: function(data) { self.formIsValid && that.submit(); }, validate: function(data) { validateForm(i, data.valid); // timeout needed for cases where the form is removed // from the DOM, triggering blur of an empty item - // in this case, we don't want the message to appear setTimeout(function() { self.$items[i].setMessage(data.valid ? '' : data.message); }, 0); } }); }); self.formIsValid = self.options.validate(self.itemIsValid); function getItemIndexById(id) { return self.itemIds.indexOf(id); } function submitCallback(data) { Ox.forEach(data, function(v, i) { self.$items[getItemIndexById(v.id)].setMessage(v.message); }); } function validateForm(pos, valid) { self.itemIsValid[pos] = valid; if (self.options.validate(self.itemIsValid) != self.formIsValid) { self.formIsValid = !self.formIsValid; that.triggerEvent('validate', { valid: self.formIsValid }); } } function validateItem(pos, callback) { var item = self.options.items[pos], validate = item.options('validate'); if (validate) { validate(item.value(), function(data) { callback(data.valid); }); } else { callback(item.value && !Ox.isEmpty(item.value)); } } that.addItem = function(pos, item) { Ox.Log('Form', 'addItem', pos) self.options.items.splice(pos, 0, item); self.$items.splice(pos, 0, Ox.FormItem({element: item})); pos == 0 ? self.$items[pos].insertBefore(self.$items[0]) : self.$items[pos].insertAfter(self.$items[pos - 1]); } that.removeItem = function(pos) { Ox.Log('Form', 'removeItem', pos); self.$items[pos].remove(); self.options.items.splice(pos, 1); self.$items.splice(pos, 1); } that.submit = function() { // fixme: this seems to be unneeded //Ox.Log('Form', '---- that.values()', that.values()) self.options.submit(that.values(), submitCallback); }; that.valid = function() { return self.formIsValid; }; that.values = function() { // fixme: can this be private? /* get/set form values call without arguments to get current form values pass values as array to set values (not implemented) */ var values = {}; if (arguments.length == 0) { self.$items.forEach(function($item, i) { //Ox.print('??????-??', self.itemIds[i], self.$items[i].value(), Ox.typeOf(self.$items[i].value())) values[self.itemIds[i]] = self.$items[i].value(); }); //Ox.Log('Form', 'VALUES', values) return values; } else { Ox.Log('Form', 'SET FORM VALUES', arguments[0]) Ox.forEach(arguments[0], function(value, key) { var index = getItemIndexById(key); index > -1 && Ox.Log('Form', ':::::::', key, value) index > -1 && self.options.items[index].value(value); }); return that; } }; return that; };