Dashboard sipadu mbip
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

jquery.j-pro.js 42KB

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