jquery.validationEngine.js 89 KB

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