paulo@81: /*global $*/ paulo@81: (function happyJS($) { paulo@81: function trim(el) { paulo@81: return (''.trim) ? el.val().trim() : $.trim(el.val()); paulo@81: } paulo@81: $.fn.isHappy = function isHappy(config) { paulo@81: var fields = [], item; paulo@81: var pauseMessages = false; paulo@81: paulo@81: function isFunction(obj) { paulo@81: return !!(obj && obj.constructor && obj.call && obj.apply); paulo@81: } paulo@81: function defaultError(error) { //Default error template paulo@81: var msgErrorClass = config.classes && config.classes.message || 'unhappyMessage'; paulo@81: return $('' + error.message + ''); paulo@81: } paulo@81: function getError(error) { //Generate error html from either config or default paulo@81: if (isFunction(config.errorTemplate)) { paulo@81: return config.errorTemplate(error); paulo@81: } paulo@81: return defaultError(error); paulo@81: } paulo@81: function handleSubmit() { paulo@81: var i, l; paulo@81: var errors = false; paulo@81: for (i = 0, l = fields.length; i < l; i += 1) { paulo@81: if (!fields[i].testValid(true)) { paulo@81: errors = true; paulo@81: } paulo@81: } paulo@81: if (errors) { paulo@81: if (isFunction(config.unHappy)) config.unHappy(); paulo@81: return false; paulo@81: } else if (config.testMode) { paulo@81: if (isFunction(config.happy)) return config.happy(); paulo@81: if (window.console) console.warn('would have submitted'); paulo@81: return false; paulo@81: } paulo@81: if (isFunction(config.happy)) return config.happy(); paulo@81: } paulo@81: function handleMouseUp() { paulo@81: pauseMessages = false; paulo@81: } paulo@81: function handleMouseDown() { paulo@81: pauseMessages = true; paulo@81: $(window).bind('mouseup', handleMouseUp); paulo@81: } paulo@81: function processField(opts, selector) { paulo@81: var field = $(selector); paulo@81: var error = { paulo@81: message: opts.message || '', paulo@81: id: selector.slice(1) + '_unhappy' paulo@81: }; paulo@81: var errorEl = $(error.id).length > 0 ? $(error.id) : getError(error); paulo@81: var handleBlur = function handleBlur() { paulo@81: if (!pauseMessages) { paulo@81: field.testValid(); paulo@81: } else { paulo@81: $(window).bind('mouseup', field.testValid.bind(this)); paulo@81: } paulo@81: }; paulo@81: paulo@81: fields.push(field); paulo@81: field.testValid = function testValid(submit) { paulo@81: var val, gotFunc, temp; paulo@81: var el = $(this); paulo@81: var errorTarget = (opts.errorTarget && $(opts.errorTarget)) || el; paulo@81: var error = false; paulo@81: var required = !!el.get(0).attributes.getNamedItem('required') || opts.required; paulo@81: var password = (field.attr('type') === 'password'); paulo@81: var arg = isFunction(opts.arg) ? opts.arg() : opts.arg; paulo@81: var fieldErrorClass = config.classes && config.classes.field || 'unhappy'; paulo@81: paulo@81: // handle control groups (checkboxes, radio) paulo@81: if (el.length > 1) { paulo@81: val = []; paulo@81: el.each(function(i,obj) { paulo@81: val.push($(obj).val()); paulo@81: }); paulo@81: val = val.join(','); paulo@81: } else { paulo@81: // clean it or trim it paulo@81: if (isFunction(opts.clean)) { paulo@81: val = opts.clean(el.val()); paulo@81: } else if (!password && typeof opts.trim === 'undefined' || opts.trim) { paulo@81: val = trim(el); paulo@81: } else { paulo@81: val = el.val(); paulo@81: } paulo@81: paulo@81: // write it back to the field paulo@81: el.val(val); paulo@81: } paulo@81: paulo@81: // get the value paulo@81: gotFunc = ((val.length > 0 || required === 'sometimes') && isFunction(opts.test)); paulo@81: paulo@81: // check if we've got an error on our hands paulo@81: if (submit === true && required === true && val.length === 0) { paulo@81: error = true; paulo@81: } else if (gotFunc) { paulo@81: error = !opts.test(val, arg); paulo@81: } paulo@81: paulo@81: if (error) { paulo@81: errorTarget.addClass(fieldErrorClass).after(errorEl); paulo@81: return false; paulo@81: } else { paulo@81: temp = errorEl.get(0); paulo@81: // this is for zepto paulo@81: if (temp.parentNode) { paulo@81: temp.parentNode.removeChild(temp); paulo@81: } paulo@81: errorTarget.removeClass(fieldErrorClass); paulo@81: return true; paulo@81: } paulo@81: }; paulo@81: field.bind(opts.when || config.when || 'blur', handleBlur); paulo@81: } paulo@81: paulo@81: for (item in config.fields) { paulo@81: processField(config.fields[item], item); paulo@81: } paulo@81: paulo@81: $(config.submitButton || this).bind('mousedown', handleMouseDown); paulo@81: paulo@81: if (config.submitButton) { paulo@81: $(config.submitButton).click(handleSubmit); paulo@81: } else { paulo@81: this.bind('submit', handleSubmit); paulo@81: } paulo@81: return this; paulo@81: }; paulo@81: })(this.jQuery || this.Zepto);