/** * Advanced form handling. * * The `.uiForm()` method add advanced form handling, like ajax or validate, to a form. * * Default handling: * * + prevent submit if no action attribute givn * + prevent submit if an element of the form invalid * * ### Examples * Apply .uiForm() to all forms and set an ajax handle for submit. * * JavaScript: * * ep('form') * .uiForm({ * ajax: { * success: function( successText ){ * alert( successText ); * }, * error: function( errorText ){ * alert( errorText ); * } * } * }); * * * @class jQuery.ui.uiForm * * @uses ep * @uses ep.ajax * @uses jQuery.fn.form * @uses jQuery.ui.widget * @since 6.11.0 */ /** * Set the validity state of the form to `true` (valid) or `false` (invalid). * * @param {Boolean} [valid] A boolean indication whether to set the valid state of the form. * * @method setValid * @member jQuery.ui.uiForm * * @since 6.11.0 */ /** * @cfg {Boolean} compareDiff A boolean indication wether to submit changed form only. */ /** * @cfg {Boolean} showSaveWarn A boolean indication wether to show a warning when leaving a page without saving. */ /** * @cfg {Object} ajax A map of ajax options to send form via ajax. If no url givn the action attribute will be used as url, same works for type/method. */ /** * See `jQuery.ui.uiForm` for details. * * @param {Object} [options] A map of additional options pass to the method. * @param {Boolean} compareDiff A boolean indication wether to submit changed form only. * @param {Boolean} showSaveWarn A boolean indication wether to show a warning when leaving a page without saving. * @param {Object} ajax A map of ajax options to send form via ajax. If no url givn the action attribute will be used as url, same works for type/method. * * @method uiForm * @member jQuery * * @since 6.11.0 */ /* * @copyright © Copyright 2006-2012, epages GmbH, All Rights Reserved. * * @module ep.ui.form * * @revision $Revision: 1.20 $ */ define("ep/ui/form", [ "jquery", "ep", "$dict!ep/dict", "jquery/ui/widget", "jquery/cookie", "jquery/fn/form", "ep/ajax" ], function ($, ep, epDict) { /* * * @dictionary ep/dict * * @translation {LoseChanges} * */ var // jQuery window object win = $(window), tLoseChanges = epDict.translate("LoseChanges"), // Hash serializer serializeArray = function (hash) { var arr = []; $.each(hash, function (name, value) { arr.push({ name: name, value: value }); }); return arr; }; $.widget('ui.uiForm', { options: { showSaveWarn: false, tokenName: "SecToken" }, _create: function () { var self = this, o = self.options, cookies = $.cookie(); self.elem = ep(self.element) .on('change.uiForm', $.proxy(self, '_change')) .on('submit.uiForm', $.proxy(self, '_submit')) .on('validate.uiForm', $.proxy(self, '_validate')) .on('reset.uiForm', $.proxy(self, '_reset')) .on('mousedown', ':submit', $.proxy(self, '_submitButtonFix')); // Add security token if (cookies[o.tokenName] && self.elem.find("input[name='" + o.tokenName + "']").length === 0) { self.elem.append('<input type="hidden" name="' + o.tokenName + '" value="' + cookies[o.tokenName] + '" />'); } self.timeStamp = $.now(); //find all saveButtons and change label and class if page was reloaded after save self.setSaveButtonSaved(); }, // Validate all input elemenst of form _validate: function (event) { var self = this; if (!self.elem.attr('formnovalidate') && event.target === self.elem[0]) { var elems = self.elem .formInput(); elems.trigger('validate') .filter(':invalid:first') .focus() .trigger('validate'); self._change(); } }, // Public get valid state /** * Get a boolean indication wether the elements of the form are valid. * * @method isValid * @member jQuery.ui.uiForm * * @since 6.11.0 */ isValid: function () { return this.elem.attr('formnovalidate') ? true : !this.elem[0].formInvalid; }, // Public set valid state by boolean value setValid: function (stat) { this.elem[0].formInvalid = !stat; }, // Set valid state by checking input elements _setValid: function (elems) { this.setValid(!elems.filter(':invalid').length); }, // Check changed state by input elements _isChanged: function (elems) { return !!elems .filter(':changed') .length; }, _submitButtonFix: function (event) { var self = this, context = self._submitButtonFix; clearTimeout(context.timeout); // Reset if (event === true && context.input) { context.input.remove(); } // Set to remember else if (event) { context.button = $(event.target); } // Fix button context by hidden input else if (!event && context.button) { context.input = $("<input type=\"hidden\">") .attr({ name: context.button.attr("name"), value: context.button.attr("value") }) .appendTo(self.elem); // Remove the hidden button fix element with a short delay context.timeout = setTimeout(function () { self._submitButtonFix(true); }, 300); } }, // Handle submit check and AJAX submits _submitCheck: function (event) { var self = this, o = self.options, valid = self.isValid(), action = self.elem.attr('action'), ajax = self.options.ajax, isPrevented; // Prevent default if invalid, double submit or form without action if ((!action && !ajax) || !valid || (event.timeStamp - self.timeStamp) < 300) { event.preventDefault(); } // Reset reference time for double call prevention self.timeStamp = event.timeStamp; // Detect prevented state to handle submit done / fail isPrevented = event.isDefaultPrevented(); if (!isPrevented) { // Remove warning before page leave if (o.showSaveWarn) { win.off('beforeunload.uiForm', self._beforeunload); } // Submit form via AJAX if (ajax) { // Prevent default without handle submitFail event.preventDefault(); // AJAX send handler var send = function (settings) { var // Local for data data = settings.data || [], // AJAX default settings defaults = { // Use action attrigute as url url: action || '', // Use method attribute as type type: self.elem.attr('method') || 'post', // Add form input data data: self.elem.serializeArray() || [] }; // Remove original data (use serialized) if (settings.data) { delete settings.data; } // Serialize for mergable data array if (!$.isArray(data)) { data = serializeArray(data); } // Build AJAX settings $.extend(true, defaults, settings); // Merge data arrays $.merge(defaults.data, data); // Return jqXHR promise return ep.ajax(defaults) .done(function () { self.elem.trigger('submitDone'); }) .fail(function () { self.elem.trigger('submitFail'); }); }; if ($.isFunction(ajax)) { ajax(send); } else { send(ajax); } } } // Fix submit buttons values self._submitButtonFix(); // Trigger done / fail (if not AJAX) if (!ajax) { self.elem.trigger(isPrevented ? 'submitFail' : 'submitDone'); } // Remove check handler self.elem.off('submit.uiForm', $.proxy(self, '_submitCheck')); }, // Submit event pre controller _submit: function (event) { var self = this, o = self.options; // Control last event handle of submit call if (!event.uiFormTriggert) { // This way other *submit* event handlers can expect the validation to be done. self.elem.trigger('validate'); // Actually set *formInvalid* attribute on *element*. self._setValid(self.elem.formInput()); // Stop event event.stopImmediatePropagation(); event.preventDefault(); // Save autocomplete for IE if (window.external && typeof window.external.AutoCompleteSaveForm === 'function') { window.external.AutoCompleteSaveForm(self.elem[0]); } // Add sumbit check as last event handle and trigger submit again self.elem .on('submit.uiForm', $.proxy(self, '_submitCheck')) .trigger({ type: 'submit', uiFormTriggert: true }); } }, // Reset form inputs _reset: function (event) { this.elem .formInput() .each(function () { $(this).triggerHandler('reset'); }); }, // Handle leave warning before leaving the page _change: function () { var self = this; //reset button state and text if form was changed var buttons = self.elem. formInput() .filter('.Saved'); if (ep.config.saved) { buttons.each(function (i) { var node = $(this), originalText = node.data('originalText'); if (node.hasClass('Saved') && originalText) { node .addClass('SaveButton') .removeClass('Saved') .text(node.data('originalText')); } }); } if (self.options.showSaveWarn) { if (self._isChanged(self.elem.formInput())) { win.on('beforeunload.uiForm', $.proxy(self._beforeunload, self)); } else { win.off('beforeunload.uiForm', $.proxy(self._beforeunload, self)); } } }, // Just the before page leave event callback _beforeunload: function () { return this.options.showSaveWarn ? tLoseChanges : undefined; }, // Public set SaveButton class to Saved setSaveButtonSaved: function () { var self = this, buttons = self.elem. formInput() .filter('.SaveButton'); if (ep.config.saved) { buttons.each(function (i) { var node = $(this), originalText = node.text(); node .addClass('Saved') .removeClass('SaveButton') .data('originalText', originalText); if (ep.config.saveButtonChangeMap && ep.config.saveButtonChangeMap[originalText]) { node.text(ep.config.saveButtonChangeMap[originalText]); } }); } } }); return ep; });