PageSourceSearch

https://www.afaindia.com/vendor/j-pro/js/jquery.j-pro.js

js afaindia.com collected 2026-10-10 00:45:03 UTC 41,320 bytes, 1,605 lines download raw bytes

1;(function ( $ ) {
2"use strict";
3
4/* Defaul options */
5var defaults = {
6	rules: {},
7	messages: {},
8	error: {
9		scroll: true,
10		highlight: true,
11		inBlock: false,
12		inTooltip: false,
13		underField: true,
14		inTooltipClass: "j-tooltip-right-top",
15	},
16	success: {
17		highlight: true,
18	},
19	validationEvent: {
20		onsubmit: true,
21		onchange: true,
22		onkeyup: true,
23	},
24	formType: {
25		modal: false,
26		multistep: false,
27	},
28	formRedirect: {
29		redirect: false,
30		address: "http://codecanyon.net/user/"
31	},
32	formHide: {
33		hide: false,
34		closeBtn: false
35	},
36	formTotalData: false,
37	timeoutSuccessMsg: 5000,
38	repeatSubmission: true,
39	submit: true,
40	debug: false,
41	debugArr: [],
42	afterInitHandler:function() { return true; },
43	beforeSubmitHandler:function() { return true; },
44	afterSubmitHandler:function() { return true; },
45	dict: {
46		debugError: "Oops! Debug errors occurred. Enable debug mode for JavaScript code",
47		rulesError: "User settings: You have to specify validation rules for a form",
48		messagesError: "User settings: You have to specify validation messages for a form",
49		modalOpenError: "Could not find a link/button with class='j-modal-open' to open modal form",
50		responseBlockError: "Could not find a block with class='j-response' for server response",
51		submitBtnError: "Could not find a button[type='submit'] for form submission",
52		totalDataBlockError: "Could not find a block with class='j-total-data' for total form data",
53		multistepOneStepError: "Multistep form has only one step. At least two steps are required!",
54		rulesUnknownRuleError: "User settings -> 'rules' array: Unknown validation rule: ",
55		rulesNotAllowedValueError: "User settings -> 'rules' array -> Not allowed value: ",
56		rulesMessagesMismatchError: "User settings: field names in 'rules' and 'messages' are mismatched",
57		formDataError: "FormData is not supported in this browser. Please update it to the latest version or use another browser",
58		disabledBtn: "Thanks!",
59		multistepNextBtn: "Next",
60		multistepPrevBtn: "Back",
61		errorBlockText: "Oops! The following errors occurred:",
62		successBlockText: "Thank you! We got your email successfully",
63	},
64	mimeTypes: {
65		"jpeg": "image/jpeg",
66		"tiff": "image/tiff",
67		"jpg": "image/jpg",
68		"png": "image/png",
69		"gif": "image/gif",
70		"ico": "image/vnd.microsoft.icon",
71		"doc": "application/msword",
72		"xls": "application/vnd.ms-excel",
73		"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
74		"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
75		"txt": "text/plain",
76		"csv": "text/csv",
77		"zip": "application/zip",
78		"gzip": "application/gzip",
79		"rar": "application/x-rar-compressed",
80		"odf": ["application/vnd.oasis.opendocument.text",
81				"application/vnd.oasis.opendocument.spreadsheet",
82				"application/vnd.oasis.opendocument.presentation",
83				"application/vnd.oasis.opendocument.graphics"],
84		"pdf": "application/pdf",
85		"powerpoint": ["application/vnd.ms-powerpoint",
86						"application/vnd.openxmlformats-officedocument.presentationml.presentation"],
87		"mpeg": ["audio/mpeg","video/mpeg"],
88		"mp4": ["audio/mp4","video/mp4"],
89		"ogg": ["audio/ogg","video/ogg","application/ogg"],
90	},
91};
92
93/* File validation */
94function _fileCheck( fileObj, rules, messages, mimeTypes ) {
95	var type, len, i,
96		file 		 = fileObj[0].files[0],
97		required	 = rules.required,
98		validate	 = rules.validate,
99		extension	 = rules.extension.toLowerCase(),
100		size		 = rules.size * 1024 * 1024,
101		allowedTypes = [],
102		extensionArr = extension.split("|");
103
104	// Add required valid types
105	for ( type in mimeTypes ) {
106		if ( extensionArr.indexOf(type) === -1 ) {
107			continue;
108		}
109		if ( $.isArray( mimeTypes[type] ) ) {
110			len = mimeTypes[type].length;
111			for ( i=0; i<len; i++ ) {
112				allowedTypes.push( mimeTypes[type][i] );
113			}
114			continue;
115		}
116		allowedTypes.push( mimeTypes[type] );
117	}
118
119	// Validation
120	if ( validate || required ) {
121		// If file is required
122		if ( required ) {
123			// If file is empty
124			if ( !file ) {
125				return messages.required;
126			}
127		}
128		// If file is not required
129		// Validate file only if it exists
130		if ( file ) {
131			// Check allowed file types
132			if ( allowedTypes.indexOf(file.type) === -1 ) {
133				return messages.size_extension;
134			}
135			// Check allowed file size
136			if ( file.size > size) {
137				return messages.size_extension;
138			}
139			return false;
140		}
141	}
142}
143
144/* Email validation */
145function _emailCheck( value ) {
146	var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
147	return !re.test( value );
148}
149
150/* Url validation */
151function _urlCheck( value ){
152	var re = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i;
153	return !re.test( value );
154}
155
156/* EqualTo validation */
157function _equalToCheck( value, targetValue ) {
158	return ( value.trim() !== targetValue.trim() ) ? true : false;
159}
160
161/* Min value validation */
162function _minValueCheck( value, minVal ) {
163	var val = value.trim();
164
165	if ( _numberCheck( val ) ) {
166		return true;
167	}
168	return ( val < minVal ) ? true : false;
169}
170
171/* Max value validation */
172function _maxValueCheck( value, maxVal ) {
173	var val = value.trim();
174
175	if ( _numberCheck( val ) ) {
176		return true;
177	}
178	return ( val > maxVal ) ? true : false;
179}
180
181/* Range value validation */
182function _rangeValueCheck( value, rangeVal ) {
183	var val = value.trim();
184
185	if ( _numberCheck( val ) ) {
186		return true;
187	}
188	return ( val < rangeVal[0] || val > rangeVal[1] ) ? true : false;
189}
190
191/* Min length validation */
192function _minLengthCheck( value, minLen ) {
193	return ( value.trim().length < minLen ) ? true : false;
194}
195
196/* Max length validation */
197function _maxLengthCheck( value, maxLen ) {
198	return ( value.trim().length > maxLen ) ? true : false;
199}
200
201/* Range length validation */
202function _rangeLengthCheck( value, rangeLen ) {
203	var val = value.trim().length;
204	return ( val < rangeLen[0] || val > rangeLen[1] ) ? true : false;
205}
206
207/* Integer validation */
208function _integerCheck( value ) {
209	var re = /^-?\d+$/;
210	return !re.test( value );
211}
212
213/* Number validation */
214function _numberCheck( value ) {
215	var re = /^-?\d+(?:\.\d+)?$/;
216	return !re.test( value );
217}
218
219/* Validate rules object */
220function _rulesCheck( obj ) {
221	var self = this,
222		config = self.config,
223		message = self.config.dict.rulesNotAllowedValueError;
224
225	$.each(obj, function( name, value ) {
226		$.each(value, function( rule, val ) {
227			switch( rule ) {
228			case "required":
229			case "email":
230			case "url":
231			case "integer":
232			case "number":
233			case "validate":
234				if ( $.type( val ) !== "boolean" ) {
235					config.debugArr.push( _errorMsg( message, rule, val ) );
236				}
237				break;
238			case "minlength":
239			case "maxlength":
240				if ( _integerCheck( val ) || val < 0 ) {
241					config.debugArr.push( _errorMsg( message, rule, val ) );
242				}
243				break;
244			case "minvalue":
245			case "maxvalue":
246				if ( _numberCheck( val ) ) {
247					config.debugArr.push( _errorMsg( message, rule, val ) );
248				}
249				break;
250			case "rangelength":
251				if ( !$.isArray( val ) ||
252						val.length !== 2 ||
253						_integerCheck( val[0] ) ||
254						_integerCheck( val[1] ) ||
255						val[0] > val[1]
256					) {
257					config.debugArr.push( _errorMsg( message, rule, val ) );
258				}
259				break;
260			case "rangevalue":
261				if ( !$.isArray( val ) ||
262						val.length !== 2 ||
263						_numberCheck( val[0] ) ||
264						_numberCheck( val[1] ) ||
265						val[0] > val[1]
266					) {
267					config.debugArr.push( _errorMsg( message, rule, val ) );
268				}
269				break;
270			case "size":
271				if ( _numberCheck( val ) || val <= 0 ) {
272					config.debugArr.push( _errorMsg( message, rule, val ) );
273				}
274				break;
275			case "extension":
276			case "equalTo":
277				if ( $.type( val ) !== "string" ) {
278					config.debugArr.push( _errorMsg( message, rule, val ) );
279				}
280				break;
281			case "requiredFromGroup":
282				if ( !$.isArray( val ) ||
283						val.length !== 2 ||
284						_integerCheck( val[0] ) ||
285						$.type( val[1] ) !== "string"
286					) {
287					config.debugArr.push( _errorMsg( message, rule, val ) );
288				}
289				break;
290			default:
291				config.debugArr.push( config.dict.rulesUnknownRuleError + rule );
292			}
293		});
294	});
295}
296
297/* Validate messages object */
298function _messagesCheck( obj ) {
299	var rKeys = Object.keys( obj.rules ).sort(),
300		mKeys = Object.keys( obj.messages ).sort();
301
302	if ( JSON.stringify( rKeys ) !== JSON.stringify( mKeys ) ) {
303		this.config.debugArr.push( this.config.dict.rulesMessagesMismatchError );
304	}
305}
306
307/* Config check error message */
308function _errorMsg( message, rule, val ) {
309	return message + rule + ": " + val +" ( " + $.type(val) + " )";
310}
311
312/* Get field value */
313function _getValue( obj ) {
314	var res;
315
316	obj.each( function() {
317		if ( $( this ).is( ":checkbox" ) || $( this ).is( ":radio" ) ) {
318			if ( $( this ).is( ":checked" ) ) {
319				if ( res !== undefined ) {
320					res += ", "+$( this ).val();
321				} else {
322					res = $( this ).val();
323				}
324			}
325		} else {
326			res = $( this ).val();
327		}
328	});
329
330	if ( $.isArray( res ) ) {
331		res = res.join(", ");
332	}
333	return ( res === undefined || res === null ) ? "" : res;
334}
335
336/* Check if field is hidden or has "display:none;" */
337function _hiddenCheck( field ) {
338	if ( $( field ).css("display") === "none" ||
339		 $( field ).css("visibility") === "hidden" ||
340		 $( field ).is(":disabled") ){
341			return true;
342		}
343	return false;
344}
345
346/* Constructor */
347function JustFormsPro( obj, options ) {
348	this.config = $.extend( true, {}, defaults, options );
349	this.form = obj[0];
350	this.$form = obj;
351	this.$formObj = {};
352	this.$formObjActiveStep = {};
353	this.errorExists = false;
354	this.errorMessages = {};
355
356	// Form elements processing
357	this.$formResponseBlock = this.$form.find( ".j-response" );
358	this.$submitBtn = this.$form.find( "button[type='submit']" );
359
360	// Form plugin init
361	this.init();
362}
363
364JustFormsPro.prototype = {
365
366	/* Init */
367	init: function() {
368		var self = this,
369			$form = self.$form,
370			config = self.config,
371			$formObj = self.$formObj,
372			str, $el;
373
374		// FormData support
375		if ( typeof window.FormData === "undefined" ) {
376			config.debugArr.push( config.dict.formDataError );
377		}
378
379		// User input validaion
380		if ( $.isEmptyObject( config.rules ) ) {
381			config.debugArr.push( config.dict.rulesError );
382		} else {
383			_rulesCheck.call( self, config.rules );
384		}
385		if ( $.isEmptyObject( config.messages ) ) {
386			config.debugArr.push( config.dict.messagesError );
387		} else {
388			_messagesCheck.call( self, config );
389		}
390
391		// Response block availability
392		if ( !self.$formResponseBlock.length ){
393			config.debugArr.push( config.dict.responseBlockError );
394		}
395
396		// Submit button availability
397		if ( !self.$submitBtn.length ){
398			config.debugArr.push( config.dict.submitBtnError );
399		}
400
401		// Hide form
402		if ( config.formHide.hide ) {
403			self.initHideForm();
404		}
405
406		// Modal form
407		if ( config.formType.modal ) {
408			self.initModalForm();
409		}
410
411		// Multistep form
412		if ( config.formType.multistep ) {
413			self.initMultistepForm();
414		}
415
416		// Init total form data
417		if ( config.formTotalData ) {
418			self.initTotalData();
419		}
420
421		// Debug result
422		if ( config.debugArr.length ) {
423			if ( !config.debug ){
424				console.log( config.dict.debugError );
425				$form.on( "submit", false );
426				return false;
427			}
428			str = config.debugArr.join( '\n -------------- \n' );
429			console.warn( str );
430			console.log( str );
431			config.debugArr = [];
432			$form.on( "submit", false );
433			return false;
434		}
435
436		// Object with html entities
437		$.each( config.rules, function( name, rules ) {
438			$el = $form.find( "[name='"+name+"']" );
439			if ( $el.length ) {
440				$formObj[name] = $el;
441			}
442		});
443
444		// Events
445		if ( config.validationEvent.onchange || config.validationEvent.onkeyup ) {
446			self.initEvent();
447		}
448
449		// Processing total form data
450		if ( config.formTotalData ) {
451			self.processingTotalData();
452		}
453
454		// Bind submit event
455		$form.on( "submit", self.submitForm.bind(self) );
456	},
457
458	/* Init events */
459	initEvent: function() {
460		var self = this,
461			config = self.config,
462			$formObj = self.$formObj,
463			eventsArr = [];
464
465		// Create events
466		if ( config.validationEvent.onchange ) {
467			eventsArr.push("change");
468		}
469		if ( config.validationEvent.onkeyup ) {
470			eventsArr.push("keyup");
471		}
472
473		// Add events
474		eventsArr.forEach( function(event) {
475			self.$form.on( event, ":input", function() {
476				self.clearField( this );
477				self.validateField( this );
478			});
479			$.each( config.rules, function( name, rules ) {
480				$.each( rules, function( rule, value ){
481					if ( rule === "equalTo" ) {
482						$( value ).on( event, function(){
483							self.clearField( $formObj[name][0] );
484							self.validateField( $formObj[name][0] );
485						});
486					}
487				});
488			});
489		});
490	},
491
492	/* Submit a form */
493	submitForm: function(e) {
494		var self = this,
495			config = self.config,
496			$prevBtn = self.$prevBtn,
497			$submitBtn = self.$submitBtn;
498
499		if (e) {
500			e.preventDefault();
501			e.stopPropagation();
502		}
503
504		// After init custom handler
505		if ( !config.afterInitHandler() ) {
506			return false;
507		}
508
509		// Form validation
510		if ( config.validationEvent.onsubmit ) {
511			// Buttons disabled
512			var formid = this.form.id;
513			if(formid == 'signupjpro' || formid == 'singninpro' || formid == 'forgotpro'){
514				
515			} else {
516				$submitBtn.toggleClass( "j-processing", true ).attr( "disabled", true );
517			}
518			if ( config.formType.multistep ) {
519				$prevBtn.attr( "disabled", true );
520			}
521
522			// Clear a form
523			self.clearForm();
524
525			// Validate form
526			self.validateForm();
527		}
528
529		// If errors exist
530		if ( self.errorExists ) {
531			// Scroll to first element with error
532			if ( config.error.scroll ) {
533				self.scrollToError();
534			}
535
536			// Show errors within an error block
537			if ( config.error.inBlock ) {
538				self.createErrorBlock( self.errorMessages );
539			}
540
541			// Buttons enabled
542			$submitBtn.toggleClass( "j-processing", false ).removeAttr( "disabled" );
543			if ( config.formType.multistep ) {
544				$prevBtn.removeAttr( "disabled" );
545			}
546			return false;
547		}
548
549		// Before submit custom handler
550		if ( !config.submit || !config.beforeSubmitHandler() ) {
551			// Buttons enabled
552			$submitBtn.toggleClass( "j-processing", false ).removeAttr( "disabled" );
553			if ( self.config.formType.multistep ) {
554				$prevBtn.removeAttr( "disabled" );
555			}
556			return false;
557		}
558
559		// Send form data
560		self.getServerResponse();
561	},
562
563	/* AJAX request */
564	sendFormDataAsync: function() {
565		
566		var self = this,
567			form = self.form,
568			formData;
569
570		// Form data
571		formData = new FormData( form );
572		var formid = this.form.id;
573		if(formid == 'signupjpro' || formid == 'singninpro' || formid == 'forgotpro'){
574			
575		} else {
576			this.form.submit();
577		}
578
579		// return $.ajax({
580			// url: form.action,
581			// type: form.method,
582			// contentType: false,
583			// processData: false,
584			// data: formData
585		// });
586	},
587
588	/* Send form data */
589	getServerResponse: function() {
590		var self = this,
591			config = self.config,
592			$prevBtn = self.$prevBtn,
593			$submitBtn = self.$submitBtn,
594			$formResponseBlock = self.$formResponseBlock,
595			result;
596
597		self.sendFormDataAsync().then(function( data ) {
598
599			// Server error processing
600			if ( config.debug ) {
601				console.warn( data );
602			}
603
604			result = $.parseJSON( data );
605
606			// Error message from server
607			if ( result.error ) {
608
609				// Add class "j-error-message"
610				$formResponseBlock.toggleClass( "j-error-message j-unit", true );
611				$formResponseBlock.html( result.error );
612
613				// Buttons enabled
614				$submitBtn.removeClass( "j-processing" ).removeAttr( "disabled" );
615				if ( config.formType.multistep ) {
616					$prevBtn.removeAttr( "disabled" );
617				}
618			}
619
620			// Success message from server
621			if ( result.success ) {
622
623				// Custom function applied after submission
624				if ( !config.afterSubmitHandler() ) {
625					// Buttons enabled
626					$submitBtn.removeClass( "j-processing" ).removeAttr( "disabled" );
627					if ( config.formType.multistep ) {
628						$prevBtn.removeAttr( "disabled" );
629					}
630					return false;
631				}
632
633				// Hide form after successful submitting
634				if ( config.formHide.hide ) {
635					self.processingHideForm();
636					return false;
637				}
638
639				// Redirect form after success submitting
640				if ( config.formRedirect.redirect ) {
641					self.redirectForm();
642					return false;
643				}
644
645				// Clear a form
646				// Show success message
647				self.resetForm();
648				self.createSuccessBlock( result.success );
649			}
650		});
651	},
652
653	/* Validate field */
654	validateField: function( field ) {
655		var self = this,
656			config = self.config,
657			$formObj = self.$formObj,
658			fieldName = $( field ).attr( "name" ),
659			fieldValue;
660
661		// If a form does not have a field
662		if ( $formObj[fieldName] === undefined || $formObj[fieldName] === null ) {
663			return false;
664		}
665
666		fieldValue = _getValue( $formObj[fieldName] );
667
668		self.validation( config.rules[fieldName], field, fieldValue, fieldName);
669
670		// Show errors within an error block
671		if ( config.error.inBlock ) {
672			self.createErrorBlock();
673		}
674	},
675
676	/* Validate all form fields */
677	validateForm: function() {
678		var self = this,
679			config = self.config,
680			$formObj, fieldValue;
681
682		// If form is mulultistep
683		// Get fields from "active" step only
684		if ( config.formType.multistep ) {
685			self.$formObjActiveStep = {};
686			self.getActiveStepField();
687			$formObj = self.$formObjActiveStep;
688		} else {
689			$formObj = self.$formObj;
690		}
691
692		$.each( config.rules, function( name, rules ) {
693
694			// If a form does not have a field
695			if ( $formObj[name] === undefined || $formObj[name] === null ) {
696				return;
697			}
698			
699			// Skip the validation if field is hidden
700			if ( _hiddenCheck( $formObj[name] ) ){
701				return;
702			}
703
704			// Get field value
705			fieldValue = _getValue( $formObj[name] );
706
707			// Fields processing
708			self.validation( rules, $formObj[name], fieldValue, name );
709		});
710	},
711
712	/* Validation functions */
713	validation: function( rules, field, fieldValue, fieldName ) {
714		var self = this,
715			config = self.config,
716			count = 0,
717			groupObj = {},
718			res, groupClass, id;
719
720		// Files processing
721		if ( $( field ).attr( "type" ) && $( field ).attr( "type" ) === "file" ) {
722			if ( config.rules[fieldName].required === true ||
723					config.rules[fieldName].validate === true ) {
724
725				res = _fileCheck( $( field ), config.rules[fieldName],
726						config.messages[fieldName], config.mimeTypes );
727
728				if ( res ) {
729					self.setErrorState( $( field ), res );
730					return false;
731				}
732				self.setSuccessState( field );
733			}
734		}
735
736		// Fields processing
737		$.each( rules, function( rule, ruleValue ){
738			switch( rule ) {
739			case "required":
740				if ( ruleValue === true ) {
741					if ( _minLengthCheck( fieldValue, 1 ) ) {
742						self.setErrorState( field, config.messages[fieldName][rule] );
743						return false;
744					}
745					self.setSuccessState( field );
746				}
747				break;
748			case "email":
749				if ( ruleValue === true ) {
750					if ( _emailCheck( fieldValue ) ) {
751						self.setErrorState( field, config.messages[fieldName][rule] );
752						return false;
753					}
754					self.setSuccessState( field );
755				}
756				break;
757			case "url":
758				if ( ruleValue === true ) {
759					if ( _urlCheck( fieldValue ) ) {
760						self.setErrorState( field, config.messages[fieldName][rule] );
761						return false;
762					}
763					self.setSuccessState( field );
764				}
765				break;
766			case "integer":
767				if ( ruleValue === true ) {
768					if ( _integerCheck( fieldValue ) ) {
769						self.setErrorState( field, config.messages[fieldName][rule] );
770						return false;
771					}
772					self.setSuccessState( field );
773				}
774				break;
775			case "number":
776				if ( ruleValue === true ) {
777					if ( _numberCheck( fieldValue ) ) {
778						self.setErrorState( field, config.messages[fieldName][rule] );
779						return false;
780					}
781					self.setSuccessState( field );
782				}
783				break;
784			case "minlength":
785				if ( _minLengthCheck( fieldValue, config.rules[fieldName][rule] ) ) {
786					self.setErrorState( field, config.messages[fieldName][rule] );
787					return false;
788				}
789				self.setSuccessState( field );
790				break;
791			case "maxlength":
792				if ( _maxLengthCheck( fieldValue, config.rules[fieldName][rule] ) ) {
793					self.setErrorState( field, config.messages[fieldName][rule] );
794					return false;
795				}
796				self.setSuccessState( field );
797				break;
798			case "rangelength":
799				if ( _rangeLengthCheck( fieldValue, config.rules[fieldName][rule] ) ) {
800					self.setErrorState( field, config.messages[fieldName][rule] );
801					return false;
802				}
803				self.setSuccessState( field );
804				break;
805			case "minvalue":
806				if ( _minValueCheck( fieldValue, config.rules[fieldName][rule] ) ) {
807					self.setErrorState( field, config.messages[fieldName][rule] );
808					return false;
809				}
810				self.setSuccessState( field );
811				break;
812			case "maxvalue":
813				if ( _maxValueCheck( fieldValue, config.rules[fieldName][rule] ) ) {
814					self.setErrorState( field, config.messages[fieldName][rule] );
815					return false;
816				}
817				self.setSuccessState( field );
818				break;
819			case "rangevalue":
820				if ( _rangeValueCheck( fieldValue, config.rules[fieldName][rule] ) ) {
821					self.setErrorState( field, config.messages[fieldName][rule] );
822					return false;
823				}
824				self.setSuccessState( field );
825				break;
826			case "equalTo":
827
828				// Get id
829				id = ruleValue.slice(1);
830
831				// Get element's value with id
832				$.each( self.$formObj, function(){
833					if ( this.attr( "id" ) === id ) {
834						res = this.val();
835					}
836				});
837				if ( _equalToCheck( fieldValue, res ) ) {
838					self.setErrorState( field, config.messages[fieldName][rule] );
839					return false;
840				}
841				self.setSuccessState( field );
842				break;
843			case "requiredFromGroup":
844
845				// Get class name
846				groupClass = ruleValue[1].slice(1);
847
848				// Get elements with class
849				$.each( self.$formObj, function( fieldName, fieldObj ){
850					if ( fieldObj.hasClass( groupClass ) ) {
851						groupObj[fieldName] = fieldObj;
852						self.clearField( fieldObj );
853					}
854				});
855
856				// Validation
857				$.each( groupObj, function(){
858					if ( !_minLengthCheck( _getValue( this ), 1 ) ) {
859						count++;
860					}
861				});
862
863				// Add error state
864				if ( count < ruleValue[0] ) {
865					$.each( groupObj, function(){
866						self.setErrorState( this, config.messages[fieldName][rule] );
867					});
868					return false;
869				}
870
871				// Add success state
872				$.each( groupObj, function(){
873					self.setSuccessState( this );
874				});
875				break;
876			}
877		});
878	},
879
880	/* Set error conditions for form fields */
881	setErrorState: function( field, msg ) {
882		var self = this,
883			config = self.config,
884			$input = $( field ).closest( ".j-input" ),
885			elem;
886
887		// Error exists
888		self.errorExists = true;
889
890		/* Remove class "j-success-view" */
891		$input.toggleClass( "j-success-view", false );
892
893		// Add class "j-error-view"
894		if ( config.error.highlight ) {
895			$input.addClass( "j-error-view" );
896		}
897
898		// Show errors in tooltips
899		if ( config.error.inTooltip ) {
900			elem = $( "<span />", {
901				class: "j-tooltip j-error-view " + config.error.inTooltipClass,
902				text: msg
903			});
904			$input.append( elem );
905		}
906
907		// Show errors under appropriate fields
908		if ( config.error.underField ) {
909			elem = $( "<span />", {
910				class: "j-error-view",
911				text: msg
912			});
913			$input.append( elem );
914		}
915
916		// Add error message into an array
917		// For showing into error block
918		if ( config.error.inBlock ) {
919			var fieldName = $( field ).attr( "name" );
920			self.errorMessages[fieldName] = msg;
921		}
922	},
923
924	/* Set success conditions for form fields */
925	setSuccessState: function( field ) {
926		var self = this,
927			$input = $( field ).closest( ".j-input" );
928
929		// Add class "j-success-view"
930		if ( self.config.success.highlight ) {
931			$input.toggleClass( "j-success-view", true );
932		}
933	},
934
935	/* Init total form data */
936	initTotalData: function() {
937		var self = this;
938
939		self.$totalDataBlock = self.$form.find( ".j-total-data" );
940		if ( !self.$totalDataBlock.length ){
941			self.config.debugArr.push( self.config.dict.totalDataBlockError );
942		}
943	},
944
945	/* Total form data processing */
946	processingTotalData: function() {
947		var self = this,
948			$formObj = self.$formObj,
949			$totalDataBlock = self.$totalDataBlock,
950			$totalDataList, $totalDataValue, $fieldName, $elem;
951
952		// List of fields to insert data in
953		$totalDataList = $totalDataBlock.find("span[data-field]");
954
955		$.each( $totalDataList, function(){
956
957			// Get form field with the name
958			$fieldName = $( this ).attr("data-field");
959			$elem = self.$form.find( "[name='"+$fieldName+"']" );
960
961			if ( $elem.length ) {
962
963				if ( $formObj[$fieldName] === undefined || $formObj[$fieldName] === null ){
964					return;
965				}
966
967				// Add event listener
968				$elem.on("change", (function(obj, name){
969					return function(){
970						$totalDataValue = _getValue( obj[name] );
971						$totalDataBlock.find( "span[data-field='"+name+"']" ).html( $totalDataValue );
972					};
973				})($formObj, $fieldName));
974			}
975		});
976	},
977
978	/* Init multistep form */
979	initMultistepForm: function() {
980		var self = this,
981			$form = self.$form;
982
983		self.$nextBtn = $form.find( ".j-multi-next-btn" );
984		self.$prevBtn = $form.find( ".j-multi-prev-btn" );
985		self.$stepTitleList = $form.find( ".j-step-title" );
986		self.$contentBlock = $form.children( ".j-content" );
987		self.$footerBlock = $form.children( ".j-footer" );
988		self.$stepList = self.$contentBlock.children( "fieldset" );
989
990		self.createMultistepForm();
991		self.processingMultistepForm();
992	},
993
994	/* Make a layout for multistep form */
995	createMultistepForm: function() {
996		var self = this,
997			$form = self.$form,
998			$stepTitleBlock, $elem, $elems, $elemsLen, $tempStep, $tempStepList, i;
999
1000		// Add class "j-multistep"
1001		$form.toggleClass( "j-multistep", true );
1002
1003		// If a form doesn't have "next" button
1004		if ( !self.$nextBtn.length ) {
1005			$elem = $("<button />", {
1006				type: "button",
1007				class: "j-primary-btn j-multi-next-btn",
1008				text: self.config.dict.multistepNextBtn
1009			});
1010			if ( self.$footerBlock.length ) {
1011				self.$footerBlock.append( $elem );
1012			} else {
1013				self.$contentBlock.append( $elem );
1014			}
1015			self.$nextBtn = self.$form.find( ".j-multi-next-btn" );
1016		}
1017
1018		// If a form doesn't have "previous" button
1019		if ( !self.$prevBtn.length ) {
1020			$elem = $("<button />", {
1021				type: "button",
1022				class: "j-secondary-btn j-multi-prev-btn",
1023				text: self.config.dict.multistepPrevBtn
1024			});
1025			if ( self.$footerBlock.length ) {
1026				self.$footerBlock.append( $elem );
1027			} else {
1028				self.$contentBlock.append( $elem );
1029			}
1030			self.$prevBtn = self.$form.find( ".j-multi-prev-btn" );
1031		}
1032
1033		// If a form doesn't have fieldsets
1034		if ( !self.$stepList.length ) {
1035
1036			// Find form fields
1037			$elems = self.$contentBlock.children( ".j-unit, .j-row" ).filter(":not(.j-step-titles)");
1038			$stepTitleBlock = self.$contentBlock.children( ".j-step-titles" );
1039
1040			$elemsLen = $elems.length;
1041
1042			// Cerate steps for every set of fields
1043			for ( i=0; i<$elemsLen; i+=3 ) {
1044
1045				// Add fields to a step
1046				$tempStep = $( "<fieldset />" ).append( $elems.slice( i, i+3 ) );
1047
1048				// Add step to a form
1049				$tempStepList = self.$contentBlock.children( "fieldset" );
1050				if ( !$tempStepList.length ){
1051					if ( !$stepTitleBlock.length ){
1052						self.$contentBlock.prepend( $tempStep );
1053						continue;
1054					}
1055					$tempStep.insertAfter( $stepTitleBlock );
1056					continue;
1057				}
1058				$tempStep.insertAfter( $tempStepList.last() );
1059			}
1060			self.$stepList = self.$contentBlock.children( "fieldset" );
1061		}
1062	},
1063
1064	/* Multistep form processing */
1065	processingMultistepForm: function() {
1066		var self = this,
1067			config = self.config,
1068			$stepList = self.$stepList,
1069			$nextBtn = self.$nextBtn,
1070			$prevBtn = self.$prevBtn,
1071			$submitBtn = self.$submitBtn,
1072			$stepTitleList = self.$stepTitleList,
1073			len = self.$stepList.length;
1074
1075		// If multistep form has one step only
1076		if ( len === 1 ){
1077			config.debugArr.push( config.dict.multistepOneStepError );
1078			$stepList.eq( 0 ).addClass( "j-active-fieldset" );
1079			return false;
1080		}
1081
1082		// Processing of the first fieldset
1083		$stepList.eq( 0 ).addClass( "j-active-fieldset" );
1084		$submitBtn.addClass( "j-hiddenBtn" );
1085		$prevBtn.addClass( "j-hiddenBtn" );
1086
1087		// Set active step title
1088		if ( $stepTitleList.length ){
1089			$stepTitleList.eq(0).addClass( "j-active-step" );
1090		}
1091
1092		// Click on the "next" button
1093		$nextBtn.on( "click", function( e ){
1094			if ( e ) {
1095				e.preventDefault();
1096				e.stopPropagation();
1097			}
1098
1099			// Clear a form
1100			self.clearForm();
1101
1102			// Form validation
1103			self.validateForm();
1104
1105			// If errors don"t exist
1106			if ( !self.errorExists ) {
1107
1108				// Switch the "active" class to the next fieldset
1109				$stepList.filter( ".j-active-fieldset" ).removeClass( "j-active-fieldset" ).
1110					next( "fieldset" ).addClass( "j-active-fieldset" );
1111
1112				// Switch the "active" class to the next step
1113				if ( $stepTitleList.length ) {
1114					$stepTitleList.filter( ".j-active-step" ).removeClass( "j-active-step" ).
1115					addClass( "j-passed-step" ).next( ".j-step-title" ).addClass( "j-active-step" );
1116				}
1117
1118				// Display "prev" button
1119				$prevBtn.removeClass( "j-hiddenBtn" );
1120
1121				// If active fieldset is a last
1122				// processing the buttons
1123				if ( $stepList.eq( len-1 ).hasClass( "j-active-fieldset" ) ) {
1124					$submitBtn.removeClass( "j-hiddenBtn" );
1125					$nextBtn.addClass( "j-hiddenBtn" );
1126				}
1127			
1128			// If current fieldset has validation errors
1129			} else {
1130				// Scroll to first element with error
1131				if ( config.error.scroll ) {
1132					self.scrollToError();
1133				}
1134				// Show errors within an error block
1135				if ( config.error.inBlock ) {
1136					self.createErrorBlock();
1137				}
1138				return false;
1139			}
1140		});
1141
1142		// Click on the "prev" button
1143		$prevBtn.on( "click", function( e ){
1144			if ( e ) {
1145				e.preventDefault();
1146				e.stopPropagation();
1147			}
1148
1149			// Clear a form
1150			self.clearForm();
1151
1152			// Switch the "active" class to the previous fieldset
1153			$stepList.filter( ".j-active-fieldset" ).removeClass( "j-active-fieldset" ).
1154				prev( "fieldset" ).addClass( "j-active-fieldset" );
1155
1156
1157			// Switch the "active" class to the next step
1158			if ( $stepTitleList.length ) {
1159				$stepTitleList.filter( ".j-active-step" ).removeClass( "j-active-step" ).
1160					prev( ".j-step-title" ).removeClass( "j-passed-step" ).addClass( "j-active-step" );
1161			}
1162
1163			// If active fieldset is a first
1164			// processing the buttons
1165			if ( $stepList.eq(0).hasClass( "j-active-fieldset" ) ) {
1166				$prevBtn.addClass( "j-hiddenBtn" );
1167			}
1168
1169			// If active fieldset is a penultimate
1170			// processing the buttons
1171			if ( $stepList.eq( len-2 ).hasClass( "j-active-fieldset" ) ) {
1172				$submitBtn.addClass( "j-hiddenBtn" );
1173				$nextBtn.removeClass( "j-hiddenBtn" );
1174			}
1175
1176			// Get fields from active step
1177			self.getActiveStepField();
1178		});
1179	},
1180
1181	/* Get fields from active step only */
1182	getActiveStepField: function() {
1183		var self = this,
1184			config = self.config,
1185			$form = self.$form,
1186			$el;
1187
1188		if ( config.formType.multistep ) {
1189			$.each( config.rules, function( name, rules ) {
1190				$el = $form.find( "fieldset.j-active-fieldset [name='"+name+"']" );
1191				if ( $el.length ) {
1192					self.$formObjActiveStep[name] = $el;
1193				}
1194			});
1195		}
1196	},
1197
1198	/* Init modal form */
1199	initModalForm: function() {
1200		var self = this,
1201			$form = self.$form;
1202
1203		self.$modalWrap  = $form.closest( ".j-modal-form" );
1204		self.$modalClose = $form.find( ".j-modal-close" );
1205		self.$modalOpen  = $( document ).find( ".j-modal-open" );
1206
1207		// If modal links dont exist
1208		if ( !self.$modalOpen.length ) {
1209			self.config.debugArr.push( self.config.dict.modalOpenError );
1210			return false;
1211		}
1212
1213		self.createModalForm();
1214		self.processingModalForm();
1215	},
1216
1217	/* Make a layout for modal form */
1218	createModalForm: function() {
1219		var self = this,
1220			$form = self.$form,
1221			$elem, $formWrap;
1222
1223		// If a form doesn't have modal wrapper
1224		if ( !self.$modalWrap.length ) {
1225			$formWrap = $form.closest( ".j-wrapper" );
1226
1227			// Modal wrapper
1228			$elem = $( "<div />", {
1229				class: "j-modal-form",
1230				id: "j-modalwrap-" + $form.attr( "id" )
1231			});
1232
1233			// Wrap a form with modal wrapper
1234			$formWrap.wrap( $elem );
1235			self.$modalWrap = $form.closest( ".j-modal-form" );
1236		}
1237
1238		// If a form doesn't have modal close button
1239		if ( !self.$modalClose.length ) {
1240
1241			// Close button
1242			$elem = $( "<label />", {
1243				class: "j-modal-close"
1244			}).append( "<i></i>" );
1245
1246			// Add close modal button to form
1247			$form.append( $elem );
1248			self.$modalClose = $form.find( ".j-modal-close" );
1249		}
1250	},
1251
1252	/* Modal form processing */
1253	processingModalForm: function() {
1254		var self = this;
1255
1256		// Modal links processing
1257		self.$modalOpen.each( function(){
1258			$( this ).on( "click", function( e ){
1259				if ( e ) {
1260					e.preventDefault();
1261					e.stopPropagation();
1262				}
1263
1264				// Show modal form for approriate modal link
1265				if ( $( this ).data( "modal-wrap" ) === self.$modalWrap.attr( "id" ) ) {
1266					self.$modalWrap.addClass( "j-modal-visible" );
1267					$( "body" ).addClass( "j-modal-scroll" );
1268				}
1269			});
1270		});
1271
1272		// Close button processing
1273		self.$modalClose.on( "click", function(){
1274			self.$modalWrap.removeClass( "j-modal-visible" );
1275			$( "body" ).removeClass( "j-modal-scroll" );
1276		});
1277	},
1278
1279	/* Init hide form feature */
1280	initHideForm: function() {
1281		var self = this;
1282
1283		self.$finalBlock = $( document ).find( ".j-final-block" );
1284		self.$finalBlockCloseBtn = self.$finalBlock.find( ".j-close" );
1285
1286		self.createHideForm();
1287	},
1288
1289	/* Make a layout for final block after form hidding */
1290	createHideForm: function() {
1291		var self = this,
1292			config = self.config,
1293			$form = self.$form,
1294			$elem;
1295
1296		// If final block doesn't exist
1297		if ( !self.$finalBlock.length ) {
1298			$elem = $("<div />", {
1299				class: "j-final-block j-hidden"
1300			});
1301			$form.after( $elem.html( config.dict.successBlockText ) );
1302			self.$finalBlock = $form.siblings( ".j-final-block" );
1303		}
1304
1305		// If "Close" btn doesn't exist
1306		if ( !self.$finalBlockCloseBtn.length ) {
1307			if ( config.formHide.closeBtn ) {
1308				$elem = $( "<label />", {
1309					class: "j-close"
1310				}).append( "<i></i>" );
1311				self.$finalBlock.append( $elem );
1312				self.$finalBlockCloseBtn = self.$finalBlock.find( ".j-close" );
1313			}
1314		} else {
1315			if ( !config.formHide.closeBtn ) {
1316				self.$finalBlockCloseBtn.addClass( "j-hidden" );
1317			}
1318		}
1319	},
1320
1321	/* Hide form processing */
1322	processingHideForm: function() {
1323		var self = this,
1324			$finalBlock = self.$finalBlock,
1325			$finalBlockCloseBtn = self.$finalBlockCloseBtn;
1326
1327		self.$form.addClass( "j-hidden" );
1328		$finalBlock.removeClass( "j-hidden" );
1329
1330		if ( $finalBlockCloseBtn.length ) {
1331			$finalBlockCloseBtn.on("click", function() {
1332				if ( self.config.formType.modal ) {
1333
1334					var $modalForm = $( document ).find( "div.j-modal-form" );
1335					if ( $modalForm.hasClass( "j-modal-visible" ) ) {
1336						$modalForm.removeClass( "j-modal-visible" );
1337						$( "body" ).removeClass( "j-modal-scroll" );
1338					}
1339				} else {
1340					$finalBlock.addClass( "j-hidden" );
1341				}
1342			});
1343		}
1344	},
1345
1346	/* Reset form */
1347	resetForm: function() {
1348		var self = this,
1349			config = self.config,
1350			$input, $totalDataList;
1351
1352		// Reset a form
1353		self.form.reset();
1354
1355		// Remove success condition from the fields
1356		if ( config.success.highlight ){
1357			$input = self.$form.find( ".j-input" );
1358			$input.each( function(){
1359				$( this ).toggleClass( "j-success-view", false );
1360			});
1361		}
1362
1363		// Clear total form data block
1364		if ( config.formTotalData ){
1365			$totalDataList = self.$totalDataBlock.find("span[data-field]");
1366			$.each( $totalDataList, function(){
1367				$( this ).html( "" );
1368			});
1369		}
1370	},
1371
1372	/* Redirect form */
1373	redirectForm: function() {
1374		var self = this;
1375
1376		$( location ).attr( "href", self.config.formRedirect.address );
1377	},
1378
1379	/* Scroll to first element with error */
1380	scrollToError: function() {
1381		var self = this,
1382			$form = self.$form,
1383			$err = $form.find( ".j-error-view" ).eq(0),
1384			$modalWrap, $wrapper, $offset;
1385
1386		if ( !$err.length ) {
1387			return false;
1388		}
1389
1390		// Scroll modal form
1391		if ( self.config.formType.modal ){
1392
1393			$modalWrap = $( document ).find( "#j-modalwrap-"+$form.attr("id") );
1394			$wrapper = $( document ).find(".j-wrapper");
1395			$offset = $wrapper.offset().top - $err.offset().top;
1396
1397			$modalWrap.animate({
1398				scrollTop: -$offset - 60
1399			}, 500);
1400			return false;
1401		}
1402
1403		// Scroll not modal form
1404		$("html, body").animate({
1405			scrollTop: $err.offset().top - 60
1406		}, 500);
1407	},
1408
1409	/* Create a block with error messages */
1410	createErrorBlock: function() {
1411		var self = this,
1412			$ul = $( "<ul></ul>" ),
1413			elems = false,
1414			$formResponseBlock = self.$formResponseBlock,
1415			$li;
1416
1417		// If error object contains error messages
1418		$.each( self.errorMessages, function( key, val ) {
1419			if ( val !== null ) {
1420				elems = true;
1421				return false;
1422			}
1423		});
1424
1425		if ( elems ) {
1426
1427			// Add class "j-error-message"
1428			$formResponseBlock.toggleClass( "j-error-message j-unit", true );
1429			$formResponseBlock.text( self.config.dict.errorBlockText ).append( $ul );
1430
1431			$.each( self.errorMessages, function( name, mes ) {
1432				if ( mes !== null ) {
1433					$li = $( "<li></li>" ).text( mes );
1434					$ul.append( $li );
1435				}
1436			});
1437		} else {
1438			self.clearBlock();
1439		}
1440	},
1441
1442	/* Create a block with success message */
1443	createSuccessBlock: function( msg ) {
1444		var self = this,
1445			config = self.config,
1446			$submitBtn = self.$submitBtn,
1447			$formResponseBlock = self.$formResponseBlock,
1448			$recaptcha = self.$form.find(".g-recaptcha");
1449
1450		// Clear response block
1451		if ( $formResponseBlock.hasClass( "j-error-message" ) ) {
1452			self.clearBlock();
1453		}
1454
1455		// Add success message
1456		$formResponseBlock.addClass( "j-success-message j-unit" ).text( msg );
1457
1458		// Button enabled
1459		$submitBtn.removeClass( "j-processing" );
1460
1461		// If repeated form submission is not allowed
1462		if ( !config.repeatSubmission ) {
1463
1464			// Disable submit button
1465			$submitBtn.addClass( "j-disabled-view" ).html( config.dict.disabledBtn );
1466		}
1467
1468		setTimeout(function(){
1469
1470			self.clearBlock();
1471
1472			// Google reCaptcha reloading
1473			if ( $recaptcha.length ) {
1474				grecaptcha.reset();
1475			}
1476
1477			// If repeated form submission is allowed
1478			if ( config.repeatSubmission ) {
1479				$submitBtn.removeAttr( "disabled" );
1480			}
1481
1482			// Modal form processing
1483			if ( config.formType.modal ) {
1484				$( "div.j-modal-form" ).removeClass( "j-modal-visible" );
1485				$( "body" ).removeClass( "j-modal-scroll" );
1486			}
1487
1488			// Multistep form processing
1489			if ( config.formType.multistep ) {
1490
1491				// Buttons processing
1492				self.$prevBtn.removeAttr( "disabled" ).addClass( "j-hiddenBtn" );
1493				self.$nextBtn.removeClass( "j-hiddenBtn" );
1494				$submitBtn.addClass( "j-hiddenBtn" );
1495
1496				// Steps processing
1497				self.$stepList.removeClass( "j-active-fieldset" ).eq( 0 ).addClass( "j-active-fieldset" );
1498
1499				// Step titles processing
1500				if ( self.$stepTitleList.length ){
1501					self.$stepTitleList.removeClass('j-active-step j-passed-step').eq(0).addClass('j-active-step');
1502				}
1503			}
1504		}, config.timeoutSuccessMsg);
1505	},
1506
1507	/* Delete all error messages, all "error" CSS classes */
1508	clearForm: function() {
1509		var self = this,
1510			$form = self.$form,
1511			config = self.config,
1512			$spanErr, $tooltipErr, $input;
1513
1514		// Clear error variables
1515		self.errorExists = false;
1516		self.errorMessages = {};
1517
1518		// Delete error messages under the fields
1519		if ( config.error.underField ) {
1520			$spanErr = $form.find("span.j-error-view");
1521			$spanErr.each(function(){
1522				$( this ).remove();
1523			});
1524		}
1525
1526		// Delete error messages from an error block
1527		if ( config.error.inBlock ) {
1528			self.clearBlock();
1529		}
1530
1531		// Remove error condition from the fields
1532		if ( config.error.highlight ) {
1533			$input = $form.find( ".j-input" );
1534			$input.each( function(){
1535				$( this ).toggleClass( "j-error-view", false );
1536			});
1537		}
1538
1539		// Delete tooltip error messages
1540		if ( config.error.inTooltip ) {
1541			$tooltipErr = $form.find( "span.j-tooltip.j-error-view" );
1542			$tooltipErr.each(function(){
1543				$( this ).remove();
1544			});
1545		}
1546	},
1547
1548	/* Delete field's error message, field's "error" CSS classes */
1549	clearField: function( field ) {
1550		var self = this,
1551			config = self.config,
1552			errorMessages = self.errorMessages,
1553			fieldName = $( field ).attr("name"),
1554			$input, $span, $tooltip;
1555
1556		// If a form does not have a field
1557		if ( self.$formObj[fieldName] === undefined || self.$formObj[fieldName] === null ){
1558			return false;
1559		}
1560
1561		self.errorExists = false;
1562		$input = $( field ).closest( ".j-input" );
1563
1564		// Delete error messages under the fields
1565		if ( config.error.underField ){
1566			$span = $input.find( "span.j-error-view" );
1567			if ( $span.length ) {
1568				$span.remove();
1569			}
1570		}
1571
1572		// Remove error condition from the fields
1573		if ( config.error.highlight ) {
1574			$input.toggleClass( "j-error-view", false );
1575		}
1576
1577		// Delete tooltip error messages
1578		if ( config.error.inTooltip ) {
1579			$tooltip = $input.find( "span.j-tooltip.j-error-view" );
1580			if ( $tooltip.length ) {
1581				$tooltip.remove();
1582			}
1583		}
1584
1585		// Delete error message from error object
1586		if ( config.error.inBlock ){
1587			if ( errorMessages[fieldName] !== undefined && errorMessages[fieldName] !== null ) {
1588				errorMessages[fieldName] = null;
1589			}
1590		}
1591	},
1592
1593	/* Clear a block with validation result */
1594	clearBlock: function() {
1595		var self = this;
1596
1597		self.$formResponseBlock.removeClass( "j-error-message j-success-message j-unit" ).html( "" );
1598	},
1599};
1600
1601$.fn.justFormsPro = function( options ){
1602	new JustFormsPro( this, options );
1603	return this;
1604};
1605}( jQuery ));

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.