jquery.validationEngine.js 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066
  1. /*
  2. * Inline Form Validation Engine 2.6.2, jQuery plugin
  3. *
  4. * Copyright(c) 2010, Cedric Dugas
  5. * http://www.position-absolute.com
  6. *
  7. * 2.0 Rewrite by Olivier Refalo
  8. * http://www.crionics.com
  9. *
  10. * Form validation engine allowing custom regex rules to be added.
  11. * Licensed under the MIT License
  12. */
  13. (function($) {
  14. "use strict";
  15. var methods = {
  16. /**
  17. * Kind of the constructor, called before any action
  18. * @param {Map} user options
  19. */
  20. init: function(options) {
  21. var form = this;
  22. if (!form.data('jqv') || form.data('jqv') == null ) {
  23. options = methods._saveOptions(form, options);
  24. // bind all formError elements to close on click
  25. $(document).on("click", ".formError", function() {
  26. $(this).fadeOut(150, function() {
  27. // remove prompt once invisible
  28. $(this).parent('.formErrorOuter').remove();
  29. $(this).remove();
  30. });
  31. });
  32. }
  33. return this;
  34. },
  35. /**
  36. * Attachs jQuery.validationEngine to form.submit and field.blur events
  37. * Takes an optional params: a list of options
  38. * ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"});
  39. */
  40. attach: function(userOptions) {
  41. var form = this;
  42. var options;
  43. if(userOptions)
  44. options = methods._saveOptions(form, userOptions);
  45. else
  46. options = form.data('jqv');
  47. options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class";
  48. if (options.binded) {
  49. // delegate fields
  50. form.on(options.validationEventTrigger, "["+options.validateAttribute+"*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent);
  51. form.on("click", "["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]", methods._onFieldEvent);
  52. form.on(options.validationEventTrigger,"["+options.validateAttribute+"*=validate][class*=datepicker]", {"delay": 300}, methods._onFieldEvent);
  53. }
  54. if (options.autoPositionUpdate) {
  55. $(window).bind("resize", {
  56. "noAnimation": true,
  57. "formElem": form
  58. }, methods.updatePromptsPosition);
  59. }
  60. form.on("click","a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
  61. form.removeData('jqv_submitButton');
  62. // bind form.submit
  63. form.on("submit", methods._onSubmitEvent);
  64. return this;
  65. },
  66. /**
  67. * Unregisters any bindings that may point to jQuery.validaitonEngine
  68. */
  69. detach: function() {
  70. var form = this;
  71. var options = form.data('jqv');
  72. // unbind fields
  73. form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").off(options.validationEventTrigger, methods._onFieldEvent);
  74. form.find("["+options.validateAttribute+"*=validate][type=checkbox],[class*=validate][type=radio]").off("click", methods._onFieldEvent);
  75. // unbind form.submit
  76. form.off("submit", methods._onSubmitEvent);
  77. form.removeData('jqv');
  78. form.off("click", "a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
  79. form.removeData('jqv_submitButton');
  80. if (options.autoPositionUpdate)
  81. $(window).off("resize", methods.updatePromptsPosition);
  82. return this;
  83. },
  84. /**
  85. * Validates either a form or a list of fields, shows prompts accordingly.
  86. * Note: There is no ajax form validation with this method, only field ajax validation are evaluated
  87. *
  88. * @return true if the form validates, false if it fails
  89. */
  90. validate: function() {
  91. var element = $(this);
  92. var valid = null;
  93. if (element.is("form") || element.hasClass("validationEngineContainer")) {
  94. if (element.hasClass('validating')) {
  95. // form is already validating.
  96. // Should abort old validation and start new one. I don't know how to implement it.
  97. return false;
  98. } else {
  99. element.addClass('validating');
  100. var options = element.data('jqv');
  101. var valid = methods._validateFields(this);
  102. // If the form doesn't validate, clear the 'validating' class before the user has a chance to submit again
  103. setTimeout(function(){
  104. element.removeClass('validating');
  105. }, 100);
  106. if (valid && options.onSuccess) {
  107. options.onSuccess();
  108. } else if (!valid && options.onFailure) {
  109. options.onFailure();
  110. }
  111. }
  112. } else if (element.is('form') || element.hasClass('validationEngineContainer')) {
  113. element.removeClass('validating');
  114. } else {
  115. // field validation
  116. var form = element.closest('form, .validationEngineContainer'),
  117. options = (form.data('jqv')) ? form.data('jqv') : $.validationEngine.defaults,
  118. valid = methods._validateField(element, options);
  119. if (valid && options.onFieldSuccess)
  120. options.onFieldSuccess();
  121. else if (options.onFieldFailure && options.InvalidFields.length > 0) {
  122. options.onFieldFailure();
  123. }
  124. }
  125. if(options.onValidationComplete) {
  126. // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
  127. return !!options.onValidationComplete(form, valid);
  128. }
  129. return valid;
  130. },
  131. /**
  132. * Redraw prompts position, useful when you change the DOM state when validating
  133. */
  134. updatePromptsPosition: function(event) {
  135. if (event && this == window) {
  136. var form = event.data.formElem;
  137. var noAnimation = event.data.noAnimation;
  138. }
  139. else
  140. var form = $(this.closest('form, .validationEngineContainer'));
  141. var options = form.data('jqv');
  142. // No option, take default one
  143. form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each(function(){
  144. var field = $(this);
  145. if (options.prettySelect && field.is(":hidden"))
  146. field = form.find("#" + options.usePrefix + field.attr('id') + options.useSuffix);
  147. var prompt = methods._getPrompt(field);
  148. var promptText = $(prompt).find(".formErrorContent").html();
  149. if(prompt)
  150. methods._updatePrompt(field, $(prompt), promptText, undefined, false, options, noAnimation);
  151. });
  152. return this;
  153. },
  154. /**
  155. * Displays a prompt on a element.
  156. * Note that the element needs an id!
  157. *
  158. * @param {String} promptText html text to display type
  159. * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  160. * @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight
  161. */
  162. showPrompt: function(promptText, type, promptPosition, showArrow) {
  163. var form = this.closest('form, .validationEngineContainer');
  164. var options = form.data('jqv');
  165. // No option, take default one
  166. if(!options)
  167. options = methods._saveOptions(this, options);
  168. if(promptPosition)
  169. options.promptPosition=promptPosition;
  170. options.showArrow = showArrow==true;
  171. methods._showPrompt(this, promptText, type, false, options);
  172. return this;
  173. },
  174. /**
  175. * Closes form error prompts, CAN be invidual
  176. */
  177. hide: function() {
  178. var form = $(this).closest('form, .validationEngineContainer');
  179. var options = form.data('jqv');
  180. var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3;
  181. var closingtag;
  182. if($(this).is("form") || $(this).hasClass("validationEngineContainer")) {
  183. closingtag = "parentForm"+methods._getClassName($(this).attr("id"));
  184. } else {
  185. closingtag = methods._getClassName($(this).attr("id")) +"formError";
  186. }
  187. $('.'+closingtag).fadeTo(fadeDuration, 0.3, function() {
  188. $(this).parent('.formErrorOuter').remove();
  189. $(this).remove();
  190. });
  191. return this;
  192. },
  193. /**
  194. * Closes all error prompts on the page
  195. */
  196. hideAll: function() {
  197. var form = this;
  198. var options = form.data('jqv');
  199. var duration = options ? options.fadeDuration:300;
  200. $('.formError').fadeTo(duration, 300, function() {
  201. $(this).parent('.formErrorOuter').remove();
  202. $(this).remove();
  203. });
  204. return this;
  205. },
  206. /**
  207. * Typically called when user exists a field using tab or a mouse click, triggers a field
  208. * validation
  209. */
  210. _onFieldEvent: function(event) {
  211. var field = $(this);
  212. var form = field.closest('form, .validationEngineContainer');
  213. var options = form.data('jqv');
  214. options.eventTrigger = "field";
  215. // validate the current field
  216. window.setTimeout(function() {
  217. methods._validateField(field, options);
  218. if (options.InvalidFields.length == 0 && options.onFieldSuccess) {
  219. options.onFieldSuccess();
  220. } else if (options.InvalidFields.length > 0 && options.onFieldFailure) {
  221. options.onFieldFailure();
  222. }
  223. }, (event.data) ? event.data.delay : 0);
  224. },
  225. /**
  226. * Called when the form is submited, shows prompts accordingly
  227. *
  228. * @param {jqObject}
  229. * form
  230. * @return false if form submission needs to be cancelled
  231. */
  232. _onSubmitEvent: function() {
  233. var form = $(this);
  234. var options = form.data('jqv');
  235. //check if it is trigger from skipped button
  236. if (form.data("jqv_submitButton")){
  237. var submitButton = $("#" + form.data("jqv_submitButton"));
  238. if (submitButton){
  239. if (submitButton.length > 0){
  240. if (submitButton.hasClass("validate-skip") || submitButton.attr("data-validation-engine-skip") == "true")
  241. return true;
  242. }
  243. }
  244. }
  245. options.eventTrigger = "submit";
  246. // validate each field
  247. // (- skip field ajax validation, not necessary IF we will perform an ajax form validation)
  248. var r=methods._validateFields(form);
  249. if (r && options.ajaxFormValidation) {
  250. methods._validateFormWithAjax(form, options);
  251. // cancel form auto-submission - process with async call onAjaxFormComplete
  252. return false;
  253. }
  254. if(options.onValidationComplete) {
  255. // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
  256. return !!options.onValidationComplete(form, r);
  257. }
  258. return r;
  259. },
  260. /**
  261. * Return true if the ajax field validations passed so far
  262. * @param {Object} options
  263. * @return true, is all ajax validation passed so far (remember ajax is async)
  264. */
  265. _checkAjaxStatus: function(options) {
  266. var status = true;
  267. $.each(options.ajaxValidCache, function(key, value) {
  268. if (!value) {
  269. status = false;
  270. // break the each
  271. return false;
  272. }
  273. });
  274. return status;
  275. },
  276. /**
  277. * Return true if the ajax field is validated
  278. * @param {String} fieldid
  279. * @param {Object} options
  280. * @return true, if validation passed, false if false or doesn't exist
  281. */
  282. _checkAjaxFieldStatus: function(fieldid, options) {
  283. return options.ajaxValidCache[fieldid] == true;
  284. },
  285. /**
  286. * Validates form fields, shows prompts accordingly
  287. *
  288. * @param {jqObject}
  289. * form
  290. * @param {skipAjaxFieldValidation}
  291. * boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked
  292. *
  293. * @return true if form is valid, false if not, undefined if ajax form validation is done
  294. */
  295. _validateFields: function(form) {
  296. var options = form.data('jqv');
  297. // this variable is set to true if an error is found
  298. var errorFound = false;
  299. // Trigger hook, start validation
  300. form.trigger("jqv.form.validating");
  301. // first, evaluate status of non ajax fields
  302. var first_err=null;
  303. form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each( function() {
  304. var field = $(this);
  305. var names = [];
  306. if ($.inArray(field.attr('name'), names) < 0) {
  307. errorFound |= methods._validateField(field, options);
  308. if (errorFound && first_err==null)
  309. if (field.is(":hidden") && options.prettySelect)
  310. first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
  311. else {
  312. //Check if we need to adjust what element to show the prompt on
  313. //and and such scroll to instead
  314. if(field.data('jqv-prompt-at') instanceof jQuery ){
  315. field = field.data('jqv-prompt-at');
  316. } else if(field.data('jqv-prompt-at')) {
  317. field = $(field.data('jqv-prompt-at'));
  318. }
  319. first_err=field;
  320. }
  321. if (options.doNotShowAllErrosOnSubmit)
  322. return false;
  323. names.push(field.attr('name'));
  324. //if option set, stop checking validation rules after one error is found
  325. if(options.showOneMessage == true && errorFound){
  326. return false;
  327. }
  328. }
  329. });
  330. // second, check to see if all ajax calls completed ok
  331. // errorFound |= !methods._checkAjaxStatus(options);
  332. // third, check status and scroll the container accordingly
  333. form.trigger("jqv.form.result", [errorFound]);
  334. if (errorFound) {
  335. if (options.scroll) {
  336. var destination=first_err.offset().top;
  337. var fixleft = first_err.offset().left;
  338. //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
  339. var positionType=options.promptPosition;
  340. if (typeof(positionType)=='string' && positionType.indexOf(":")!=-1)
  341. positionType=positionType.substring(0,positionType.indexOf(":"));
  342. if (positionType!="bottomRight" && positionType!="bottomLeft") {
  343. var prompt_err= methods._getPrompt(first_err);
  344. if (prompt_err) {
  345. destination=prompt_err.offset().top;
  346. }
  347. }
  348. // Offset the amount the page scrolls by an amount in px to accomodate fixed elements at top of page
  349. if (options.scrollOffset) {
  350. destination -= options.scrollOffset;
  351. }
  352. // get the position of the first error, there should be at least one, no need to check this
  353. //var destination = form.find(".formError:not('.greenPopup'):first").offset().top;
  354. if (options.isOverflown) {
  355. var overflowDIV = $(options.overflownDIV);
  356. if(!overflowDIV.length) return false;
  357. var scrollContainerScroll = overflowDIV.scrollTop();
  358. var scrollContainerPos = -parseInt(overflowDIV.offset().top);
  359. destination += scrollContainerScroll + scrollContainerPos - 5;
  360. var scrollContainer = $(options.overflownDIV + ":not(:animated)");
  361. scrollContainer.animate({ scrollTop: destination }, 1100, function(){
  362. if(options.focusFirstField) first_err.focus();
  363. });
  364. } else {
  365. $("html, body").animate({
  366. scrollTop: destination
  367. }, 1100, function(){
  368. if(options.focusFirstField) first_err.focus();
  369. });
  370. $("html, body").animate({scrollLeft: fixleft},1100)
  371. }
  372. } else if(options.focusFirstField)
  373. first_err.focus();
  374. return false;
  375. }
  376. return true;
  377. },
  378. /**
  379. * This method is called to perform an ajax form validation.
  380. * During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true
  381. *
  382. * @param {jqObject} form
  383. * @param {Map} options
  384. */
  385. _validateFormWithAjax: function(form, options) {
  386. var data = form.serialize();
  387. var type = (options.ajaxFormValidationMethod) ? options.ajaxFormValidationMethod : "GET";
  388. var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action");
  389. var dataType = (options.dataType) ? options.dataType : "json";
  390. $.ajax({
  391. type: type,
  392. url: url,
  393. cache: false,
  394. dataType: dataType,
  395. data: data,
  396. form: form,
  397. methods: methods,
  398. options: options,
  399. beforeSend: function() {
  400. return options.onBeforeAjaxFormValidation(form, options);
  401. },
  402. error: function(data, transport) {
  403. methods._ajaxError(data, transport);
  404. },
  405. success: function(json) {
  406. if ((dataType == "json") && (json !== true)) {
  407. // getting to this case doesn't necessary means that the form is invalid
  408. // the server may return green or closing prompt actions
  409. // this flag helps figuring it out
  410. var errorInForm=false;
  411. for (var i = 0; i < json.length; i++) {
  412. var value = json[i];
  413. var errorFieldId = value[0];
  414. var errorField = $($("#" + errorFieldId)[0]);
  415. // make sure we found the element
  416. if (errorField.length == 1) {
  417. // promptText or selector
  418. var msg = value[2];
  419. // if the field is valid
  420. if (value[1] == true) {
  421. if (msg == "" || !msg){
  422. // if for some reason, status==true and error="", just close the prompt
  423. methods._closePrompt(errorField);
  424. } else {
  425. // the field is valid, but we are displaying a green prompt
  426. if (options.allrules[msg]) {
  427. var txt = options.allrules[msg].alertTextOk;
  428. if (txt)
  429. msg = txt;
  430. }
  431. if (options.showPrompts) methods._showPrompt(errorField, msg, "pass", false, options, true);
  432. }
  433. } else {
  434. // the field is invalid, show the red error prompt
  435. errorInForm|=true;
  436. if (options.allrules[msg]) {
  437. var txt = options.allrules[msg].alertText;
  438. if (txt)
  439. msg = txt;
  440. }
  441. if(options.showPrompts) methods._showPrompt(errorField, msg, "", false, options, true);
  442. }
  443. }
  444. }
  445. options.onAjaxFormComplete(!errorInForm, form, json, options);
  446. } else
  447. options.onAjaxFormComplete(true, form, json, options);
  448. }
  449. });
  450. },
  451. /**
  452. * Validates field, shows prompts accordingly
  453. *
  454. * @param {jqObject}
  455. * field
  456. * @param {Array[String]}
  457. * field's validation rules
  458. * @param {Map}
  459. * user options
  460. * @return false if field is valid (It is inversed for *fields*, it return false on validate and true on errors.)
  461. */
  462. _validateField: function(field, options, skipAjaxValidation) {
  463. if (!field.attr("id")) {
  464. field.attr("id", "form-validation-field-" + $.validationEngine.fieldIdCounter);
  465. ++$.validationEngine.fieldIdCounter;
  466. }
  467. if (!options.validateNonVisibleFields && (field.is(":hidden") && !options.prettySelect || field.parent().is(":hidden")))
  468. return false;
  469. var rulesParsing = field.attr(options.validateAttribute);
  470. var getRules = /validate\[(.*)\]/.exec(rulesParsing);
  471. if (!getRules)
  472. return false;
  473. var str = getRules[1];
  474. var rules = str.split(/\[|,|\]/);
  475. // true if we ran the ajax validation, tells the logic to stop messing with prompts
  476. var isAjaxValidator = false;
  477. var fieldName = field.attr("name");
  478. var promptText = "";
  479. var promptType = "";
  480. var required = false;
  481. var limitErrors = false;
  482. options.isError = false;
  483. options.showArrow = true;
  484. // If the programmer wants to limit the amount of error messages per field,
  485. if (options.maxErrorsPerField > 0) {
  486. limitErrors = true;
  487. }
  488. var form = $(field.closest("form, .validationEngineContainer"));
  489. // Fix for adding spaces in the rules
  490. for (var i = 0; i < rules.length; i++) {
  491. rules[i] = rules[i].replace(" ", "");
  492. // Remove any parsing errors
  493. if (rules[i] === '') {
  494. delete rules[i];
  495. }
  496. }
  497. for (var i = 0, field_errors = 0; i < rules.length; i++) {
  498. // If we are limiting errors, and have hit the max, break
  499. if (limitErrors && field_errors >= options.maxErrorsPerField) {
  500. // If we haven't hit a required yet, check to see if there is one in the validation rules for this
  501. // field and that it's index is greater or equal to our current index
  502. if (!required) {
  503. var have_required = $.inArray('required', rules);
  504. required = (have_required != -1 && have_required >= i);
  505. }
  506. break;
  507. }
  508. var errorMsg = undefined;
  509. switch (rules[i]) {
  510. case "required":
  511. required = true;
  512. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required);
  513. break;
  514. case "custom":
  515. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom);
  516. break;
  517. case "groupRequired":
  518. // Check is its the first of group, if not, reload validation with first field
  519. // AND continue normal validation on present field
  520. var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]";
  521. var firstOfGroup = form.find(classGroup).eq(0);
  522. if(firstOfGroup[0] != field[0]){
  523. methods._validateField(firstOfGroup, options, skipAjaxValidation);
  524. options.showArrow = true;
  525. }
  526. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired);
  527. if(errorMsg) required = true;
  528. options.showArrow = false;
  529. break;
  530. case "ajax":
  531. // AJAX defaults to returning it's loading message
  532. errorMsg = methods._ajax(field, rules, i, options);
  533. if (errorMsg) {
  534. promptType = "load";
  535. }
  536. break;
  537. case "minSize":
  538. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize);
  539. break;
  540. case "maxSize":
  541. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize);
  542. break;
  543. case "min":
  544. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min);
  545. break;
  546. case "max":
  547. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max);
  548. break;
  549. case "past":
  550. errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._past);
  551. break;
  552. case "future":
  553. errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._future);
  554. break;
  555. case "dateRange":
  556. var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]";
  557. options.firstOfGroup = form.find(classGroup).eq(0);
  558. options.secondOfGroup = form.find(classGroup).eq(1);
  559. //if one entry out of the pair has value then proceed to run through validation
  560. if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
  561. errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateRange);
  562. }
  563. if (errorMsg) required = true;
  564. options.showArrow = false;
  565. break;
  566. case "dateTimeRange":
  567. var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]";
  568. options.firstOfGroup = form.find(classGroup).eq(0);
  569. options.secondOfGroup = form.find(classGroup).eq(1);
  570. //if one entry out of the pair has value then proceed to run through validation
  571. if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
  572. errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateTimeRange);
  573. }
  574. if (errorMsg) required = true;
  575. options.showArrow = false;
  576. break;
  577. case "maxCheckbox":
  578. field = $(form.find("input[name='" + fieldName + "']"));
  579. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox);
  580. break;
  581. case "minCheckbox":
  582. field = $(form.find("input[name='" + fieldName + "']"));
  583. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox);
  584. break;
  585. case "equals":
  586. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals);
  587. break;
  588. case "funcCall":
  589. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall);
  590. break;
  591. case "creditCard":
  592. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard);
  593. break;
  594. case "condRequired":
  595. errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._condRequired);
  596. if (errorMsg !== undefined) {
  597. required = true;
  598. }
  599. break;
  600. default:
  601. }
  602. var end_validation = false;
  603. // If we were passed back an message object, check what the status was to determine what to do
  604. if (typeof errorMsg == "object") {
  605. switch (errorMsg.status) {
  606. case "_break":
  607. end_validation = true;
  608. break;
  609. // If we have an error message, set errorMsg to the error message
  610. case "_error":
  611. errorMsg = errorMsg.message;
  612. break;
  613. // If we want to throw an error, but not show a prompt, return early with true
  614. case "_error_no_prompt":
  615. return true;
  616. break;
  617. // Anything else we continue on
  618. default:
  619. break;
  620. }
  621. }
  622. // If it has been specified that validation should end now, break
  623. if (end_validation) {
  624. break;
  625. }
  626. // If we have a string, that means that we have an error, so add it to the error message.
  627. if (typeof errorMsg == 'string') {
  628. promptText += errorMsg + "<br/>";
  629. options.isError = true;
  630. field_errors++;
  631. }
  632. }
  633. // If the rules required is not added, an empty field is not validated
  634. if(!required && !(field.val()) && field.val().length < 1) options.isError = false;
  635. // Hack for radio/checkbox group button, the validation go into the
  636. // first radio/checkbox of the group
  637. var fieldType = field.prop("type");
  638. var positionType=field.data("promptPosition") || options.promptPosition;
  639. if ((fieldType == "radio" || fieldType == "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) {
  640. if(positionType === 'inline') {
  641. field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:last"));
  642. } else {
  643. field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first"));
  644. }
  645. options.showArrow = false;
  646. }
  647. if(field.is(":hidden") && options.prettySelect) {
  648. field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
  649. }
  650. if (options.isError && options.showPrompts){
  651. methods._showPrompt(field, promptText, promptType, false, options);
  652. }else{
  653. if (!isAjaxValidator) methods._closePrompt(field);
  654. }
  655. if (!isAjaxValidator) {
  656. field.trigger("jqv.field.result", [field, options.isError, promptText]);
  657. }
  658. /* Record error */
  659. var errindex = $.inArray(field[0], options.InvalidFields);
  660. if (errindex == -1) {
  661. if (options.isError)
  662. options.InvalidFields.push(field[0]);
  663. } else if (!options.isError) {
  664. options.InvalidFields.splice(errindex, 1);
  665. }
  666. methods._handleStatusCssClasses(field, options);
  667. /* run callback function for each field */
  668. if (options.isError && options.onFieldFailure)
  669. options.onFieldFailure(field);
  670. if (!options.isError && options.onFieldSuccess)
  671. options.onFieldSuccess(field);
  672. return options.isError;
  673. },
  674. /**
  675. * Handling css classes of fields indicating result of validation
  676. *
  677. * @param {jqObject}
  678. * field
  679. * @param {Array[String]}
  680. * field's validation rules
  681. * @private
  682. */
  683. _handleStatusCssClasses: function(field, options) {
  684. /* remove all classes */
  685. if(options.addSuccessCssClassToField)
  686. field.removeClass(options.addSuccessCssClassToField);
  687. if(options.addFailureCssClassToField)
  688. field.removeClass(options.addFailureCssClassToField);
  689. /* Add classes */
  690. if (options.addSuccessCssClassToField && !options.isError)
  691. field.addClass(options.addSuccessCssClassToField);
  692. if (options.addFailureCssClassToField && options.isError)
  693. field.addClass(options.addFailureCssClassToField);
  694. },
  695. /********************
  696. * _getErrorMessage
  697. *
  698. * @param form
  699. * @param field
  700. * @param rule
  701. * @param rules
  702. * @param i
  703. * @param options
  704. * @param originalValidationMethod
  705. * @return {*}
  706. * @private
  707. */
  708. _getErrorMessage:function (form, field, rule, rules, i, options, originalValidationMethod) {
  709. // If we are using the custon validation type, build the index for the rule.
  710. // Otherwise if we are doing a function call, make the call and return the object
  711. // that is passed back.
  712. var rule_index = jQuery.inArray(rule, rules);
  713. if (rule === "custom" || rule === "funcCall") {
  714. var custom_validation_type = rules[rule_index + 1];
  715. rule = rule + "[" + custom_validation_type + "]";
  716. // Delete the rule from the rules array so that it doesn't try to call the
  717. // same rule over again
  718. delete(rules[rule_index]);
  719. }
  720. // Change the rule to the composite rule, if it was different from the original
  721. var alteredRule = rule;
  722. var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class");
  723. var element_classes_array = element_classes.split(" ");
  724. // Call the original validation method. If we are dealing with dates or checkboxes, also pass the form
  725. var errorMsg;
  726. if (rule == "future" || rule == "past" || rule == "maxCheckbox" || rule == "minCheckbox") {
  727. errorMsg = originalValidationMethod(form, field, rules, i, options);
  728. } else {
  729. errorMsg = originalValidationMethod(field, rules, i, options);
  730. }
  731. // If the original validation method returned an error and we have a custom error message,
  732. // return the custom message instead. Otherwise return the original error message.
  733. if (errorMsg != undefined) {
  734. var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, alteredRule, options);
  735. if (custom_message) errorMsg = custom_message;
  736. }
  737. return errorMsg;
  738. },
  739. _getCustomErrorMessage:function (field, classes, rule, options) {
  740. var custom_message = false;
  741. var validityProp = /^custom\[.*\]$/.test(rule) ? methods._validityProp["custom"] : methods._validityProp[rule];
  742. // If there is a validityProp for this rule, check to see if the field has an attribute for it
  743. if (validityProp != undefined) {
  744. custom_message = field.attr("data-errormessage-"+validityProp);
  745. // If there was an error message for it, return the message
  746. if (custom_message != undefined)
  747. return custom_message;
  748. }
  749. custom_message = field.attr("data-errormessage");
  750. // If there is an inline custom error message, return it
  751. if (custom_message != undefined)
  752. return custom_message;
  753. var id = '#' + field.attr("id");
  754. // If we have custom messages for the element's id, get the message for the rule from the id.
  755. // Otherwise, if we have custom messages for the element's classes, use the first class message we find instead.
  756. if (typeof options.custom_error_messages[id] != "undefined" &&
  757. typeof options.custom_error_messages[id][rule] != "undefined" ) {
  758. custom_message = options.custom_error_messages[id][rule]['message'];
  759. } else if (classes.length > 0) {
  760. for (var i = 0; i < classes.length && classes.length > 0; i++) {
  761. var element_class = "." + classes[i];
  762. if (typeof options.custom_error_messages[element_class] != "undefined" &&
  763. typeof options.custom_error_messages[element_class][rule] != "undefined") {
  764. custom_message = options.custom_error_messages[element_class][rule]['message'];
  765. break;
  766. }
  767. }
  768. }
  769. if (!custom_message &&
  770. typeof options.custom_error_messages[rule] != "undefined" &&
  771. typeof options.custom_error_messages[rule]['message'] != "undefined"){
  772. custom_message = options.custom_error_messages[rule]['message'];
  773. }
  774. return custom_message;
  775. },
  776. _validityProp: {
  777. "required": "value-missing",
  778. "custom": "custom-error",
  779. "groupRequired": "value-missing",
  780. "ajax": "custom-error",
  781. "minSize": "range-underflow",
  782. "maxSize": "range-overflow",
  783. "min": "range-underflow",
  784. "max": "range-overflow",
  785. "past": "type-mismatch",
  786. "future": "type-mismatch",
  787. "dateRange": "type-mismatch",
  788. "dateTimeRange": "type-mismatch",
  789. "maxCheckbox": "range-overflow",
  790. "minCheckbox": "range-underflow",
  791. "equals": "pattern-mismatch",
  792. "funcCall": "custom-error",
  793. "creditCard": "pattern-mismatch",
  794. "condRequired": "value-missing"
  795. },
  796. /**
  797. * Required validation
  798. *
  799. * @param {jqObject} field
  800. * @param {Array[String]} rules
  801. * @param {int} i rules index
  802. * @param {Map}
  803. * user options
  804. * @param {bool} condRequired flag when method is used for internal purpose in condRequired check
  805. * @return an error string if validation failed
  806. */
  807. _required: function(field, rules, i, options, condRequired) {
  808. switch (field.prop("type")) {
  809. case "text":
  810. case "password":
  811. case "textarea":
  812. case "file":
  813. case "select-one":
  814. case "select-multiple":
  815. default:
  816. var field_val = $.trim( field.val() );
  817. var dv_placeholder = $.trim( field.attr("data-validation-placeholder") );
  818. var placeholder = $.trim( field.attr("placeholder") );
  819. if (
  820. ( !field_val )
  821. || ( dv_placeholder && field_val == dv_placeholder )
  822. || ( placeholder && field_val == placeholder )
  823. ) {
  824. return options.allrules[rules[i]].alertText;
  825. }
  826. break;
  827. case "radio":
  828. case "checkbox":
  829. // new validation style to only check dependent field
  830. if (condRequired) {
  831. if (!field.attr('checked')) {
  832. return options.allrules[rules[i]].alertTextCheckboxMultiple;
  833. }
  834. break;
  835. }
  836. // old validation style
  837. var form = field.closest("form, .validationEngineContainer");
  838. var name = field.attr("name");
  839. if (form.find("input[name='" + name + "']:checked").size() == 0) {
  840. if (form.find("input[name='" + name + "']:visible").size() == 1)
  841. return options.allrules[rules[i]].alertTextCheckboxe;
  842. else
  843. return options.allrules[rules[i]].alertTextCheckboxMultiple;
  844. }
  845. break;
  846. }
  847. },
  848. /**
  849. * Validate that 1 from the group field is required
  850. *
  851. * @param {jqObject} field
  852. * @param {Array[String]} rules
  853. * @param {int} i rules index
  854. * @param {Map}
  855. * user options
  856. * @return an error string if validation failed
  857. */
  858. _groupRequired: function(field, rules, i, options) {
  859. var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]";
  860. var isValid = false;
  861. // Check all fields from the group
  862. field.closest("form, .validationEngineContainer").find(classGroup).each(function(){
  863. if(!methods._required($(this), rules, i, options)){
  864. isValid = true;
  865. return false;
  866. }
  867. });
  868. if(!isValid) {
  869. return options.allrules[rules[i]].alertText;
  870. }
  871. },
  872. /**
  873. * Validate rules
  874. *
  875. * @param {jqObject} field
  876. * @param {Array[String]} rules
  877. * @param {int} i rules index
  878. * @param {Map}
  879. * user options
  880. * @return an error string if validation failed
  881. */
  882. _custom: function(field, rules, i, options) {
  883. var customRule = rules[i + 1];
  884. var rule = options.allrules[customRule];
  885. var fn;
  886. if(!rule) {
  887. alert("jqv:custom rule not found - "+customRule);
  888. return;
  889. }
  890. if(rule["regex"]) {
  891. var ex=rule.regex;
  892. if(!ex) {
  893. alert("jqv:custom regex not found - "+customRule);
  894. return;
  895. }
  896. var pattern = new RegExp(ex);
  897. if (!pattern.test(field.val())) return options.allrules[customRule].alertText;
  898. } else if(rule["func"]) {
  899. fn = rule["func"];
  900. if (typeof(fn) !== "function") {
  901. alert("jqv:custom parameter 'function' is no function - "+customRule);
  902. return;
  903. }
  904. if (!fn(field, rules, i, options))
  905. return options.allrules[customRule].alertText;
  906. } else {
  907. alert("jqv:custom type not allowed "+customRule);
  908. return;
  909. }
  910. },
  911. /**
  912. * Validate custom function outside of the engine scope
  913. *
  914. * @param {jqObject} field
  915. * @param {Array[String]} rules
  916. * @param {int} i rules index
  917. * @param {Map}
  918. * user options
  919. * @return an error string if validation failed
  920. */
  921. _funcCall: function(field, rules, i, options) {
  922. var functionName = rules[i + 1];
  923. var fn;
  924. if(functionName.indexOf('.') >-1)
  925. {
  926. var namespaces = functionName.split('.');
  927. var scope = window;
  928. while(namespaces.length)
  929. {
  930. scope = scope[namespaces.shift()];
  931. }
  932. fn = scope;
  933. }
  934. else
  935. fn = window[functionName] || options.customFunctions[functionName];
  936. if (typeof(fn) == 'function')
  937. return fn(field, rules, i, options);
  938. },
  939. /**
  940. * Field match
  941. *
  942. * @param {jqObject} field
  943. * @param {Array[String]} rules
  944. * @param {int} i rules index
  945. * @param {Map}
  946. * user options
  947. * @return an error string if validation failed
  948. */
  949. _equals: function(field, rules, i, options) {
  950. var equalsField = rules[i + 1];
  951. if (field.val() != $("#" + equalsField).val())
  952. return options.allrules.equals.alertText;
  953. },
  954. /**
  955. * Check the maximum size (in characters)
  956. *
  957. * @param {jqObject} field
  958. * @param {Array[String]} rules
  959. * @param {int} i rules index
  960. * @param {Map}
  961. * user options
  962. * @return an error string if validation failed
  963. */
  964. _maxSize: function(field, rules, i, options) {
  965. var max = rules[i + 1];
  966. var len = field.val().length;
  967. if (len > max) {
  968. var rule = options.allrules.maxSize;
  969. return rule.alertText + max + rule.alertText2;
  970. }
  971. },
  972. /**
  973. * Check the minimum size (in characters)
  974. *
  975. * @param {jqObject} field
  976. * @param {Array[String]} rules
  977. * @param {int} i rules index
  978. * @param {Map}
  979. * user options
  980. * @return an error string if validation failed
  981. */
  982. _minSize: function(field, rules, i, options) {
  983. var min = rules[i + 1];
  984. var len = field.val().length;
  985. if (len < min) {
  986. var rule = options.allrules.minSize;
  987. return rule.alertText + min + rule.alertText2;
  988. }
  989. },
  990. /**
  991. * Check number minimum value
  992. *
  993. * @param {jqObject} field
  994. * @param {Array[String]} rules
  995. * @param {int} i rules index
  996. * @param {Map}
  997. * user options
  998. * @return an error string if validation failed
  999. */
  1000. _min: function(field, rules, i, options) {
  1001. var min = parseFloat(rules[i + 1]);
  1002. var len = parseFloat(field.val());
  1003. if (len < min) {
  1004. var rule = options.allrules.min;
  1005. if (rule.alertText2) return rule.alertText + min + rule.alertText2;
  1006. return rule.alertText + min;
  1007. }
  1008. },
  1009. /**
  1010. * Check number maximum value
  1011. *
  1012. * @param {jqObject} field
  1013. * @param {Array[String]} rules
  1014. * @param {int} i rules index
  1015. * @param {Map}
  1016. * user options
  1017. * @return an error string if validation failed
  1018. */
  1019. _max: function(field, rules, i, options) {
  1020. var max = parseFloat(rules[i + 1]);
  1021. var len = parseFloat(field.val());
  1022. if (len >max ) {
  1023. var rule = options.allrules.max;
  1024. if (rule.alertText2) return rule.alertText + max + rule.alertText2;
  1025. //orefalo: to review, also do the translations
  1026. return rule.alertText + max;
  1027. }
  1028. },
  1029. /**
  1030. * Checks date is in the past
  1031. *
  1032. * @param {jqObject} field
  1033. * @param {Array[String]} rules
  1034. * @param {int} i rules index
  1035. * @param {Map}
  1036. * user options
  1037. * @return an error string if validation failed
  1038. */
  1039. _past: function(form, field, rules, i, options) {
  1040. var p=rules[i + 1];
  1041. var fieldAlt = $(form.find("input[name='" + p.replace(/^#+/, '') + "']"));
  1042. var pdate;
  1043. if (p.toLowerCase() == "now") {
  1044. pdate = new Date();
  1045. } else if (undefined != fieldAlt.val()) {
  1046. if (fieldAlt.is(":disabled"))
  1047. return;
  1048. pdate = methods._parseDate(fieldAlt.val());
  1049. } else {
  1050. pdate = methods._parseDate(p);
  1051. }
  1052. var vdate = methods._parseDate(field.val());
  1053. if (vdate > pdate ) {
  1054. var rule = options.allrules.past;
  1055. if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
  1056. return rule.alertText + methods._dateToString(pdate);
  1057. }
  1058. },
  1059. /**
  1060. * Checks date is in the future
  1061. *
  1062. * @param {jqObject} field
  1063. * @param {Array[String]} rules
  1064. * @param {int} i rules index
  1065. * @param {Map}
  1066. * user options
  1067. * @return an error string if validation failed
  1068. */
  1069. _future: function(form, field, rules, i, options) {
  1070. var p=rules[i + 1];
  1071. var fieldAlt = $(form.find("input[name='" + p.replace(/^#+/, '') + "']"));
  1072. var pdate;
  1073. if (p.toLowerCase() == "now") {
  1074. pdate = new Date();
  1075. } else if (undefined != fieldAlt.val()) {
  1076. if (fieldAlt.is(":disabled"))
  1077. return;
  1078. pdate = methods._parseDate(fieldAlt.val());
  1079. } else {
  1080. pdate = methods._parseDate(p);
  1081. }
  1082. var vdate = methods._parseDate(field.val());
  1083. if (vdate < pdate ) {
  1084. var rule = options.allrules.future;
  1085. if (rule.alertText2)
  1086. return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
  1087. return rule.alertText + methods._dateToString(pdate);
  1088. }
  1089. },
  1090. /**
  1091. * Checks if valid date
  1092. *
  1093. * @param {string} date string
  1094. * @return a bool based on determination of valid date
  1095. */
  1096. _isDate: function (value) {
  1097. var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/);
  1098. return dateRegEx.test(value);
  1099. },
  1100. /**
  1101. * Checks if valid date time
  1102. *
  1103. * @param {string} date string
  1104. * @return a bool based on determination of valid date time
  1105. */
  1106. _isDateTime: function (value){
  1107. var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/);
  1108. return dateTimeRegEx.test(value);
  1109. },
  1110. //Checks if the start date is before the end date
  1111. //returns true if end is later than start
  1112. _dateCompare: function (start, end) {
  1113. return (new Date(start.toString()) < new Date(end.toString()));
  1114. },
  1115. /**
  1116. * Checks date range
  1117. *
  1118. * @param {jqObject} first field name
  1119. * @param {jqObject} second field name
  1120. * @return an error string if validation failed
  1121. */
  1122. _dateRange: function (field, rules, i, options) {
  1123. //are not both populated
  1124. if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
  1125. return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  1126. }
  1127. //are not both dates
  1128. if (!methods._isDate(options.firstOfGroup[0].value) || !methods._isDate(options.secondOfGroup[0].value)) {
  1129. return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  1130. }
  1131. //are both dates but range is off
  1132. if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
  1133. return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  1134. }
  1135. },
  1136. /**
  1137. * Checks date time range
  1138. *
  1139. * @param {jqObject} first field name
  1140. * @param {jqObject} second field name
  1141. * @return an error string if validation failed
  1142. */
  1143. _dateTimeRange: function (field, rules, i, options) {
  1144. //are not both populated
  1145. if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
  1146. return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  1147. }
  1148. //are not both dates
  1149. if (!methods._isDateTime(options.firstOfGroup[0].value) || !methods._isDateTime(options.secondOfGroup[0].value)) {
  1150. return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  1151. }
  1152. //are both dates but range is off
  1153. if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
  1154. return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  1155. }
  1156. },
  1157. /**
  1158. * Max number of checkbox selected
  1159. *
  1160. * @param {jqObject} field
  1161. * @param {Array[String]} rules
  1162. * @param {int} i rules index
  1163. * @param {Map}
  1164. * user options
  1165. * @return an error string if validation failed
  1166. */
  1167. _maxCheckbox: function(form, field, rules, i, options) {
  1168. var nbCheck = rules[i + 1];
  1169. var groupname = field.attr("name");
  1170. var groupSize = form.find("input[name='" + groupname + "']:checked").size();
  1171. if (groupSize > nbCheck) {
  1172. options.showArrow = false;
  1173. if (options.allrules.maxCheckbox.alertText2)
  1174. return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2;
  1175. return options.allrules.maxCheckbox.alertText;
  1176. }
  1177. },
  1178. /**
  1179. * Min number of checkbox selected
  1180. *
  1181. * @param {jqObject} field
  1182. * @param {Array[String]} rules
  1183. * @param {int} i rules index
  1184. * @param {Map}
  1185. * user options
  1186. * @return an error string if validation failed
  1187. */
  1188. _minCheckbox: function(form, field, rules, i, options) {
  1189. var nbCheck = rules[i + 1];
  1190. var groupname = field.attr("name");
  1191. var groupSize = form.find("input[name='" + groupname + "']:checked").size();
  1192. if (groupSize < nbCheck) {
  1193. options.showArrow = false;
  1194. return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + options.allrules.minCheckbox.alertText2;
  1195. }
  1196. },
  1197. /**
  1198. * Checks that it is a valid credit card number according to the
  1199. * Luhn checksum algorithm.
  1200. *
  1201. * @param {jqObject} field
  1202. * @param {Array[String]} rules
  1203. * @param {int} i rules index
  1204. * @param {Map}
  1205. * user options
  1206. * @return an error string if validation failed
  1207. */
  1208. _creditCard: function(field, rules, i, options) {
  1209. //spaces and dashes may be valid characters, but must be stripped to calculate the checksum.
  1210. var valid = false, cardNumber = field.val().replace(/ +/g, '').replace(/-+/g, '');
  1211. var numDigits = cardNumber.length;
  1212. if (numDigits >= 14 && numDigits <= 16 && parseInt(cardNumber) > 0) {
  1213. var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String();
  1214. do {
  1215. digit = parseInt(cardNumber.charAt(i));
  1216. luhn += (pos++ % 2 == 0) ? digit * 2 : digit;
  1217. } while (--i >= 0)
  1218. for (i = 0; i < luhn.length; i++) {
  1219. sum += parseInt(luhn.charAt(i));
  1220. }
  1221. valid = sum % 10 == 0;
  1222. }
  1223. if (!valid) return options.allrules.creditCard.alertText;
  1224. },
  1225. /**
  1226. * Ajax field validation
  1227. *
  1228. * @param {jqObject} field
  1229. * @param {Array[String]} rules
  1230. * @param {int} i rules index
  1231. * @param {Map}
  1232. * user options
  1233. * @return nothing! the ajax validator handles the prompts itself
  1234. */
  1235. _ajax: function(field, rules, i, options) {
  1236. var errorSelector = rules[i + 1];
  1237. var rule = options.allrules[errorSelector];
  1238. var extraData = rule.extraData;
  1239. var extraDataDynamic = rule.extraDataDynamic;
  1240. var data = {
  1241. "fieldId" : field.attr("id"),
  1242. "fieldValue" : field.val()
  1243. };
  1244. if (typeof extraData === "object") {
  1245. $.extend(data, extraData);
  1246. } else if (typeof extraData === "string") {
  1247. var tempData = extraData.split("&");
  1248. for(var i = 0; i < tempData.length; i++) {
  1249. var values = tempData[i].split("=");
  1250. if (values[0] && values[0]) {
  1251. data[values[0]] = values[1];
  1252. }
  1253. }
  1254. }
  1255. if (extraDataDynamic) {
  1256. var tmpData = [];
  1257. var domIds = String(extraDataDynamic).split(",");
  1258. for (var i = 0; i < domIds.length; i++) {
  1259. var id = domIds[i];
  1260. if ($(id).length) {
  1261. var inputValue = field.closest("form, .validationEngineContainer").find(id).val();
  1262. var keyValue = id.replace('#', '') + '=' + escape(inputValue);
  1263. data[id.replace('#', '')] = inputValue;
  1264. }
  1265. }
  1266. }
  1267. // If a field change event triggered this we want to clear the cache for this ID
  1268. if (options.eventTrigger == "field") {
  1269. delete(options.ajaxValidCache[field.attr("id")]);
  1270. }
  1271. // If there is an error or if the the field is already validated, do not re-execute AJAX
  1272. if (!options.isError && !methods._checkAjaxFieldStatus(field.attr("id"), options)) {
  1273. $.ajax({
  1274. type: options.ajaxFormValidationMethod,
  1275. url: rule.url,
  1276. cache: false,
  1277. dataType: "json",
  1278. data: data,
  1279. field: field,
  1280. rule: rule,
  1281. methods: methods,
  1282. options: options,
  1283. beforeSend: function() {},
  1284. error: function(data, transport) {
  1285. methods._ajaxError(data, transport);
  1286. },
  1287. success: function(json) {
  1288. // asynchronously called on success, data is the json answer from the server
  1289. var errorFieldId = json[0];
  1290. //var errorField = $($("#" + errorFieldId)[0]);
  1291. var errorField = $("#"+ errorFieldId).eq(0);
  1292. // make sure we found the element
  1293. if (errorField.length == 1) {
  1294. var status = json[1];
  1295. // read the optional msg from the server
  1296. var msg = json[2];
  1297. if (!status) {
  1298. // Houston we got a problem - display an red prompt
  1299. options.ajaxValidCache[errorFieldId] = false;
  1300. options.isError = true;
  1301. // resolve the msg prompt
  1302. if(msg) {
  1303. if (options.allrules[msg]) {
  1304. var txt = options.allrules[msg].alertText;
  1305. if (txt) {
  1306. msg = txt;
  1307. }
  1308. }
  1309. }
  1310. else
  1311. msg = rule.alertText;
  1312. if (options.showPrompts) methods._showPrompt(errorField, msg, "", true, options);
  1313. } else {
  1314. options.ajaxValidCache[errorFieldId] = true;
  1315. // resolves the msg prompt
  1316. if(msg) {
  1317. if (options.allrules[msg]) {
  1318. var txt = options.allrules[msg].alertTextOk;
  1319. if (txt) {
  1320. msg = txt;
  1321. }
  1322. }
  1323. }
  1324. else
  1325. msg = rule.alertTextOk;
  1326. if (options.showPrompts) {
  1327. // see if we should display a green prompt
  1328. if (msg)
  1329. methods._showPrompt(errorField, msg, "pass", true, options);
  1330. else
  1331. methods._closePrompt(errorField);
  1332. }
  1333. // If a submit form triggered this, we want to re-submit the form
  1334. if (options.eventTrigger == "submit")
  1335. field.closest("form").submit();
  1336. }
  1337. }
  1338. errorField.trigger("jqv.field.result", [errorField, options.isError, msg]);
  1339. }
  1340. });
  1341. return rule.alertTextLoad;
  1342. }
  1343. },
  1344. /**
  1345. * Common method to handle ajax errors
  1346. *
  1347. * @param {Object} data
  1348. * @param {Object} transport
  1349. */
  1350. _ajaxError: function(data, transport) {
  1351. if(data.status == 0 && transport == null)
  1352. alert("The page is not served from a server! ajax call failed");
  1353. else if(typeof console != "undefined")
  1354. console.log("Ajax error: " + data.status + " " + transport);
  1355. },
  1356. /**
  1357. * date -> string
  1358. *
  1359. * @param {Object} date
  1360. */
  1361. _dateToString: function(date) {
  1362. return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate();
  1363. },
  1364. /**
  1365. * Parses an ISO date
  1366. * @param {String} d
  1367. */
  1368. _parseDate: function(d) {
  1369. var dateParts = d.split("-");
  1370. if(dateParts==d)
  1371. dateParts = d.split("/");
  1372. if(dateParts==d) {
  1373. dateParts = d.split(".");
  1374. return new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]);
  1375. }
  1376. return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]);
  1377. },
  1378. /**
  1379. * Builds or updates a prompt with the given information
  1380. *
  1381. * @param {jqObject} field
  1382. * @param {String} promptText html text to display type
  1383. * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  1384. * @param {boolean} ajaxed - use to mark fields than being validated with ajax
  1385. * @param {Map} options user options
  1386. */
  1387. _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) {
  1388. //Check if we need to adjust what element to show the prompt on
  1389. if(field.data('jqv-prompt-at') instanceof jQuery ){
  1390. field = field.data('jqv-prompt-at');
  1391. } else if(field.data('jqv-prompt-at')) {
  1392. field = $(field.data('jqv-prompt-at'));
  1393. }
  1394. var prompt = methods._getPrompt(field);
  1395. // The ajax submit errors are not see has an error in the form,
  1396. // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time
  1397. // Because no error was found befor submitting
  1398. if(ajaxform) prompt = false;
  1399. // Check that there is indded text
  1400. if($.trim(promptText)){
  1401. if (prompt)
  1402. methods._updatePrompt(field, prompt, promptText, type, ajaxed, options);
  1403. else
  1404. methods._buildPrompt(field, promptText, type, ajaxed, options);
  1405. }
  1406. },
  1407. /**
  1408. * Builds and shades a prompt for the given field.
  1409. *
  1410. * @param {jqObject} field
  1411. * @param {String} promptText html text to display type
  1412. * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  1413. * @param {boolean} ajaxed - use to mark fields than being validated with ajax
  1414. * @param {Map} options user options
  1415. */
  1416. _buildPrompt: function(field, promptText, type, ajaxed, options) {
  1417. // create the prompt
  1418. var prompt = $('<div>');
  1419. prompt.addClass(methods._getClassName(field.attr("id")) + "formError");
  1420. // add a class name to identify the parent form of the prompt
  1421. prompt.addClass("parentForm"+methods._getClassName(field.closest('form, .validationEngineContainer').attr("id")));
  1422. prompt.addClass("formError");
  1423. switch (type) {
  1424. case "pass":
  1425. prompt.addClass("greenPopup");
  1426. break;
  1427. case "load":
  1428. prompt.addClass("blackPopup");
  1429. break;
  1430. default:
  1431. /* it has error */
  1432. //alert("unknown popup type:"+type);
  1433. }
  1434. if (ajaxed)
  1435. prompt.addClass("ajaxed");
  1436. // create the prompt content
  1437. var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt);
  1438. // determine position type
  1439. var positionType=field.data("promptPosition") || options.promptPosition;
  1440. // create the css arrow pointing at the field
  1441. // note that there is no triangle on max-checkbox and radio
  1442. if (options.showArrow) {
  1443. var arrow = $('<div>').addClass("formErrorArrow");
  1444. //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
  1445. if (typeof(positionType)=='string')
  1446. {
  1447. var pos=positionType.indexOf(":");
  1448. if(pos!=-1)
  1449. positionType=positionType.substring(0,pos);
  1450. }
  1451. switch (positionType) {
  1452. case "bottomLeft":
  1453. case "bottomRight":
  1454. prompt.find(".formErrorContent").before(arrow);
  1455. arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
  1456. break;
  1457. case "topLeft":
  1458. case "topRight":
  1459. arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
  1460. prompt.append(arrow);
  1461. break;
  1462. }
  1463. }
  1464. // Add custom prompt class
  1465. if (options.addPromptClass)
  1466. prompt.addClass(options.addPromptClass);
  1467. // Add custom prompt class defined in element
  1468. var requiredOverride = field.attr('data-required-class');
  1469. if(requiredOverride !== undefined) {
  1470. prompt.addClass(requiredOverride);
  1471. } else {
  1472. if(options.prettySelect) {
  1473. if($('#' + field.attr('id')).next().is('select')) {
  1474. var prettyOverrideClass = $('#' + field.attr('id').substr(options.usePrefix.length).substring(options.useSuffix.length)).attr('data-required-class');
  1475. if(prettyOverrideClass !== undefined) {
  1476. prompt.addClass(prettyOverrideClass);
  1477. }
  1478. }
  1479. }
  1480. }
  1481. prompt.css({
  1482. "opacity": 0
  1483. });
  1484. if(positionType === 'inline') {
  1485. prompt.addClass("inline");
  1486. if(typeof field.attr('data-prompt-target') !== 'undefined' && $('#'+field.attr('data-prompt-target')).length > 0) {
  1487. prompt.appendTo($('#'+field.attr('data-prompt-target')));
  1488. } else {
  1489. field.after(prompt);
  1490. }
  1491. } else {
  1492. field.before(prompt);
  1493. }
  1494. var pos = methods._calculatePosition(field, prompt, options);
  1495. prompt.css({
  1496. 'position': positionType === 'inline' ? 'relative' : 'absolute',
  1497. "top": pos.callerTopPosition,
  1498. "left": pos.callerleftPosition,
  1499. "marginTop": pos.marginTopSize,
  1500. "opacity": 0
  1501. }).data("callerField", field);
  1502. if (options.autoHidePrompt) {
  1503. setTimeout(function(){
  1504. prompt.animate({
  1505. "opacity": 0
  1506. },function(){
  1507. prompt.closest('.formErrorOuter').remove();
  1508. prompt.remove();
  1509. });
  1510. }, options.autoHideDelay);
  1511. }
  1512. return prompt.animate({
  1513. "opacity": 0.87
  1514. });
  1515. },
  1516. /**
  1517. * Updates the prompt text field - the field for which the prompt
  1518. * @param {jqObject} field
  1519. * @param {String} promptText html text to display type
  1520. * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  1521. * @param {boolean} ajaxed - use to mark fields than being validated with ajax
  1522. * @param {Map} options user options
  1523. */
  1524. _updatePrompt: function(field, prompt, promptText, type, ajaxed, options, noAnimation) {
  1525. if (prompt) {
  1526. if (typeof type !== "undefined") {
  1527. if (type == "pass")
  1528. prompt.addClass("greenPopup");
  1529. else
  1530. prompt.removeClass("greenPopup");
  1531. if (type == "load")
  1532. prompt.addClass("blackPopup");
  1533. else
  1534. prompt.removeClass("blackPopup");
  1535. }
  1536. if (ajaxed)
  1537. prompt.addClass("ajaxed");
  1538. else
  1539. prompt.removeClass("ajaxed");
  1540. prompt.find(".formErrorContent").html(promptText);
  1541. var pos = methods._calculatePosition(field, prompt, options);
  1542. var css = {"top": pos.callerTopPosition,
  1543. "left": pos.callerleftPosition,
  1544. "marginTop": pos.marginTopSize};
  1545. if (noAnimation)
  1546. prompt.css(css);
  1547. else
  1548. prompt.animate(css);
  1549. }
  1550. },
  1551. /**
  1552. * Closes the prompt associated with the given field
  1553. *
  1554. * @param {jqObject}
  1555. * field
  1556. */
  1557. _closePrompt: function(field) {
  1558. var prompt = methods._getPrompt(field);
  1559. if (prompt)
  1560. prompt.fadeTo("fast", 0, function() {
  1561. prompt.parent('.formErrorOuter').remove();
  1562. prompt.remove();
  1563. });
  1564. },
  1565. closePrompt: function(field) {
  1566. return methods._closePrompt(field);
  1567. },
  1568. /**
  1569. * Returns the error prompt matching the field if any
  1570. *
  1571. * @param {jqObject}
  1572. * field
  1573. * @return undefined or the error prompt (jqObject)
  1574. */
  1575. _getPrompt: function(field) {
  1576. var formId = $(field).closest('form, .validationEngineContainer').attr('id');
  1577. var className = methods._getClassName(field.attr("id")) + "formError";
  1578. var match = $("." + methods._escapeExpression(className) + '.parentForm' + methods._getClassName(formId))[0];
  1579. if (match)
  1580. return $(match);
  1581. },
  1582. /**
  1583. * Returns the escapade classname
  1584. *
  1585. * @param {selector}
  1586. * className
  1587. */
  1588. _escapeExpression: function (selector) {
  1589. return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1");
  1590. },
  1591. /**
  1592. * returns true if we are in a RTLed document
  1593. *
  1594. * @param {jqObject} field
  1595. */
  1596. isRTL: function(field)
  1597. {
  1598. var $document = $(document);
  1599. var $body = $('body');
  1600. var rtl =
  1601. (field && field.hasClass('rtl')) ||
  1602. (field && (field.attr('dir') || '').toLowerCase()==='rtl') ||
  1603. $document.hasClass('rtl') ||
  1604. ($document.attr('dir') || '').toLowerCase()==='rtl' ||
  1605. $body.hasClass('rtl') ||
  1606. ($body.attr('dir') || '').toLowerCase()==='rtl';
  1607. return Boolean(rtl);
  1608. },
  1609. /**
  1610. * Calculates prompt position
  1611. *
  1612. * @param {jqObject}
  1613. * field
  1614. * @param {jqObject}
  1615. * the prompt
  1616. * @param {Map}
  1617. * options
  1618. * @return positions
  1619. */
  1620. _calculatePosition: function (field, promptElmt, options) {
  1621. var promptTopPosition, promptleftPosition, marginTopSize;
  1622. var fieldWidth = field.width();
  1623. var fieldLeft = field.position().left;
  1624. var fieldTop = field.position().top;
  1625. var fieldHeight = field.height();
  1626. var promptHeight = promptElmt.height();
  1627. // is the form contained in an overflown container?
  1628. promptTopPosition = promptleftPosition = 0;
  1629. // compensation for the arrow
  1630. marginTopSize = -promptHeight;
  1631. //prompt positioning adjustment support
  1632. //now you can adjust prompt position
  1633. //usage: positionType:Xshift,Yshift
  1634. //for example:
  1635. // bottomLeft:+20 means bottomLeft position shifted by 20 pixels right horizontally
  1636. // topRight:20, -15 means topRight position shifted by 20 pixels to right and 15 pixels to top
  1637. //You can use +pixels, - pixels. If no sign is provided than + is default.
  1638. var positionType=field.data("promptPosition") || options.promptPosition;
  1639. var shift1="";
  1640. var shift2="";
  1641. var shiftX=0;
  1642. var shiftY=0;
  1643. if (typeof(positionType)=='string') {
  1644. //do we have any position adjustments ?
  1645. if (positionType.indexOf(":")!=-1) {
  1646. shift1=positionType.substring(positionType.indexOf(":")+1);
  1647. positionType=positionType.substring(0,positionType.indexOf(":"));
  1648. //if any advanced positioning will be needed (percents or something else) - parser should be added here
  1649. //for now we use simple parseInt()
  1650. //do we have second parameter?
  1651. if (shift1.indexOf(",") !=-1) {
  1652. shift2=shift1.substring(shift1.indexOf(",") +1);
  1653. shift1=shift1.substring(0,shift1.indexOf(","));
  1654. shiftY=parseInt(shift2);
  1655. if (isNaN(shiftY)) shiftY=0;
  1656. };
  1657. shiftX=parseInt(shift1);
  1658. if (isNaN(shift1)) shift1=0;
  1659. };
  1660. };
  1661. switch (positionType) {
  1662. default:
  1663. case "topRight":
  1664. promptleftPosition += fieldLeft + fieldWidth - 30;
  1665. promptTopPosition += fieldTop;
  1666. break;
  1667. case "topLeft":
  1668. promptTopPosition += fieldTop;
  1669. promptleftPosition += fieldLeft;
  1670. break;
  1671. case "centerRight":
  1672. promptTopPosition = fieldTop+4;
  1673. marginTopSize = 0;
  1674. promptleftPosition= fieldLeft + field.outerWidth(true)+5;
  1675. break;
  1676. case "centerLeft":
  1677. promptleftPosition = fieldLeft - (promptElmt.width() + 2);
  1678. promptTopPosition = fieldTop+4;
  1679. marginTopSize = 0;
  1680. break;
  1681. case "bottomLeft":
  1682. promptTopPosition = fieldTop + field.height() + 5;
  1683. marginTopSize = 0;
  1684. promptleftPosition = fieldLeft;
  1685. break;
  1686. case "bottomRight":
  1687. promptleftPosition = fieldLeft + fieldWidth - 30;
  1688. promptTopPosition = fieldTop + field.height() + 5;
  1689. marginTopSize = 0;
  1690. break;
  1691. case "inline":
  1692. promptleftPosition = 0;
  1693. promptTopPosition = 0;
  1694. marginTopSize = 0;
  1695. };
  1696. //apply adjusments if any
  1697. promptleftPosition += shiftX;
  1698. promptTopPosition += shiftY;
  1699. return {
  1700. "callerTopPosition": promptTopPosition + "px",
  1701. "callerleftPosition": promptleftPosition + "px",
  1702. "marginTopSize": marginTopSize + "px"
  1703. };
  1704. },
  1705. /**
  1706. * Saves the user options and variables in the form.data
  1707. *
  1708. * @param {jqObject}
  1709. * form - the form where the user option should be saved
  1710. * @param {Map}
  1711. * options - the user options
  1712. * @return the user options (extended from the defaults)
  1713. */
  1714. _saveOptions: function(form, options) {
  1715. // is there a language localisation ?
  1716. if ($.validationEngineLanguage)
  1717. var allRules = $.validationEngineLanguage.allRules;
  1718. else
  1719. $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page");
  1720. // --- Internals DO NOT TOUCH or OVERLOAD ---
  1721. // validation rules and i18
  1722. $.validationEngine.defaults.allrules = allRules;
  1723. var userOptions = $.extend(true,{},$.validationEngine.defaults,options);
  1724. form.data('jqv', userOptions);
  1725. return userOptions;
  1726. },
  1727. /**
  1728. * Removes forbidden characters from class name
  1729. * @param {String} className
  1730. */
  1731. _getClassName: function(className) {
  1732. if(className)
  1733. return className.replace(/:/g, "_").replace(/\./g, "_");
  1734. },
  1735. /**
  1736. * Escape special character for jQuery selector
  1737. * http://totaldev.com/content/escaping-characters-get-valid-jquery-id
  1738. * @param {String} selector
  1739. */
  1740. _jqSelector: function(str){
  1741. return str.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
  1742. },
  1743. /**
  1744. * Conditionally required field
  1745. *
  1746. * @param {jqObject} field
  1747. * @param {Array[String]} rules
  1748. * @param {int} i rules index
  1749. * @param {Map}
  1750. * user options
  1751. * @return an error string if validation failed
  1752. */
  1753. _condRequired: function(field, rules, i, options) {
  1754. var idx, dependingField;
  1755. for(idx = (i + 1); idx < rules.length; idx++) {
  1756. dependingField = jQuery("#" + rules[idx]).first();
  1757. /* Use _required for determining wether dependingField has a value.
  1758. * There is logic there for handling all field types, and default value; so we won't replicate that here
  1759. * Indicate this special use by setting the last parameter to true so we only validate the dependingField on chackboxes and radio buttons (#462)
  1760. */
  1761. if (dependingField.length && methods._required(dependingField, ["required"], 0, options, true) == undefined) {
  1762. /* We now know any of the depending fields has a value,
  1763. * so we can validate this field as per normal required code
  1764. */
  1765. return methods._required(field, ["required"], 0, options);
  1766. }
  1767. }
  1768. },
  1769. _submitButtonClick: function(event) {
  1770. var button = $(this);
  1771. var form = button.closest('form, .validationEngineContainer');
  1772. form.data("jqv_submitButton", button.attr("id"));
  1773. }
  1774. };
  1775. /**
  1776. * Plugin entry point.
  1777. * You may pass an action as a parameter or a list of options.
  1778. * if none, the init and attach methods are being called.
  1779. * Remember: if you pass options, the attached method is NOT called automatically
  1780. *
  1781. * @param {String}
  1782. * method (optional) action
  1783. */
  1784. $.fn.validationEngine = function(method) {
  1785. var form = $(this);
  1786. if(!form[0]) return form; // stop here if the form does not exist
  1787. if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) {
  1788. // make sure init is called once
  1789. if(method != "showPrompt" && method != "hide" && method != "hideAll")
  1790. methods.init.apply(form);
  1791. return methods[method].apply(form, Array.prototype.slice.call(arguments, 1));
  1792. } else if (typeof method == 'object' || !method) {
  1793. // default constructor with or without arguments
  1794. methods.init.apply(form, arguments);
  1795. return methods.attach.apply(form);
  1796. } else {
  1797. $.error('Method ' + method + ' does not exist in jQuery.validationEngine');
  1798. }
  1799. };
  1800. // LEAK GLOBAL OPTIONS
  1801. $.validationEngine= {fieldIdCounter: 0,defaults:{
  1802. // Name of the event triggering field validation
  1803. validationEventTrigger: "blur",
  1804. // Automatically scroll viewport to the first error
  1805. scroll: true,
  1806. // Focus on the first input
  1807. focusFirstField:true,
  1808. // Show prompts, set to false to disable prompts
  1809. showPrompts: true,
  1810. // Should we attempt to validate non-visible input fields contained in the form? (Useful in cases of tabbed containers, e.g. jQuery-UI tabs)
  1811. validateNonVisibleFields: false,
  1812. // Opening box position, possible locations are: topLeft,
  1813. // topRight, bottomLeft, centerRight, bottomRight, inline
  1814. // inline gets inserted after the validated field or into an element specified in data-prompt-target
  1815. promptPosition: "topRight",
  1816. bindMethod:"bind",
  1817. // internal, automatically set to true when it parse a _ajax rule
  1818. inlineAjax: false,
  1819. // if set to true, the form data is sent asynchronously via ajax to the form.action url (get)
  1820. ajaxFormValidation: false,
  1821. // The url to send the submit ajax validation (default to action)
  1822. ajaxFormValidationURL: false,
  1823. // HTTP method used for ajax validation
  1824. ajaxFormValidationMethod: 'get',
  1825. // Ajax form validation callback method: boolean onComplete(form, status, errors, options)
  1826. // retuns false if the form.submit event needs to be canceled.
  1827. onAjaxFormComplete: $.noop,
  1828. // called right before the ajax call, may return false to cancel
  1829. onBeforeAjaxFormValidation: $.noop,
  1830. // Stops form from submitting and execute function assiciated with it
  1831. onValidationComplete: false,
  1832. // Used when you have a form fields too close and the errors messages are on top of other disturbing viewing messages
  1833. doNotShowAllErrosOnSubmit: false,
  1834. // Object where you store custom messages to override the default error messages
  1835. custom_error_messages:{},
  1836. // true if you want to vind the input fields
  1837. binded: true,
  1838. // set to true, when the prompt arrow needs to be displayed
  1839. showArrow: true,
  1840. // did one of the validation fail ? kept global to stop further ajax validations
  1841. isError: false,
  1842. // Limit how many displayed errors a field can have
  1843. maxErrorsPerField: false,
  1844. // Caches field validation status, typically only bad status are created.
  1845. // the array is used during ajax form validation to detect issues early and prevent an expensive submit
  1846. ajaxValidCache: {},
  1847. // Auto update prompt position after window resize
  1848. autoPositionUpdate: false,
  1849. InvalidFields: [],
  1850. onFieldSuccess: false,
  1851. onFieldFailure: false,
  1852. onSuccess: false,
  1853. onFailure: false,
  1854. validateAttribute: "class",
  1855. addSuccessCssClassToField: "",
  1856. addFailureCssClassToField: "",
  1857. // Auto-hide prompt
  1858. autoHidePrompt: false,
  1859. // Delay before auto-hide
  1860. autoHideDelay: 10000,
  1861. // Fade out duration while hiding the validations
  1862. fadeDuration: 0.3,
  1863. // Use Prettify select library
  1864. prettySelect: false,
  1865. // Add css class on prompt
  1866. addPromptClass : "",
  1867. // Custom ID uses prefix
  1868. usePrefix: "",
  1869. // Custom ID uses suffix
  1870. useSuffix: "",
  1871. // Only show one message per error prompt
  1872. showOneMessage: false
  1873. }};
  1874. $(function(){$.validationEngine.defaults.promptPosition = methods.isRTL()?'topLeft':"topRight"});
  1875. })(jQuery);