Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Custom form error event handler to manipulate the bootstrap markup and show
18
 * nicely styled errors in an mform.
19
 *
20
 * @module     theme_boost/form-display-errors
21
 * @copyright  2016 Damyon Wiese <damyon@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
define(['jquery', 'core_form/events'], function($, FormEvent) {
25
    let focusedAlready = false;
26
    return {
27
        /**
28
         * Enhance the supplied element to handle form field errors.
29
         *
30
         * @method
31
         * @param {String} elementid
32
         * @listens event:formFieldValidationFailed
33
         */
34
        enhance: function(elementid) {
35
            var element = document.getElementById(elementid);
36
            if (!element) {
37
                // Some elements (e.g. static) don't have a form field.
38
                // Hence there is no validation. So, no setup required here.
39
                return;
40
            }
41
 
42
            element.addEventListener(FormEvent.eventTypes.formFieldValidationFailed, e => {
43
                const msg = e.detail.message;
44
                e.preventDefault();
45
 
46
                var parent = $(element).closest('.fitem');
47
                var feedback = parent.find('.form-control-feedback');
48
                const feedbackId = feedback.attr('id');
49
 
50
                // Get current aria-describedby value.
51
                let describedBy = $(element).attr('aria-describedby');
52
                if (typeof describedBy === "undefined") {
53
                    describedBy = '';
54
                }
55
                // Split aria-describedby attribute into an array of IDs if necessary.
56
                let describedByIds = [];
57
                if (describedBy.length) {
58
                    describedByIds = describedBy.split(" ");
59
                }
60
                // Find the the feedback container in the aria-describedby attribute.
61
                const feedbackIndex = describedByIds.indexOf(feedbackId);
62
 
63
                // Sometimes (atto) we have a hidden textarea backed by a real contenteditable div.
64
                if (($(element).prop("tagName") == 'TEXTAREA') && parent.find('[contenteditable]').length > 0) {
65
                    element = parent.find('[contenteditable]');
66
                }
67
                if (msg !== '') {
68
                    parent.addClass('has-danger');
69
                    parent.data('client-validation-error', true);
70
                    $(element).addClass('is-invalid');
71
                    // Append the feedback ID to the aria-describedby attribute if it doesn't exist yet.
72
                    if (feedbackIndex === -1) {
73
                        describedByIds.push(feedbackId);
74
                        $(element).attr('aria-describedby', describedByIds.join(" "));
75
                    }
76
                    $(element).attr('aria-invalid', true);
77
                    feedback.html(msg);
78
                    feedback.show();
79
 
80
                    // If we haven't focused anything yet, focus this one.
81
                    if (!focusedAlready) {
82
                        element.scrollIntoView({behavior: "smooth", block: "center"});
83
                        focusedAlready = true;
84
                        setTimeout(()=> {
85
                            // Actual focus happens later in case we need to do this in response to
86
                            // a change event which happens in the middle of changing focus.
87
                            element.focus({preventScroll: true});
88
                            // Let it focus again next time they submit the form.
89
                            focusedAlready = false;
90
                        }, 0);
91
                    }
92
 
93
                } else {
94
                    if (parent.data('client-validation-error') === true) {
95
                        parent.removeClass('has-danger');
96
                        parent.data('client-validation-error', false);
97
                        $(element).removeClass('is-invalid');
98
                        // If the aria-describedby attribute contains the error container's ID, remove it.
99
                        if (feedbackIndex > -1) {
100
                            describedByIds.splice(feedbackIndex, 1);
101
                        }
102
                        // Check the remaining element IDs in the aria-describedby attribute.
103
                        if (describedByIds.length) {
104
                            // If there's at least one, combine them with a blank space and update the aria-describedby attribute.
105
                            describedBy = describedByIds.join(" ");
106
                            // Put back the new describedby attribute.
107
                            $(element).attr('aria-describedby', describedBy);
108
                        } else {
109
                            // If there's none, remove the aria-describedby attribute.
110
                            $(element).removeAttr('aria-describedby');
111
                        }
112
                        $(element).attr('aria-invalid', false);
113
                        feedback.hide();
114
                    }
115
                }
116
            });
117
 
118
            var form = element.closest('form');
119
            if (form && !('boostFormErrorsEnhanced' in form.dataset)) {
120
                form.addEventListener('submit', function() {
121
                    var visibleError = $('.form-control-feedback:visible');
122
                    if (visibleError.length) {
123
                        visibleError[0].focus();
124
                    }
125
                });
126
                form.dataset.boostFormErrorsEnhanced = 1;
127
            }
128
        }
129
    };
130
});