summaryrefslogtreecommitdiff
path: root/framework/web/js/source/jquery.yiiactiveform.js
diff options
context:
space:
mode:
Diffstat (limited to 'framework/web/js/source/jquery.yiiactiveform.js')
-rw-r--r--framework/web/js/source/jquery.yiiactiveform.js426
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 &copy; 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