PageSourceSearch

https://www.knf.org.za/default/theme/addons/uniform/js/uni-form-validation.jquery.js

js knf.org.za collected 2026-10-04 13:24:06 UTC 23,737 bytes, 711 lines download raw bytes

1/**
2 * Uni-Form jQuery Plugin with Validation
3 *
4 * Provides form actions for use with the Uni-Form markup style
5 * This version adds additional support for client side validation
6 *
7 * Author: Ilija Studen for the purposes of Uni-Form
8 * 
9 * Modified by Aris Karageorgos to use the parents function
10 * 
11 * Modified by Toni Karlheinz to support input fields' text
12 * coloring and removal of their initial values on focus
13 * 
14 * Modified by Jason Brumwell for optimization, addition
15 * of valid and invalid states and default data attribues
16 * 
17 * Modified by LearningStation to add support for client
18 * side validation routines. The validation routines based on an
19 * open source library of unknown authorship.
20 *
21 * @see http://sprawsm.com/uni-form/
22 * @license MIT http://www.opensource.org/licenses/mit-license.php
23 */
24jQuery.fn.uniform = function(extended_settings) {
25    
26    /**
27     * Self reference for closures
28     *
29     * @var object
30     */
31    var self = this;
32    
33    /**
34     * Object extending the defaults object
35     *
36     * @var object
37     */
38    var settings = jQuery.extend(
39        jQuery.fn.uniform.defaults,
40        extended_settings
41    );
42    
43    /**
44     * Language abstration string
45     * 
46     * to extend, use a script tag to include a file from the localization folder
47     *
48     * @var object
49     */
50    var i18n_strings = jQuery.fn.uniform.language;
51
52    /**
53     * Supported validators
54     *
55     * @var Object
56     */
57    this.validators = {
58    
59        /**
60         * Get the value for a validator that takes parameters
61         *
62         * @param string name
63         * @param string all classes on element
64         *
65         * @return mixed || null
66         */
67        get_val : function(name, classes, default_value) {
68            var value = default_value;
69            classes = classes.split(' ');
70            for(var i = 0; i < classes.length; i++) {
71                if(classes[i] == name) {
72                    if((classes[i + 1] != 'undefined') && ('val-' === classes[i + 1].substr(0,4))) {
73                        value = parseInt(classes[i + 1].substr(4),10);
74                        return value;
75                    }
76                }
77            }
78            return value;
79        },
80        
81        /**
82         * Value of field is not empty, whitespace will be counted as empty
83         *
84         * @param jQuery field
85         * @param string caption
86         */
87        required : function(field, caption) {
88            if(field.is(':radio')) {
89                var name = field.attr('name');
90                if($("input[name=" + name + "]:checked").length) {
91                    return true;
92                }
93                return i18n('req_radio', caption);
94            }
95            if(field.is(':checkbox')) {
96                var name = field.attr('name');
97                if(field.is(":checked")) {
98                    return true;
99                }
100                return i18n('req_checkbox', caption);
101            }
102            if(jQuery.trim(field.val()) == '') {
103                return i18n('required', caption);
104            }
105            return true;
106        },
107    
108        /**
109         * Value is shorter than allowed
110         *
111         * @param jQuery field
112         * @param sting caption
113         */
114        validateMinLength : function(field, caption) {
115            var min_length = this.get_val('validateMinLength', field.attr('class'), 0);
116
117            if((min_length > 0) && (field.val().length < min_length)) {
118                return i18n('minlength', caption, min_length);
119            }
120            return true;
121        },
122        
123        /**
124         * Value is less than min
125         *
126         * @param jQuery field
127         * @param sting caption
128         */
129        validateMin : function(field, caption) {
130            var min_val = this.get_val('validateMin', field.attr('class'), 0);
131      
132            if((parseInt(field.val(),10) < min_val)) {
133                return i18n('min', caption, min_val);
134            }
135            return true;
136        },
137    
138        /**
139         * Value is longer than allowed
140         *
141         * @param jQuery field
142         * @param string caption
143         */
144        validateMaxLength : function(field, caption) {
145            var max_length = this.get_val('validateMaxLength', field.attr('class'), 0);
146      
147            if((max_length > 0) && (field.val().length > max_length)) {
148                return i18n('maxlength', caption, max_length);
149            }
150            return true;
151        },
152        
153        /**
154         * Value is greater than max
155         *
156         * @param jQuery field
157         * @param sting caption
158         */
159        validateMax : function(field, caption) {
160            var max_val = this.get_val('validateMax', field.attr('class'), 0);
161    
162            if((parseInt(field.val(),10) > max_val)) {
163                return i18n('max', caption, max_val);
164            }
165            return true;
166        },
167    
168        /**
169         * Element has same value as that of the target Element
170         *
171         * This does not use the val-{name} format, and instead
172         * is only the name of the element
173         *
174         * class="validateSameAs field_id"
175         *
176         * @param jQuery field
177         * @param string caption
178         */
179        validateSameAs : function(field, caption) {
180            var classes = field.attr('class').split(' ');
181            var target_field_name = '';
182      
183            for(var i = 0; i < classes.length; i++) {
184                if(classes[i] == 'validateSameAs') {
185                    if(classes[i + 1] != 'undefined') {
186                        target_field_name = classes[i + 1];
187                        break;
188                    }
189                }
190            }
191            
192            if(target_field_name) {
193                var target_field = field.closest('form').find('input[name="' + target_field_name + '"]');
194                if(target_field.length > 0) {
195                    if(target_field.val() != field.val()) {
196                        var target_field_caption = target_field.closest('div.'+settings.holder_class).find('label').text().replace('*','');
197                        return i18n('same_as', caption, target_field_caption);
198                    }
199                }
200            }
201      
202            return true;
203        },
204    
205        /**
206         * Valid email address
207         *
208         * @param jQuery field
209         * @param string caption
210         */
211        validateEmail : function(field, caption) {
212            if(field.val().match(/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/)) {
213                return true;
214            } else {
215                return i18n('email', caption);
216            }
217        },
218    
219        /**
220         * Valid URL (http://,https://,ftp://)
221         *
222         * @param jQuery field
223         * @param string caption
224         */
225        validateUrl : function(field, caption) {
226            if(field.val().match(/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i)) {
227                return true;
228            }
229            return i18n('url', caption);
230        },
231    
232        /**
233         * Number is only valid value (integers and floats)
234         *
235         * @param jQuery field
236         * @param string caption
237         */
238        validateNumber : function(field, caption) {
239            if(field.val().match(/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/) || field.val() == '') {
240                return true;
241            }
242            return i18n('number', caption);
243        },
244    
245        /**
246         * Whole numbers are allowed
247         *
248         * @param jQuery field
249         * @param string caption
250         */
251        validateInteger : function(field, caption) {
252            if(field.val().match(/(^-?\d\d*$)/) || field.val() == '') {
253                return true;
254            }
255            return i18n('integer', caption);
256        },
257    
258        /**
259         * Letters only
260         *
261         * @param jQuery field
262         * @param string caption
263         */
264        validateAlpha : function(field, caption) {
265            if(field.val().match(/^[a-zA-Z]+$/)) {
266                return true;
267            }
268            return i18n('alpha', caption);
269        },
270    
271        /**
272         * Letters and numbers
273         *
274         * @param jQuery field
275         * @param string caption
276         */
277        validateAlphaNum : function(field, caption) {
278            if(field.val().match(/\W/)) {
279                return i18n('alphanum', caption);
280            }
281            return true;
282        },
283    
284        /**
285         * Simple phrases
286         *
287         * @param jQuery field
288         * @param string caption
289         */
290        validatePhrase : function(field, caption) {
291            if((field.val() == '') || field.val().match(/^[\w\d\.\-_\(\)\*'# :,]+$/i)) {
292                return true;
293            }
294            return i18n('phrase', caption);
295        },
296        
297        /**
298         * Phone number
299         *
300         * @param jQuery field
301         * @param string caption
302         */
303        validatePhone : function(field, caption) {
304            phoneNumber = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
305            if(phoneNumber.test(field.val())) {
306                return true;
307            }
308            return i18n('phone', caption);
309        },
310        
311        /**
312         * Date in MM/DD/YYYY format
313         *
314         * @param jQuery field
315         * @param string caption
316         */
317        validateDate : function(field, caption) {
318            if(field.val().match('(1[0-9]|[1-9])/([1-3][0-9]|[1-9])/((19|20)[0-9][0-9]|[0-9][0-9])')) {
319                return true;
320            }
321            return i18n('date', caption);
322        },
323    
324        /**
325         * Callback validator
326         *
327         * Lets you define your own validators. Usage:
328         *
329         * <input name="myinput" class="validateCallback my_callback" />
330         *
331         * This will result in UniForm searching for window.my_callback funciton and
332         * executing it with field and caption arguments. Sample implementation:
333         *
334         * window.my_callback = function(field, caption) {
335         *   if(field.val() == '34') {
336         *     return true;
337         *   } else {
338         *     return caption + ' value should be "34"';
339         *   }
340         * }
341         *
342         * @param jQuery field
343         * @param caption
344         */
345        validateCallback : function(field, caption) {
346            var classes = field.attr('class').split(' ');
347            var callback_function = '';
348      
349            for(var i = 0; i < classes.length; i++) {
350                if(classes[i] == 'validateCallback') {
351                    if(classes[i + 1] != 'undefined') {
352                        callback_function = classes[i + 1];
353                        break;
354                    }
355                }
356            }
357      
358            if(window[callback_function] != 'undefined' && (typeof window[callback_function] == 'function')) {
359                return window[callback_function](field, caption);
360            }
361      
362            return i18n('callback', caption, callback_function);
363        }
364  
365    };
366
367    /** 
368     * Simple replacement for i18n + sprintf
369     *
370     * @param string language for for the local i18n object
371     * @param mixed substitution vars
372     *
373     * @return internationalized string
374     */
375    var i18n = function(lang_key) {
376        var lang_string = i18n_strings[lang_key];
377        var bits = lang_string.split('%');
378        var out = bits[0];
379        var re = /^([ds])(.*)$/;
380        for (var i=1; i<bits.length; i++) {
381            p = re.exec(bits[i]);
382            if (!p || arguments[i] == null) continue;
383            if (p[1] == 'd') {
384                out += parseInt(arguments[i], 10);
385            } else if (p[1] == 's') {
386                out += arguments[i];
387            }
388            out += p[2];
389        }
390        return out;
391    };
392
393    /** 
394     * Uni-Form form validation error
395     *
396     * @param string title of the form error
397     * @param array  list of error messages to show
398     *
399     * @return false
400     */
401    var showFormError = function(form, title, messages) {
402      if($('#errorMsg').length) {
403        $('#errorMsg').remove();
404      }
405      $message =
406          $('<div />')
407              .attr('id','errorMsg')
408              .html("<h3>" + title + "</h3>");
409      if(messages.length) {
410          $message.append($('<ol />'));
411          for(m in messages) {
412              $('ol', $message).append(
413                  $('<li />').text(messages[m])
414              );
415          }
416      }
417      form.prepend($message);
418      $('html, body').animate({
419          scrollTop: form.offset().top
420      }, 500);
421      $('#errorMsg').slideDown();
422      return false;
423    };
424    
425    var showFormSuccess = function(form, title) {
426      if($('#okMsg').length) {
427        $('#okMsg').remove();
428      }
429      $message =
430          $('<div />')
431              .attr('id','okMsg')
432              .html("<h3>" + title + "</h3>");
433      form.prepend($message);
434      $('html, body').animate({
435          scrollTop: form.offset().top
436      }, 500);
437      $('#okMsg').slideDown();
438      return false;
439    };
440
441    return this.each(function() {
442        var form = jQuery(this);
443        
444        /**
445         * Set the results of form validation on the form element
446         *
447         * @param object $input jQuery form element
448         * @param bool   valid  true if the form value passed all validation
449         * @param string text   validation error message to display
450         *
451         * @return null
452         */
453        var validate = function($input,valid,text) {
454            var $p = $input.closest('div.' + settings.holder_class)
455                           .andSelf()
456                           .toggleClass(settings.invalid_class, !valid)
457                           .toggleClass(settings.error_class, !valid)
458                           .toggleClass(settings.valid_class, valid)
459                           .find('p.formHint');
460    
461            if (! valid && ! $p.data('info-text')) {
462                $p.data('info-text', $p.html());
463            }
464            else if (valid) {
465                text = $p.data('info-text');
466            }
467    
468            if (text) {
469                $p.html(text);
470            }
471        };
472        
473        /**
474         * Select form fields and attach the higlighter functionality
475         *
476         */
477        form.find(settings.field_selector).each(function(){
478            var $input = $(this),
479                value = $input.val();
480
481            $input.data('default-color',$input.css('color'));
482
483            if (value === $input.data('default-value') || ! value) {
484                $input.not('select').css("color", settings.default_value_color);
485                $input.val($input.data('default-value'));
486            }
487        });
488
489        /**
490         * If we've set ask_on_leave we'll register a handler here
491         *
492         * We need to seriaze the form data, wait for a beforeunload, 
493         * then serialize and compare for changes
494         *
495         * If they changed things, and haven't submitted, we'll let them
496         * know about it
497         * 
498         */
499        if(settings.ask_on_leave || form.hasClass('askOnLeave')) {
500            var initial_values = form.serialize();
501            $(window).bind("beforeunload", function(e) {
502                if((initial_values != form.serialize()) 
503                    && (settings.ask_on_leave || form.hasClass('askOnLeave'))
504                ) {
505                    return ($.isFunction(settings.on_leave_callback))
506                        ? settings.on_leave_callback(form)
507                        : confirm(i18n('on_leave'));
508                }
509            });
510        }
511
512        /** 
513         * Handle the submission of the form
514         *
515         * Tasks
516         *  * Remove any default values from the form
517         *  * If prevent_submit is set true, return false if
518         *    there are outstanding errors in the form
519         *
520         * Todo
521         *  * it would be novel to use prevent_submit to disable
522         *    the submit button in the blur handler
523         *
524         * @return bool
525         */
526        form.submit(function(){
527            // in the case of a previously failed submit, we'll remove our marker
528            form.removeClass('failedSubmit');
529            
530            // remove the default values from the val() where they were being displayed
531            form.find(settings.field_selector).each(function(){
532                if($(this).val() === $(this).data('default-value')) { $(this).val(""); }
533            });
534
535            // traverse and revalidate making sure that we haven't missed any fields
536            // perhaps if a field was filled in before uniform was initialized
537            // or if blur failed to fire correctly
538            if(settings.prevent_submit || form.hasClass('preventSubmit')) {
539              // use blur to run the validators on each field
540              form.find(settings.field_selector).each(function(){
541                $(this).blur();
542              });
543            
544              if (form
545                    .find('.' + settings.invalid_class)
546                    .add('.' + settings.error_class).length
547              ) {
548                form.addClass('failedSubmit');
549                return ($.isFunction(settings.prevent_submit_callback))
550                    ? settings.prevent_submit_callback(form)
551                    : showFormError(form, i18n('submit_msg'), [i18n('submit_help')]);
552              }
553              
554              settings.ask_on_leave = false;
555              form.removeClass('askOnLeave');
556              return true;
557            }
558            
559            // qUnit needs to run this function, and still prevent the submit
560            if(form.parents('#qunit-fixture').length) {
561              return false;
562            }
563          
564            settings.ask_on_leave = false;
565            form.removeClass('askOnLeave');
566            return true;
567        });
568        
569        /**
570         * Set the form focus class
571         * 
572         * Remove any classes other than the focus class and hide the default label text
573         *
574         */
575        form.delegate(settings.field_selector, 'focus', function() {
576            form.find('.' + settings.focused_class).removeClass(settings.focused_class);
577
578            var $input = $(this);
579
580            $input.parents().filter('.'+settings.holder_class+':first').addClass(settings.focused_class);
581            
582            if($input.val() === $input.data('default-value')){
583                $input.val("");
584            }
585
586            $input.not('select').css('color',$input.data('default-color'));
587        });
588
589        /**
590         * Validate a form field on the blur event
591         * 
592         * Search the classnames on the element for the names of 
593         * validators, and run them as we find them
594         *
595         * If the validators fail, we trigger either 'success' or 'error' events
596         *
597         */
598        form.delegate(settings.field_selector, 'blur', function() {
599            var $input = $(this);
600            var label  = $(this)
601                .closest('div.' + settings.holder_class)
602                .find('label').text().replace('*','');
603
604            // remove focus from form element
605            form.find('.' + settings.focused_class).removeClass(settings.focused_class);
606            
607            // (if empty or equal to default value) AND not required
608            if(($input.val() === "" || $input.val() === $input.data('default-value'))
609                && !$input.hasClass('required')
610            ){
611                $input.not('select').css("color",settings.default_value_color);
612                $input.val($input.data('default-value'));
613                return;
614            }
615
616            // run the validation and if they all pass, we mark the color and move on
617            var has_validation = false;
618            for(validator in self.validators) {
619                if($input.hasClass(validator)){
620                    has_validation = true;
621                    var validation_result = self.validators[validator]($input, label);
622                    if(typeof(validation_result) == 'string') {
623                        $input.trigger('error', validation_result);
624                        return;
625                    }
626                }
627            }
628            
629            // if it had validation and we didn't return above,
630            // then all validation passed
631            if (has_validation) {
632                $input.trigger('success');
633            }
634            
635            // return the color to the default
636            $input.css('color', $input.data('default-color'));
637            return;
638        });
639
640        /**
641         * Handle a validation error in the form element
642         * 
643         * This will set the field to have the error marker
644         * and update the warning text
645         *
646         * @param event  e
647         * @param string validation message
648         */
649        form.delegate(settings.field_selector,'error',function(e,text) {
650            validate($(this), false, text);
651        });
652
653        /**
654         * Handle a succesful validation in the form element
655         * 
656         * Remove any error messages and set the validation 
657         * marker to be success
658         *
659         * @param event  e
660         * @param string unused
661         */
662        form.delegate(settings.field_selector,'success',function(e,text) {
663            validate($(this), true);
664        });
665    });
666};
667
668/**
669 * Internationalized language strings for validation messages
670 */
671jQuery.fn.uniform.language = {
672    required      : '%s is required',
673    req_radio     : 'Please make a selection',
674    req_checkbox  : 'You must select this checkbox to continue',
675    minlength     : '%s should be at least %d characters long',
676    min           : '%s should be greater than or equal to %d',
677    maxlength     : '%s should not be longer than %d characters',
678    max           : '%s should be less than or equal to %d',
679    same_as       : '%s is expected to be same as %s',
680    email         : '%s is not a valid email address',
681    url           : '%s is not a valid URL',
682    number        : '%s needs to be a number',
683    integer       : '%s needs to be a whole number',
684    alpha         : '%s should contain only letters (without special characters or numbers)',
685    alphanum      : '%s should contain only numbers and letters (without special characters)',
686    phrase        : '%s should contain only alphabetic characters, numbers, spaces, and the following: . , - _ () * # :',
687    phone         : '%s should be a phone number',
688    date          : '%s should be a date (mm/dd/yyyy)',
689    callback      : 'Failed to validate %s field. Validator function (%s) is not defined!',
690    on_leave      : 'Are you sure you want to leave this page without saving this form?',
691    submit_msg    : 'Sorry, this form needs corrections.',
692    submit_help   : 'Please see the items marked below.',
693    submit_success: 'Thank you, this form has been sent.'
694};
695
696/**
697 * See the validation.md file for more information about these options
698 */
699jQuery.fn.uniform.defaults = {
700    prevent_submit          : false,
701    prevent_submit_callback : false,
702    ask_on_leave            : false,
703    on_leave_callback       : false,
704    valid_class             : 'valid',
705    invalid_class           : 'invalid',
706    error_class             : 'error',
707    focused_class           : 'focused',
708    holder_class            : 'ctrlHolder',
709    field_selector          : 'input, textarea, select',
710    default_value_color     : "#AFAFAF"
711};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.