diff options
Diffstat (limited to 'framework/web/js/source/jquery.yiiactiveform.js')
| -rw-r--r-- | framework/web/js/source/jquery.yiiactiveform.js | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/framework/web/js/source/jquery.yiiactiveform.js b/framework/web/js/source/jquery.yiiactiveform.js new file mode 100644 index 0000000..63d320d --- /dev/null +++ b/framework/web/js/source/jquery.yiiactiveform.js @@ -0,0 +1,426 @@ +/** + * jQuery yiiactiveform plugin file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2010 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: jquery.yiiactiveform.js 3538 2012-01-14 18:24:30Z mdomba $ + * @since 1.1.1 + */ + +(function ($) { + /* + * returns the value of the CActiveForm input field + * performs additional checks to get proper values for checkbox / radiobutton / checkBoxList / radioButtonList + * @param o object the jQuery object of the input element + */ + var getAFValue = function (o) { + var type, + c = []; + if (!o.length) { + return undefined; + } + if (o[0].tagName.toLowerCase() === 'span') { + o.find(':checked').each(function () { + c.push(this.value); + }); + return c.join(','); + } + type = o.attr('type'); + if (type === 'checkbox' || type === 'radio') { + return o.filter(':checked').val(); + } else { + return o.val(); + } + }; + + /** + * yiiactiveform set function. + * @param options map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options. + */ + $.fn.yiiactiveform = function (options) { + return this.each(function () { + var settings = $.extend({}, $.fn.yiiactiveform.defaults, options || {}), + $form = $(this); + + if (settings.validationUrl === undefined) { + settings.validationUrl = $form.attr('action'); + } + $.each(settings.attributes, function (i) { + this.value = getAFValue($form.find('#' + this.inputID)); + settings.attributes[i] = $.extend({}, { + validationDelay: settings.validationDelay, + validateOnChange: settings.validateOnChange, + validateOnType: settings.validateOnType, + hideErrorMessage: settings.hideErrorMessage, + inputContainer: settings.inputContainer, + errorCssClass: settings.errorCssClass, + successCssClass: settings.successCssClass, + beforeValidateAttribute: settings.beforeValidateAttribute, + afterValidateAttribute: settings.afterValidateAttribute, + validatingCssClass: settings.validatingCssClass + }, this); + }); + $form.data('settings', settings); + + settings.submitting = false; // whether it is waiting for ajax submission result + var validate = function (attribute, forceValidate) { + if (forceValidate) { + attribute.status = 2; + } + $.each(settings.attributes, function () { + if (this.value !== getAFValue($form.find('#' + this.inputID))) { + this.status = 2; + forceValidate = true; + } + }); + if (!forceValidate) { + return; + } + + if (settings.timer !== undefined) { + clearTimeout(settings.timer); + } + settings.timer = setTimeout(function () { + if (settings.submitting || $form.is(':hidden')) { + return; + } + if (attribute.beforeValidateAttribute === undefined || attribute.beforeValidateAttribute($form, attribute)) { + $.each(settings.attributes, function () { + if (this.status === 2) { + this.status = 3; + $.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass); + } + }); + $.fn.yiiactiveform.validate($form, function (data) { + var hasError = false; + $.each(settings.attributes, function () { + if (this.status === 2 || this.status === 3) { + hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError; + } + }); + if (attribute.afterValidateAttribute !== undefined) { + attribute.afterValidateAttribute($form, attribute, data, hasError); + } + }); + } + }, attribute.validationDelay); + }; + + $.each(settings.attributes, function (i, attribute) { + if (this.validateOnChange) { + $form.find('#' + this.inputID).change(function () { + validate(attribute, false); + }).blur(function () { + if (attribute.status !== 2 && attribute.status !== 3) { + validate(attribute, !attribute.status); + } + }); + } + if (this.validateOnType) { + $form.find('#' + this.inputID).keyup(function () { + if (attribute.value !== getAFValue($(this))) { + validate(attribute, false); + } + }); + } + }); + + if (settings.validateOnSubmit) { + $form.find(':submit').live('mouseup keyup', function () { + $form.data('submitObject', $(this)); + }); + var validated = false; + $form.submit(function () { + if (validated) { + return true; + } + if (settings.timer !== undefined) { + clearTimeout(settings.timer); + } + settings.submitting = true; + if (settings.beforeValidate === undefined || settings.beforeValidate($form)) { + $.fn.yiiactiveform.validate($form, function (data) { + var hasError = false; + $.each(settings.attributes, function () { + hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError; + }); + $.fn.yiiactiveform.updateSummary($form, data); + if (settings.afterValidate === undefined || settings.afterValidate($form, data, hasError)) { + if (!hasError) { + validated = true; + var $button = $form.data('submitObject') || $form.find(':submit:first'); + // TODO: if the submission is caused by "change" event, it will not work + if ($button.length) { + $button.click(); + } else { // no submit button in the form + $form.submit(); + } + return; + } + } + settings.submitting = false; + }); + } else { + settings.submitting = false; + } + return false; + }); + } + + /* + * In case of reseting the form we need to reset error messages + * NOTE1: $form.reset - does not exist + * NOTE2: $form.live('reset', ...) does not work + */ + $form.bind('reset', function () { + /* + * because we bind directly to a form reset event, not to a reset button (that could or could not exist), + * when this function is executed form elements values have not been reset yet, + * because of that we use the setTimeout + */ + setTimeout(function () { + $.each(settings.attributes, function () { + this.status = 0; + var $error = $form.find('#' + this.errorID), + $container = $.fn.yiiactiveform.getInputContainer(this, $form); + + $container.removeClass( + this.validatingCssClass + ' ' + + this.errorCssClass + ' ' + + this.successCssClass + ); + + $error.html('').hide(); + + /* + * without the setTimeout() we would get here the current entered value before the reset instead of the reseted value + */ + this.value = getAFValue($form.find('#' + this.inputID)); + }); + /* + * If the form is submited (non ajax) with errors, labels and input gets the class 'error' + */ + $form.find('label, input').each(function () { + $(this).removeClass('error'); + }); + $('#' + settings.summaryID).hide().find('ul').html(''); + //.. set to initial focus on reset + if (settings.focus !== undefined && !window.location.hash) { + $form.find(settings.focus).focus(); + } + }, 1); + }); + + /* + * set to initial focus + */ + if (settings.focus !== undefined && !window.location.hash) { + $form.find(settings.focus).focus(); + } + }); + }; + + /** + * Returns the container element of the specified attribute. + * @param attribute object the configuration for a particular attribute. + * @param form the form jQuery object + * @return jquery the jquery representation of the container + */ + $.fn.yiiactiveform.getInputContainer = function (attribute, form) { + if (attribute.inputContainer === undefined) { + return form.find('#' + attribute.inputID).closest('div'); + } else { + return form.find(attribute.inputContainer).filter(':has("#' + attribute.inputID + '")'); + } + }; + + /** + * updates the error message and the input container for a particular attribute. + * @param attribute object the configuration for a particular attribute. + * @param messages array the json data obtained from the ajax validation request + * @param form the form jQuery object + * @return boolean whether there is a validation error for the specified attribute + */ + $.fn.yiiactiveform.updateInput = function (attribute, messages, form) { + attribute.status = 1; + var $error, $container, + hasError = false, + $el = form.find('#' + attribute.inputID); + if ($el.length) { + hasError = messages !== null && $.isArray(messages[attribute.id]) && messages[attribute.id].length > 0; + $error = form.find('#' + attribute.errorID); + $container = $.fn.yiiactiveform.getInputContainer(attribute, form); + + $container.removeClass( + attribute.validatingCssClass + ' ' + + attribute.errorCssClass + ' ' + + attribute.successCssClass + ); + + if (hasError) { + $error.html(messages[attribute.id][0]); + $container.addClass(attribute.errorCssClass); + } else if (attribute.enableAjaxValidation || attribute.clientValidation) { + $container.addClass(attribute.successCssClass); + } + if (!attribute.hideErrorMessage) { + $error.toggle(hasError); + } + + attribute.value = getAFValue($el); + } + return hasError; + }; + + /** + * updates the error summary, if any. + * @param form jquery the jquery representation of the form + * @param messages array the json data obtained from the ajax validation request + */ + $.fn.yiiactiveform.updateSummary = function (form, messages) { + var settings = $(form).data('settings'), + content = ''; + if (settings.summaryID === undefined) { + return; + } + if (messages) { + $.each(settings.attributes, function () { + if ($.isArray(messages[this.id])) { + $.each(messages[this.id], function (j, message) { + content = content + '<li>' + message + '</li>'; + }); + } + }); + } + $('#' + settings.summaryID).toggle(content !== '').find('ul').html(content); + }; + + /** + * Performs the ajax validation request. + * This method is invoked internally to trigger the ajax validation. + * @param form jquery the jquery representation of the form + * @param successCallback function the function to be invoked if the ajax request succeeds + * @param errorCallback function the function to be invoked if the ajax request fails + */ + $.fn.yiiactiveform.validate = function (form, successCallback, errorCallback) { + var $form = $(form), + settings = $form.data('settings'), + needAjaxValidation = false, + messages = {}; + $.each(settings.attributes, function () { + var value, + msg = []; + if (this.clientValidation !== undefined && (settings.submitting || this.status === 2 || this.status === 3)) { + value = getAFValue($form.find('#' + this.inputID)); + this.clientValidation(value, msg, this); + if (msg.length) { + messages[this.id] = msg; + } + } + if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status === 2 || this.status === 3)) { + needAjaxValidation = true; + } + }); + + if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) { + if (settings.submitting) { + // delay callback so that the form can be submitted without problem + setTimeout(function () { + successCallback(messages); + }, 200); + } else { + successCallback(messages); + } + return; + } + + var $button = $form.data('submitObject'), + extData = '&' + settings.ajaxVar + '=' + $form.attr('id'); + if ($button && $button.length) { + extData += '&' + $button.attr('name') + '=' + $button.attr('value'); + } + + $.ajax({ + url : settings.validationUrl, + type : $form.attr('method'), + data : $form.serialize() + extData, + dataType : 'json', + success : function (data) { + if (data !== null && typeof data === 'object') { + $.each(settings.attributes, function () { + if (!this.enableAjaxValidation) { + delete data[this.id]; + } + }); + successCallback($.extend({}, messages, data)); + } else { + successCallback(messages); + } + }, + error : function () { + if (errorCallback !== undefined) { + errorCallback(); + } + } + }); + }; + + /** + * Returns the configuration for the specified form. + * The configuration contains all needed information to perform ajax-based validation. + * @param form jquery the jquery representation of the form + * @return object the configuration for the specified form. + */ + $.fn.yiiactiveform.getSettings = function (form) { + return $(form).data('settings'); + }; + + $.fn.yiiactiveform.defaults = { + ajaxVar: 'ajax', + validationUrl: undefined, + validationDelay: 200, + validateOnSubmit : false, + validateOnChange : true, + validateOnType : false, + hideErrorMessage : false, + inputContainer : undefined, + errorCssClass : 'error', + successCssClass : 'success', + validatingCssClass : 'validating', + summaryID : undefined, + timer: undefined, + beforeValidateAttribute: undefined, // function (form, attribute) : boolean + afterValidateAttribute: undefined, // function (form, attribute, data, hasError) + beforeValidate: undefined, // function (form) : boolean + afterValidate: undefined, // function (form, data, hasError) : boolean + /** + * list of attributes to be validated. Each array element is of the following structure: + * { + * id : 'ModelClass_attribute', // the unique attribute ID + * model : 'ModelClass', // the model class name + * name : 'name', // attribute name + * inputID : 'input-tag-id', + * errorID : 'error-tag-id', + * value : undefined, + * status : 0, // 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating + * focus : undefined, // jquery selector that indicates which element to receive input focus initially + * validationDelay: 200, + * validateOnChange : true, + * validateOnType : false, + * hideErrorMessage : false, + * inputContainer : undefined, + * errorCssClass : 'error', + * successCssClass : 'success', + * validatingCssClass : 'validating', + * enableAjaxValidation : true, + * enableClientValidation : true, + * clientValidation : undefined, // function (value, messages, attribute) : client-side validation + * beforeValidateAttribute: undefined, // function (form, attribute) : boolean + * afterValidateAttribute: undefined, // function (form, attribute, data, hasError) + * } + */ + attributes : [] + }; +})(jQuery);
\ No newline at end of file |
