<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<%@taglib prefix="s" uri="/struts-tags" %>

<html>
<head>
    <title>Struts2 Showcase - Validation - AJAX Form Submit</title>
    <s:head theme="xhtml"/>

    <style type="text/css">
        /* see comment of script element below! */
        .ajaxVisualFeedback {
            width: 16px;
            height: 16px;
            background-image: url('../images/indicator.gif');
            background-repeat: no-repeat;
            float: right;
        }
    </style>

</head>
<body>

<div class="page-header">
    <h1>AJAX Form Submit</h1>
</div>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-12">

            <!-- START SNIPPET: ajaxFormSubmit -->

            <h3>Action Errors Will Appear Here</h3>
            <s:actionerror theme="ajaxErrorContainers"/>

            <hr/>

            <s:form method="POST" theme="xhtml">
                <s:textfield label="Required Validator Field" name="requiredValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="Required String Validator Field" name="requiredStringValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="Integer Validator Field" name="integerValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="Date Validator Field" name="dateValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="Email Validator Field" name="emailValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="URL Validator Field" name="urlValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="String Length Validator Field" name="stringLengthValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="Regex Validator Field" name="regexValidatorField" theme="ajaxErrorContainers"/>
                <s:textfield label="Field Expression Validator Field" name="fieldExpressionValidatorField" theme="ajaxErrorContainers"/>
                <s:submit label="Submit" cssClass="btn btn-primary"/>
            </s:form>

            <!-- END SNIPPET: ajaxFormSubmit -->
        </div>
    </div>
</div>

<s:script type="text/javascript">
/********************************************************************
 * JS just used on this page.
 * Usually this would be placed in a JS file
 * but as this showcase app is already hard to follow
 * I place it here so it is easier to find.
 *
 * note: this requires jQuery.
 *******************************************************************/

 /**
  * Validates form per AJAX. To be called as onSubmit handler.
  *
  * @param event onSubmit event
  */
function ajaxFormValidation(event) {
    event.preventDefault();
    _removeValidationErrors();

    var _form = $(event.target);
    var _formData = _form.serialize(true);

    // prepare visual feedback
    // you may want to use other elements here
    var originalButton = _form.find('.btn-primary');
    // note: jQuery returns an array-like object
    if (originalButton && originalButton.length && originalButton.length > 0) {
        originalButton.hide();
        var feedbackElement = $('<div class="ajaxVisualFeedback"></div>').insertAfter(originalButton);
        var restoreFunction = function() {
            originalButton.show();
            feedbackElement.remove();
        }
    }


    var options = {
        data: 'struts.enableJSONValidation=true&struts.validateOnly=false&' + _formData,
        async: true,
        processData: false,
        type: 'POST',
        success: function (response, statusText, xhr) {
            if (response.location) {
                // no validation errors
                // action has been executed and sent a redirect URL wrapped as JSON
                // cannot use a normal http-redirect (status-code 3xx) as this would be followed by browsers and would not be available here

                // follow JSON-redirect
                window.location.href = response.location;
            } else {
                if (restoreFunction) {
                    restoreFunction();
                }
                _handleValidationResult(_form, response);
            }
        },
        error: function(xhr, textStatus, errorThrown) {
            if (restoreFunction) {
                restoreFunction();
            }
            // struts sends status code 400 when validation errors are present
            if (xhr.status === 400) {
                _handleValidationResult(_form, JSON.parse(xhr.responseText))
            } else {
                // a real error occurred -> show user an error message
                _handleValidationResult(_form, {errors: ['Network or server error!']})
            }
        }
    }

    // send request, after delay to make sure everybody notices the visual feedback :)
    window.setTimeout(function() {
        var url = _form[0].action;
        jQuery.ajax(url, options);
    }, 1000);
}

/**
 * Removes validation errors from HTML DOM.
 */
function _removeValidationErrors() {
    // action errors
    // you might want to use a custom ID here
    $('ul.errorMessage li').remove();

    // field errors
    $('div.errorMessage').remove();
}

/**
 * Incorporates validation errors in HTML DOM.
 *
 * @param form Form containing errors.
 * @param errors Errors from server.
 */
function _handleValidationResult(form, errors) {
    // action errors
    if (errors.errors) {
        // you might want to use a custom ID here
        var errorContainer = $('ul.errorMessage');
        $.each(errors.errors, function(index, errorMsg) {
            var li = $('<li><span></span></li>');
            li.text(errorMsg); // use text() for security reasons
            errorContainer.append(li);
        });
    }

    // field errors
    if (errors.fieldErrors) {
        $.each(errors.fieldErrors, function(fieldName, errorMsg) {
            var td = $('td[data-error-for-fieldname="' + fieldName + '"]');
            if (td) {
                var div = $('<div class="errorMessage"></div>');
                div.text(errorMsg); // use text() for security reasons
                td.append(div);
            }
        });
    }
}

// register onSubmit handler
$(window).bind('load', function() {
    $('form').bind('submit', ajaxFormValidation);
});
</s:script>
</body>
</html>
