var edx = edx || {}; (function( $, _, _s, gettext ) { 'use strict'; /* Mix non-conflicting functions from underscore.string * (all but include, contains, and reverse) into the * Underscore namespace. In practice, this mixin is done * by the access view, but doing it here helps keep the * utility self-contained. */ _.mixin( _.str.exports() ); edx.utils = edx.utils || {}; var utils = (function(){ var _fn = { validate: { msg: { email: '<li><%- gettext("The email address you\'ve provided isn\'t formatted correctly.") %></li>', min: '<li><%- _.sprintf( gettext("%(field)s must have at least %(count)d characters."), context ) %></li>', max: '<li><%- _.sprintf( gettext("%(field)s can only contain up to %(count)d characters."), context ) %></li>', required: '<li><%- _.sprintf( gettext("Please enter your %(field)s."), context ) %></li>', custom: '<li><%= content %></li>' }, field: function( el ) { var $el = $(el), required = true, min = true, max = true, email = true, response = {}, isBlank = _fn.validate.isBlank( $el ); if ( _fn.validate.isRequired( $el ) ) { if ( isBlank ) { required = false; } else { min = _fn.validate.str.minlength( $el ); max = _fn.validate.str.maxlength( $el ); email = _fn.validate.email.valid( $el ); } } else if ( !isBlank ) { min = _fn.validate.str.minlength( $el ); max = _fn.validate.str.maxlength( $el ); email = _fn.validate.email.valid( $el ); } response.isValid = required && min && max && email; if ( !response.isValid ) { _fn.validate.removeDefault( $el ); response.message = _fn.validate.getMessage( $el, { required: required, min: min, max: max, email: email }); } return response; }, str: { minlength: function( $el ) { var min = $el.attr('minlength') || 0; return min <= $el.val().length; }, maxlength: function( $el ) { var max = $el.attr('maxlength') || false; return ( !!max ) ? max >= $el.val().length : true; } }, isRequired: function( $el ) { return $el.attr('required'); }, isBlank: function( $el ) { var type = $el.attr('type'), isBlank; if ( type === 'checkbox' ) { isBlank = !$el.prop('checked'); } else if ( type === 'select' ) { isBlank = ( $el.data('isdefault') === true ); } else { isBlank = !$el.val(); } return isBlank; }, email: { // This is the same regex used to validate email addresses in Django 1.4 regex: new RegExp( [ '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*', '|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"', ')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+[A-Z]{2,6}\\.?$)', '|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$' ].join(''), 'i' ), valid: function( $el ) { return $el.attr('type') === 'email' ? _fn.validate.email.format( $el.val() ) : true; }, format: function( str ) { return _fn.validate.email.regex.test( str ); } }, getLabel: function( id ) { // Extract the field label, remove the asterisk (if it appears) and any extra whitespace return $("label[for=" + id + "]").text().split("*")[0].trim(); }, getMessage: function( $el, tests ) { var txt = [], tpl, label, obj, customMsg; _.each( tests, function( value, key ) { if ( !value ) { label = _fn.validate.getLabel( $el.attr('id') ); customMsg = $el.data('errormsg-' + key) || false; // If the field has a custom error msg attached, use it if ( customMsg ) { tpl = _fn.validate.msg.custom; obj = { content: customMsg }; } else { tpl = _fn.validate.msg[key]; obj = { // We pass the context object to the template so that // we can perform variable interpolation using sprintf context: { field: label } }; if ( key === 'min' ) { obj.context.count = parseInt( $el.attr('minlength'), 10 ); } else if ( key === 'max' ) { obj.context.count = parseInt( $el.attr('maxlength'), 10 ); } } txt.push( _.template( tpl, obj ) ); } }); return txt.join(' '); }, // Removes the default HTML5 validation pop-up removeDefault: function( $el ) { if ( $el.setCustomValidity ) { $el.setCustomValidity(' '); } } } }; return { validate: _fn.validate.field }; })(); edx.utils.validate = utils.validate; })( jQuery, _, _.str, gettext );