/*
* File: formval.js
* Version: 12 (see Version History below)
* Author: Russell Robinson, http://www.tectite.com/
* Created: 5 October 2010
*
* Copyright (c) 2010-2015 tectite.com and Open Concepts (Vic) Pty Ltd
* (ABN 12 130 429 248), Melbourne, Australia.
* This script is free for all use as described in the "Copying and Use" and
* "Warranty and Disclaimer" sections below.
*
* Description
* ~~~~~~~~~~~
* This JavaScript script provides client-side field validation
* and can be used with any HTML form. Version 5+ supports AJAX form processing.
* In particular, it is utilized by forms created by the Tectite
* Form Designer, available at http://www.tectite.com/
* Visit us for support and updates.
* Copying and Use (Software License)
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* You can also read the full license details here:
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Warranty and Disclaimer
* ~~~~~~~~~~~~~~~~~~~~~~~
* This script is provided free-of-charge and with ABSOLUTELY NO WARRANTY.
* It has not been verified for use in critical applications, including,
* but not limited to, medicine, defense, aircraft, space exploration,
* or any other potentially dangerous activity.
*
* By using this file you agree to indemnify tectite.com and
* Open Concepts (Vic) Pty Ltd, their agents, employees, directors and
* associated companies and businesses from any liability whatsoever.
*
* We still care
* ~~~~~~~~~~~~~
* If you find a bug or fault in this file, please report it to us.
* We will respond to your report and make endeavours to rectify any
* faults you've detected as soon as possible.
*
* To contact us please register on our forums at:
* http://www.tectite.com/vbforums/
* or view our contact information:
* http://www.tectite.com/contacts.php
*
* Version History
* ~~~~~~~~~~~~~~~
*
* Version 12, 10 January 2015
* Reloads the reCaptcha image on successful submission. Adds support for reCaptcha version 2.
*
* Version 11, 24 November 2014
* Adds option "keep_mesg" to tectite_form_environ. If set to true, then the "Message Sent" and
* tick are kept indefinitely on completion of the form submission. They get cleared if the user
* puts focus on a field again.
*
* Version 10, 18 November 2014
* CORES submission is now async. Adds support for id="ABC_error" tags that provide
* a place to report errors for field ABC. This is particularly useful for
* Captcha error reports - just create a div tag with id="imgverify_error".
* Also, understands that imgverify errors relate to recaptcha_response_field.
* Captcha error reporting needs FormMail version 9.09 or later.
* Improves error reporting on fields that were not generated by the Tectite Form Designer.
*
* Version 9, 19 September 2014
* Rewrite to use jQuery throughout. No longer supports front-end validation: all
* validation is done via Ajax. Validation must be enabled with a "fmmode" hidden field.
*
* Version 8, 29 April 2014
* Supports fetching good_url on successful submission. Useful for
* "thank you" pages that use analytics code.
*
* Version 7, 23 July 2012
* Now supports both "form_environ" and "tectite_form_environ" global
* variables. This provides compatibility with non-plugin uses.
*
* Version 6, 16 July 2012
* Now includes Ajax submission logic when fmmode is set to "auto"
* or "ajax". Implements CORS or JSONP processing for cross-domain
* form submissions. Resets the form on successful submission
* (clears the fields).
*
* Version 5, 20 March 2012
* Refactored the code to allow use in FormMail Ajax forms.
*
* Version 4, 11 November 2010
* Added support for disabling the validation processing
* by passing a "formval=off" parameter in the URL.
*
* Version 3, 28 October 2010
* Fixed bugs in handling radio buttons.
* Fixed bug: was marking valid fields that were not invalid.
* Changed names of ltrim, rtrim, and trim functions added to String class.
* Added support for Multi-Page forms: the "back" button no longer triggers
* validation.
* Changed name of "form" property added to Element class.
* Debug level is now stored in a window property, along with a function to
* set it. Cancelling debug messages now restores debug messages after 2 seconds.
*
* Version 2, 8 October 2010
* Fixed a bug in finding DIV tags in some documents.
* Added better default message for select and checkbox field errors.
* The alert popup when there are field errors on form submit is now
* optional.
* The first field in error is given focus on submit failure.
* For simple required testing, the attached message is not
* used for field errors.
*
* Version 1, 7 October 2010
* Initial working version.
*/
/*
* Class: TectiteFormValidator
* Description:
* This is the form validation setup function.
*/
(function () {
var TectiteFormValidator;
var iMobileWidth = 320; // width for mobile phones
var bKeepMesg = false; // if true, keep the completion message displayed
var aTargetForms = [];
/*
* Thanks to Sam Deering for the following function.
*/
function loadScript(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
function TectiteFormValidator($) {
var validator = this;
var s_ajax_img_path = '';
/*
* The following messages are shown to the user in the circumstances
* indicated.
* You can modify or translate these messages as required.
*/
/*
* s_error_submit is shown in a popup alert when the user tries to
* submit the form and there are some errors.
* Set this to an empty string to skip the popup alert.
*/
var s_error_submit = "The form is not complete. Please fix the errors before submitting.";
/*
* s_error_field_empty is the default error message for a required field
* whose value is empty.
*/
var s_error_field_empty = "Please enter a value for this field.";
/*
* s_error_radio_reqd is the default error message for a required radio
* button field whose value is not checked.
*/
var s_error_radio_reqd = "Please select one of the options.";
/*
* s_error_select_reqd is the default error message for a required select
* field whose value is empty.
*/
var s_error_select_reqd = "Please select a value for this field.";
/*
* s_error_checkbox_reqd is the default error message for a required select
* field whose value is empty.
*/
var s_error_checkbox_reqd = "You must check this box.";
/*alert("FormVal.js");*/
/*
* Add some useful functions to the String object.
*/
/*
* Left-trim the string and return a copy.
*/
String.prototype.tectite_ltrim = function () {
return (this.replace(/^\s*/, ""));
};
/*
* Right-trim the string and return a copy.
*/
String.prototype.tectite_rtrim = function () {
return (this.replace(/\s*$/, ""));
};
/*
* Trim both ends of the string and return a copy.
*/
String.prototype.tectite_trim = function () {
return (this.tectite_ltrim().tectite_rtrim());
};
/*
* Test code for above functions.
*/
/***
var s_test = " \f\r\n\t hello world \f\r\n\t ";
alert("Trimming '" + s_test + "': '" + s_test.tectite_trim() + "'");
alert("Original is: '" + s_test + "'");
***/
/*
* This function parses ampersand-separated name=value argument pairs from
* the query string of the URL. It stores the name=value pairs in
* properties of an object and returns that object. Use it like this:
*
* var args = getArgs(); // Parse args from URL
* var q = args.q || ""; // Use argument, if defined, or a default value
* var n = args.n ? parseInt(args.n) : 10;
*
* Adapted from "JavaScript: The Definitive Guide" by David Flanagan.
*/
function GetArgs() {
var args = new Object();
var query = location.search.substring(1); // Get query string
var pairs = query.split("&"); // Break at ampersand
for (var i = 0; i < pairs.length; i++) {
var pos = pairs[i].indexOf('='); // Look for "name=value"
if (pos == -1) continue; // If not found, skip
var argname = pairs[i].substring(0, pos); // Extract the name
var value = pairs[i].substring(pos + 1); // Extract the value
value = decodeURIComponent(value); // Decode it, if needed
args[argname] = value; // Store as a property
}
return args; // Return the object
}
var nDebugLevel = 0;
/* current debug level */
var oURLArgs = GetArgs();
if (oURLArgs["formval"] && oURLArgs["formval"] == "off")
return;
/*
* On document load, run the Initialize function.
*/
$(document).ready(function () {
Initialize();
});
if (oURLArgs["debug"]) {
if (oURLArgs["debug"] === "off")
nDebugLevel = 0;
else if (!isNaN(parseInt(oURLArgs["debug"])))
nDebugLevel = parseInt(oURLArgs["debug"]);
if (nDebugLevel) {
window.tectite_debug_level = nDebugLevel;
window.tectite_setdebug = function () {
nDebugLevel = window.tectite_debug_level;
}
}
}
function logMesg(m_data) {
if (typeof console != 'undefined' && typeof console.log != 'undefined') {
console.log(m_data);
return true;
} else {
return false;
}
}
/*
* Function: Debug
* Parameters: i_level output level for this message
* rem remaining arguments are strings or values to output
* Returns: void
* Description:
* Produces debug output based on the current debug level and the
* output level specified.
*/
function Debug(i_level/*, variable arguments */) {
var s_mesg = "";
if (arguments.length > 1 && i_level <= nDebugLevel) {
for (var ii = 1; ii < arguments.length; ii++) {
s_mesg += arguments[ii];
}
if (!logMesg(s_mesg)) {
s_mesg += "\r\n\r\n" + "Click Cancel to switch off further debug messages for 2 seconds.";
if (!confirm(s_mesg)) {
nDebugLevel = 0;
window.setTimeout(window.tectite_setdebug, 2000);
}
}
}
}
/*
* Function: Initialize
* Parameters: void
* Returns: void
* Description:
* Initializes the form validation by scanning
* all fields in all forms in the document
* looking for fields that are listed in "required"
* or "conditions" fields.
*/
function Initialize() {
var a_reqd = [];
var a_cond = [];
var j_form_list = $('form');
Debug(5, "Initialize");
j_form_list.each(function () {
var j_form = $(this);
var j_mode = j_form.find('input[name=fmmode]');
Debug(5, 'Found form');
/*
* there must be a field called fmmode (usually hidden)
* with the value 'auto' or 'ajax'
*/
if (j_mode.length > 0 && (j_mode.val() == 'auto' || j_mode.val() == 'ajax')) {
Debug(5, 'Found form with fmmode = ' + j_mode.val());
//
// tell FormMail to use Ajax protocol
//
$(j_mode).val('ajax');
aTargetForms.push(j_form);
//
// grab the path for the images (passed in by Wordpress plugin, for example)
// we support both "tectite_form_environ" and "form_environ" (the latter is used
// in some old non-plugin cases).
// Only tectite_form_environ supports more fields.
//
s_ajax_img_path = '';
if (typeof tectite_form_environ !== 'undefined' &&
typeof tectite_form_environ.img_url !== 'undefined' &&
tectite_form_environ.img_url !== null) {
s_ajax_img_path = tectite_form_environ.img_url;
if (typeof tectite_form_environ.keep_mesg != "undefined") {
bKeepMesg = tectite_form_environ.keep_mesg;
}
} else if (typeof form_environ !== 'undefined' &&
typeof form_environ.img_url !== 'undefined' &&
form_environ.img_url !== null) {
s_ajax_img_path = form_environ.img_url;
}
recordBackClicks(j_form);
AjaxSetup(j_form, s_ajax_img_path);
//
// the submit button container needs to be full width so we can
// provide feedback during operation - adjust
//
j_form.find('input[type=submit]').parent('.TectiteFormDesignerField').css('width', '98%');
j_form.find('input[type=submit]').parents('form').submit({
j_form: j_form
}, function (ev) {
ev.preventDefault();
AjaxSubmit(ev.data.j_form);
return false;
});
} else {
Debug(5, 'Found form without active fmmode field: ' + j_mode.length);
}
});
}
/*
* Function: recordBackClicks
* Parameters: j_form the form
* Returns: void
* Description:
* Checks for a "multi_go_back" button and, if it exists,
* adds an onclick handler to all buttons so we can accurately
* record whether the user has clicked the back button.
*/
function recordBackClicks(j_form) {
var j_form = j_form;
var j_go_back = j_form.find('[name=multi_go_back]');
j_go_back.each(function () {
var j_button = $(this);
if (isPushButton(this)) {
j_button.click(function (ev) {
j_form.data('tectite_back_clicked', true);
});
}
});
}
function getLabel(j_elem) {
var s_id = j_elem.attr('id');
var j_label;
j_label = j_elem.closest('form').find('label[for="' + s_id + '"]');
if (j_label.length == 0) {
j_elem.parent('label');
}
return j_label;
}
function saveWidth(j_label) {
j_label.data('formval_old_width', j_label.css('width'));
j_label.css('width', 'auto');
}
function restoreWidth(j_label) {
var i_width = j_label.data('formval_old_width');
if (i_width !== null) {
j_label.css('width', i_width);
j_label.removeData('formval_old_width');
}
}
/*
* Function: addErrorMessage
* Parameters: j_elem the field element to add the message to
* s_mesg the error message to display
* Returns: void
* Description:
* Adds an error message to the document.
* This method looks for a parent or grandparent DIV with a CSS class
* of TectiteFormDesignerField, and utilizes that.
* If no such DIV is found, then it calls addMessage2Element or addMessageNearElement
* to display the error.
*/
function addErrorMessage(j_elem, s_mesg) {
var j_div = j_elem.closest('div.TectiteFormDesignerField');
Debug(5, 'addErrorMessage: ' + s_mesg);
if (j_div.length > 0) {
j_div.addClass('TectiteFieldError');
recordErrorClass(j_elem,j_div);
Debug(4, "addErrorMessage: adding class to div: '", j_div.attr('class'), "'");
recordErrorElems(j_elem, addErrorMesg2Div(j_div, s_mesg));
} else if ($(window).width() <= iMobileWidth) {
//
// For mobile devices, the label is likely to be above the field, not in line with it.
// For this case, add the error message to the label.
//
var j_label = getLabel(j_elem);
logMesg('label for id= "' + j_elem.attr('id') + '": ' + (j_label.length > 0 ? 'found' : 'not found'));
if (j_label.length) {
recordErrorElems(j_elem, addMessage2Element(j_label, s_mesg));
saveWidth(j_label);
}
else {
recordErrorElems(j_elem, addMessageNearElement(j_elem, s_mesg));
}
} else {
recordErrorElems(j_elem, addMessageNearElement(j_elem, s_mesg));
}
}
function recordErrorClass(j_elem, j_error_class) {
var m_list;
m_list = j_elem.data('formval_error_classed');
if (!m_list) {
m_list = [];
}
m_list.push(j_error_class);
Debug(6, 'recordErrorClass: element ' +j_elem.prop('tagName') + ' for ' + j_elem.attr('name'));
j_elem.data('formval_error_classed', m_list);
}
function recordErrorElems(j_elem, m_list) {
var m_list = [].concat(m_list);
Debug(6, 'Recording ' + m_list.length + ' error elements for ' + j_elem.attr('name'));
j_elem.data('formval_errors', m_list);
}
function removeErrorElems(j_elem) {
var m_list;
m_list = j_elem.data('formval_errors');
if (m_list) {
Debug(6, 'Removing ' + m_list.length + ' error elements for ' + j_elem.attr('name'));
$.each(m_list, function (i_index, j_added) {
j_added.remove();
});
j_elem.removeData('formval_errors');
}
m_list = j_elem.data('formval_error_classed');
if (m_list) {
Debug(6, 'Removing class from ' + m_list.length + ' elements for ' + j_elem.attr('name'));
$.each(m_list, function (i_index, j_el) {
j_el.removeClass('TectiteFieldError');
});
j_elem.removeData('formval_error_classed');
}
}
/*
* Function: addMessage2Element
* Parameters: j_elem the element to add the message to
* s_mesg the error message to display
* Returns: jQuery list of elements added
* Description:
* Adds an error message to the document near the given field element.
*/
function addMessage2Element(j_elem, s_mesg) {
/*
* create a span element with CSS class TectiteFieldError
* to contain the error message
*/
var s_span;
s_span = '' + s_mesg + '';
return $(s_span).appendTo(j_elem);
}
/*
* Function: addMessageNearElement
* Parameters: j_elem the element to add the message near
* s_mesg the error message to display
* Returns: jQuery list of elements added
* Description:
* Adds an error message to the document near the given field element.
*/
function addMessageNearElement(j_elem, s_mesg) {
/*
* create a span element with CSS class TectiteFieldError to contain the error message
*/
var j_span;
var a_added = [];
/*
* for radio buttons, add the message above it, along with a line break
*/
j_span = $('' + s_mesg + '');
if (isRadio(j_elem)) {
a_added.push(j_span.insertBefore(j_elem));
a_added.push($('
').insertBefore(j_elem));
} else {
a_added.push(j_span.insertAfter(j_elem));
}
return a_added;
}
/*
* Function: addErrorMesg2Div
* Parameters: j_div the div element to add the message to
* s_mesg the error message to display
* Returns: jQuery list of elements added
* Description:
* Adds an error message to the document inside the given DIV.
*/
function addErrorMesg2Div(j_div, s_mesg) {
/*
* create a P element with CSS class TectiteFieldError
* to contain the error message
*/
return $('
' + s_mesg + '
').appendTo(j_div); } /* * Function: removeErrorMessage * Parameters: elem the field element from which to remove the message * Returns: Node the node that was removed from the document * Description: * Removes an error message from the document near the given field element. */ function removeErrorMessage(elem) { var node = null; /* * The field element must have the document node stored. */ if (elem.tectite_error_mesg_node) { var o_node_info = elem.tectite_error_mesg_node; node = o_node_info.GetParent().removeChild(o_node_info.GetChild()); Debug(4, "removeErrorMessage: removed node"); elem.tectite_error_mesg_node = null; } var e_div; if ((e_div = FindParentDiv(elem, "TectiteFormDesignerField")) !== null) { e_div.className = e_div.className.replace(/\s*TectiteFieldError/, ""); Debug(4, "removeErrorMessage: removed class from div: '", e_div.className, "'"); } return (node); } /* * Function: isField * Parameters: j_elem an HTMLelement object * Returns: bool true if the element is a form field * Description: * Checks if an element is a form field. */ function isField(j_elem) { switch (j_elem.prop('tagName').toLowerCase()) { case "input": case "select": case "button": case "textarea": return (true); default: return (false); } } /* * Function: isRadio * Parameters: j_elem an HTMLelement object * Returns: bool true if the element is a radio button * Description: * Checks if an element is a radio button */ function isRadio(j_elem) { if (j_elem.prop('tagName').toLowerCase() === "input") return (j_elem.attr('type').toLowerCase() === "radio"); return (false); } /* * Function: isCheckbox * Parameters: j_elem an HTMLelement object * Returns: bool true if the element is a radio button * Description: * Checks if an element is a check box. */ function isCheckbox(j_elem) { if (j_elem.prop('tagName').toLowerCase() === "input") return (j_elem.attr('type').toLowerCase() === "checkbox"); return (false); } /* * Function: isSelect * Parameters: j_elem an HTMLelement object * Returns: bool true if the element is a select field * Description: * Checks if an element is a select field */ function isSelect(j_elem) { return (j_elem.prop('tagName').toLowerCase() === "select"); } /* * Function: isPushButton * Parameters: j_elem an HTMLelement object * Returns: bool true if the element is a push button * Description: * Checks if an element is a push button. */ function isPushButton(j_elem) { switch (j_elem.prop('tagName').toLowerCase()) { case "input": switch (j_elem.attr('type').toLowerCase()) { case "button": case "reset": case "submit": return (true); } return (false); case "button": return (true); default: return (false); } } /* * Function: isInputField * Parameters: j_elem an HTMLelement object * Returns: bool true if the element is a form field that accepts user input * Description: * Checks if an element is a form field that accepts user input (i.e. is not a hidden field). */ function isInputField(j_elem) { return (isField(j_elem) && j_elem.attr('type').toLowerCase() != "hidden"); } /* * Function: AjaxSetup * Parameters: m_form the form element * s_img_url URL to obtain images * Returns: void * Description: * Initializes Ajax for the form. */ function AjaxSetup(m_form, s_img_url) { var j_form = $(m_form); var mesgs = { loading_img: '', done_img: '', done_mesg: 'Message sent.', error_img: '', error_mesg: 'Error!' }; function AdjustImgTag(s_url, s_img) { return (s_img.replace(/src="/, 'src="' + s_url)); } /* * Preload images */ for (var s_fld in mesgs) { if (s_fld.indexOf('_img') != -1) $(AdjustImgTag(s_img_url, mesgs[s_fld])); } /* * Store HTML strings in the form. */ j_form.data('progress_loading', AdjustImgTag(s_img_url, mesgs.loading_img)); j_form.data('progress_done', AdjustImgTag(s_img_url, mesgs.done_img) + ' ' + mesgs.done_mesg); j_form.data('progress_error', AdjustImgTag(s_img_url, mesgs.error_img) + ' ' + mesgs.error_mesg); } /* * Function: AjaxSubmit * Parameters: j_form the form element * Returns: void * Description: * Submits the given form using Ajax. */ function AjaxSubmit(j_form) { // // if the back button was clicked, don't use Ajax to submit // if (j_form.data('tectite_back_clicked')) { return; } var s_data = j_form.serialize(); var j_progress = j_form.find('.TectiteAjaxProgress'); function setProgress(s_stage, s_mesg) { if (typeof s_mesg === 'undefined' || s_mesg === null) { s_mesg = ''; } if (s_stage === null) { j_progress.html(s_mesg); } else { j_progress.html(j_form.data('progress_' + s_stage) + s_mesg); } } function clearProgress() { j_progress.html(''); if (j_form.data('tprogress')) { clearTimeout(j_form.data('tprogress')); j_form.data('tprogress', null) } } /* * Function: isInvalid * Parameters: j_elem a field * Returns: bool true if the field is marked invalid * Description: * Checks the CSS class for a "TectiteFieldError". */ function isInvalid(j_elem) { return (j_elem.hasClass("TectiteFieldError")); } /* * Function: resetInvalid * Parameters: j_elem a field element * Returns: void * Description: * Resets a field that's been marked as in error. */ function resetInvalid(j_elem) { var j_label = getLabel(j_elem); restoreWidth(j_label); removeErrorElems(j_elem); j_elem.removeClass('TectiteFieldError'); } /* * Function: markInvalid * Parameters: j_elem a field element * s_mesg the message to display * Returns: void * Description: * Marks a field invalid by adding the TectiteFieldError CSS * class. */ function markInvalid(j_elem, s_mesg) { if (!isInvalid(j_elem)) { if (!s_mesg) { if (isRadio(j_elem)) s_mesg = s_error_radio_reqd; else if (isSelect(j_elem)) s_mesg = s_error_select_reqd; else if (isCheckbox(j_elem)) s_mesg = s_error_checkbox_reqd; else s_mesg = s_error_field_empty; } /* * for radio buttons, add the CSS class to all of them */ if (isRadio(j_elem)) { var s_name = j_elem.attr('name'); var j_buttons = j_elem.parents('form').find('input[type=radio][name="' + s_name + '"]'); j_buttons.addClass('TectiteFieldError'); addErrorMessage(j_buttons.first(), s_mesg); } else { j_elem.addClass('TectiteFieldError'); addErrorMessage(j_elem, s_mesg); } } Debug(2, "markInvalid: Field ", j_elem.attr('name'), " is now class '", j_elem.attr('class'), "'"); } function resetReCaptcha() { if (typeof grecaptcha !== 'undefined') { grecaptcha.reset(); } else { $('#recaptcha_reload').click(); } } function displayError(error_items) { for (var s_fld in error_items) { Debug(5, 'Error for field ' + s_fld); try { // // look for an _error element first, otherwise look for the field // var j_err_elem = j_form.find('#' + s_fld + '_error'); var j_field = j_form.find('[name=' + s_fld + ']'); function reloadRecaptcha(ev) { var j_elem = $(ev.target); resetReCaptcha(); j_elem.val(''); } if (j_field.length == 0 && s_fld === 'imgverify') { // // if there is no 'imgverify' field, look for 'recaptcha_response_field' // j_field = j_form.find('[name=recaptcha_response_field]'); j_field.focus(reloadRecaptcha); } if (j_err_elem.length == 0) { j_err_elem = j_field; } else { j_field.data('formval_error_elem',j_err_elem); } if (j_err_elem.length > 0) { markInvalid(j_err_elem, error_items[s_fld]); function resetElement(ev) { var j_field = $(ev.target); var j_err_elem = j_field.data('formval_error_elem'); var s_fld_name = j_field.attr('name'); if (!j_err_elem) { j_err_elem = j_field; } Debug(5, 'resetElement for field "' + s_fld_name + '"'); resetInvalid(j_err_elem); j_field.unbind('change', resetElement); j_field.unbind('blur', resetElement); j_field.unbind('focus', reloadRecaptcha); clearProgress(); }; j_field.change(resetElement); j_field.blur(resetElement); } } catch (exc) { } } } clearProgress(); setProgress('loading'); j_form.find('input[type=submit]').attr('disabled', true); var s_good_url = j_form.find('input[name=good_url]').val(); // // note we handle remote posting using CORS and then fallback to jsonp // var o_submit = { type: 'POST', url: j_form.attr('action'), cache: false, data: s_data, success: function (s_data, s_status, jqxhr) { var res; var i_clear_secs = 10; /* set for errors */ var o_goodurl = { type: 'GET', cache: false }; function complete(res) { j_form.data('tprogress', setTimeout(function () { if (!bKeepMesg) { clearProgress(); } else { function onFocus(ev) { var j_elem = $(ev.target); clearProgress(); j_elem.parents('form').find('input,select,radio,textarea').unbind('focus',onFocus); } $.each(aTargetForms,function (i_index,j_form) { j_form.find('input,select,radio,textarea').focus(onFocus); }); } if (res.Result == "OK") { j_form.get(0).reset(); resetReCaptcha(); } }, i_clear_secs * 1000)); j_form.find('input[type=submit]').attr('disabled', false); } try { if (typeof s_data == 'object') res = s_data; else res = $.parseJSON(s_data); } catch (exc) { res = { Result: 'FAILED', ErrorMesg: 'Server returned unexpected result (JSON parse failed)' }; } if (res.Result == "OK") { function all_done() { setProgress('done'); // j_form.find(' input[name=subject]').val(''); // j_form.find(' textarea[name=Message]').val(''); i_clear_secs = 2; /* shorten for success */ complete(res); } // // if "good_url" field exists and has a value, and "tectite_fetch_goodurl" is true // arrange to fetch it // if (s_good_url && typeof tectite_fetch_goodurl !== 'undefined' && tectite_fetch_goodurl) { o_goodurl.url = s_good_url; o_goodurl.success = all_done; $.ajax(o_goodurl); } else { all_done(); } } else { setProgress('error', ' ' + res.ErrorMesg); logMesg(res); if (res.ErrorItems) { displayError(res.ErrorItems); } complete(res); } } }; var clone = (function () { return function (obj) { Clone.prototype = obj; return new Clone() }; function Clone() { } }()); /* * Try CORS so we can do a POST, if that doesn't work try jsonp * (only GET method). * IE8 and IE9 don't work with CORS, so GET method is used. */ function TryJSONP(o_submit) { o_submit.dataType = 'jsonp'; o_submit.type = 'GET'; o_submit.async = true; // // if a second network error occurs after the TryCORS error (e.g. wrong // URL, the error function below won't be called.) // The workaround is the following timeout value (which is a good idea anyway for // a GET request). // o_submit.timeout = 10000; o_submit.error = function (jqxhr, s_status, m_error) { if (s_status == 'timeout') setProgress(null, 'Timeout....please try again later.'); else setProgress('error', ' A network error occurred: ' + s_status + ' ' + m_error); j_form.find('input[type=submit]').attr('disabled', false); }; $.ajax(o_submit); } function TryCORS(o_submit) { var orig_data = clone(o_submit); o_submit.dataType = 'json'; o_submit.type = 'POST'; o_submit.async = true; o_submit.error = function (jqxhr, s_status, m_error) { TryJSONP(orig_data); }; $.ajax(o_submit); } Debug(2,'Submitting the form via Ajax'); TryCORS(o_submit); } } /* * Load jQuery */ if (typeof jQuery == 'undefined') { loadScript("//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js", function () { TectiteFormValidator = new TectiteFormValidator(jQuery); }); } else { TectiteFormValidator = new TectiteFormValidator(jQuery); } })();