validation.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. /*
  2. * Really easy field validation with Prototype
  3. * http://tetlaw.id.au/view/javascript/really-easy-field-validation
  4. * Andrew Tetlaw
  5. * Version 1.5.4.1 (2007-01-05)
  6. *
  7. * Copyright (c) 2007 Andrew Tetlaw
  8. * Permission is hereby granted, free of charge, to any person
  9. * obtaining a copy of this software and associated documentation
  10. * files (the "Software"), to deal in the Software without
  11. * restriction, including without limitation the rights to use, copy,
  12. * modify, merge, publish, distribute, sublicense, and/or sell copies
  13. * of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  23. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  24. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  25. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  26. * SOFTWARE.
  27. *
  28. */
  29. var Validator = Class.create();
  30. Validator.prototype = {
  31. initialize : function(className, error, test, options) {
  32. if(typeof test == 'function'){
  33. this.options = $H(options);
  34. this._test = test;
  35. } else {
  36. this.options = $H(test);
  37. this._test = function(){return true};
  38. }
  39. this.error = error || 'Validation failed.';
  40. this.className = className;
  41. },
  42. test : function(v, elm) {
  43. return (this._test(v,elm) && this.options.all(function(p){
  44. return Validator.methods[p.key] ? Validator.methods[p.key](v,elm,p.value) : true;
  45. }));
  46. }
  47. }
  48. Validator.methods = {
  49. pattern : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) || opt.test(v)},
  50. minLength : function(v,elm,opt) {return v.length >= opt},
  51. maxLength : function(v,elm,opt) {return v.length <= opt},
  52. min : function(v,elm,opt) {return v >= parseFloat(opt)},
  53. max : function(v,elm,opt) {return v <= parseFloat(opt)},
  54. notOneOf : function(v,elm,opt) {return $A(opt).all(function(value) {
  55. return v != value;
  56. })},
  57. oneOf : function(v,elm,opt) {return $A(opt).any(function(value) {
  58. return v == value;
  59. })},
  60. is : function(v,elm,opt) {return v == opt},
  61. isNot : function(v,elm,opt) {return v != opt},
  62. equalToField : function(v,elm,opt) {return v == $F(opt)},
  63. notEqualToField : function(v,elm,opt) {return v != $F(opt)},
  64. include : function(v,elm,opt) {return $A(opt).all(function(value) {
  65. return Validation.get(value).test(v,elm);
  66. })}
  67. }
  68. var Validation = Class.create();
  69. Validation.defaultOptions = {
  70. onSubmit : true,
  71. stopOnFirst : false,
  72. immediate : false,
  73. focusOnError : true,
  74. useTitles : false,
  75. addClassNameToContainer: false,
  76. containerClassName: '.input-box',
  77. onFormValidate : function(result, form) {},
  78. onElementValidate : function(result, elm) {}
  79. };
  80. Validation.prototype = {
  81. initialize : function(form, options){
  82. this.form = $(form);
  83. if (!this.form) {
  84. return;
  85. }
  86. this.options = Object.extend({
  87. onSubmit : Validation.defaultOptions.onSubmit,
  88. stopOnFirst : Validation.defaultOptions.stopOnFirst,
  89. immediate : Validation.defaultOptions.immediate,
  90. focusOnError : Validation.defaultOptions.focusOnError,
  91. useTitles : Validation.defaultOptions.useTitles,
  92. onFormValidate : Validation.defaultOptions.onFormValidate,
  93. onElementValidate : Validation.defaultOptions.onElementValidate
  94. }, options || {});
  95. if(this.options.onSubmit) Event.observe(this.form,'submit',this.onSubmit.bind(this),false);
  96. if(this.options.immediate) {
  97. Form.getElements(this.form).each(function(input) { // Thanks Mike!
  98. if (input.tagName.toLowerCase() == 'select') {
  99. Event.observe(input, 'blur', this.onChange.bindAsEventListener(this));
  100. }
  101. if (input.type.toLowerCase() == 'radio' || input.type.toLowerCase() == 'checkbox') {
  102. Event.observe(input, 'click', this.onChange.bindAsEventListener(this));
  103. } else {
  104. Event.observe(input, 'change', this.onChange.bindAsEventListener(this));
  105. }
  106. }, this);
  107. }
  108. },
  109. onChange : function (ev) {
  110. Validation.isOnChange = true;
  111. Validation.validate(Event.element(ev),{
  112. useTitle : this.options.useTitles,
  113. onElementValidate : this.options.onElementValidate
  114. });
  115. Validation.isOnChange = false;
  116. },
  117. onSubmit : function(ev){
  118. if(!this.validate()) Event.stop(ev);
  119. },
  120. validate : function() {
  121. var result = false;
  122. var useTitles = this.options.useTitles;
  123. var callback = this.options.onElementValidate;
  124. try {
  125. if(this.options.stopOnFirst) {
  126. result = Form.getElements(this.form).all(function(elm) {
  127. if (elm.hasClassName('local-validation') && !this.isElementInForm(elm, this.form)) {
  128. return true;
  129. }
  130. return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback});
  131. }, this);
  132. } else {
  133. result = Form.getElements(this.form).collect(function(elm) {
  134. if (elm.hasClassName('local-validation') && !this.isElementInForm(elm, this.form)) {
  135. return true;
  136. }
  137. if (elm.hasClassName('validation-disabled')) {
  138. return true;
  139. }
  140. return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback});
  141. }, this).all();
  142. }
  143. } catch (e) {
  144. }
  145. if(!result && this.options.focusOnError) {
  146. try{
  147. Form.getElements(this.form).findAll(function(elm){return $(elm).hasClassName('validation-failed')}).first().focus()
  148. }
  149. catch(e){
  150. }
  151. }
  152. this.options.onFormValidate(result, this.form);
  153. return result;
  154. },
  155. reset : function() {
  156. Form.getElements(this.form).each(Validation.reset);
  157. },
  158. isElementInForm : function(elm, form) {
  159. var domForm = elm.up('form');
  160. if (domForm == form) {
  161. return true;
  162. }
  163. return false;
  164. }
  165. }
  166. Object.extend(Validation, {
  167. validate : function(elm, options){
  168. options = Object.extend({
  169. useTitle : false,
  170. onElementValidate : function(result, elm) {}
  171. }, options || {});
  172. elm = $(elm);
  173. var cn = $w(elm.className);
  174. return result = cn.all(function(value) {
  175. var test = Validation.test(value,elm,options.useTitle);
  176. options.onElementValidate(test, elm);
  177. return test;
  178. });
  179. },
  180. insertAdvice : function(elm, advice){
  181. var container = $(elm).up('.field-row');
  182. if(container){
  183. Element.insert(container, {after: advice});
  184. } else if (elm.up('td.value')) {
  185. elm.up('td.value').insert({bottom: advice});
  186. } else if (elm.advaiceContainer && $(elm.advaiceContainer)) {
  187. $(elm.advaiceContainer).update(advice);
  188. }
  189. else {
  190. switch (elm.type.toLowerCase()) {
  191. case 'checkbox':
  192. case 'radio':
  193. var p = elm.parentNode;
  194. if(p) {
  195. Element.insert(p, {'bottom': advice});
  196. } else {
  197. Element.insert(elm, {'after': advice});
  198. }
  199. break;
  200. default:
  201. Element.insert(elm, {'after': advice});
  202. }
  203. }
  204. },
  205. showAdvice : function(elm, advice, adviceName){
  206. if(!elm.advices){
  207. elm.advices = new Hash();
  208. }
  209. else{
  210. elm.advices.each(function(pair){
  211. if (!advice || pair.value.id != advice.id) {
  212. // hide non-current advice after delay
  213. this.hideAdvice(elm, pair.value);
  214. }
  215. }.bind(this));
  216. }
  217. elm.advices.set(adviceName, advice);
  218. if(typeof Effect == 'undefined') {
  219. advice.style.display = 'block';
  220. } else {
  221. if(!advice._adviceAbsolutize) {
  222. new Effect.Appear(advice, {duration : 1 });
  223. } else {
  224. Position.absolutize(advice);
  225. advice.show();
  226. advice.setStyle({
  227. 'top':advice._adviceTop,
  228. 'left': advice._adviceLeft,
  229. 'width': advice._adviceWidth,
  230. 'z-index': 1000
  231. });
  232. advice.addClassName('advice-absolute');
  233. }
  234. }
  235. },
  236. hideAdvice : function(elm, advice){
  237. if (advice != null) {
  238. new Effect.Fade(advice, {duration : 1, afterFinishInternal : function() {advice.hide();}});
  239. }
  240. },
  241. updateCallback : function(elm, status) {
  242. if (typeof elm.callbackFunction != 'undefined') {
  243. eval(elm.callbackFunction+'(\''+elm.id+'\',\''+status+'\')');
  244. }
  245. },
  246. ajaxError : function(elm, errorMsg) {
  247. var name = 'validate-ajax';
  248. var advice = Validation.getAdvice(name, elm);
  249. if (advice == null) {
  250. advice = this.createAdvice(name, elm, false, errorMsg);
  251. }
  252. this.showAdvice(elm, advice, 'validate-ajax');
  253. this.updateCallback(elm, 'failed');
  254. elm.addClassName('validation-failed');
  255. elm.addClassName('validate-ajax');
  256. if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
  257. var container = elm.up(Validation.defaultOptions.containerClassName);
  258. if (container && this.allowContainerClassName(elm)) {
  259. container.removeClassName('validation-passed');
  260. container.addClassName('validation-error');
  261. }
  262. }
  263. },
  264. allowContainerClassName: function (elm) {
  265. if (elm.type == 'radio' || elm.type == 'checkbox') {
  266. return elm.hasClassName('change-container-classname');
  267. }
  268. return true;
  269. },
  270. test : function(name, elm, useTitle) {
  271. var v = Validation.get(name);
  272. var prop = '__advice'+name.camelize();
  273. try {
  274. if(Validation.isVisible(elm) && !v.test($F(elm), elm)) {
  275. //if(!elm[prop]) {
  276. var advice = Validation.getAdvice(name, elm);
  277. if (advice == null) {
  278. advice = this.createAdvice(name, elm, useTitle);
  279. }
  280. this.showAdvice(elm, advice, name);
  281. this.updateCallback(elm, 'failed');
  282. //}
  283. elm[prop] = 1;
  284. if (!elm.advaiceContainer) {
  285. elm.removeClassName('validation-passed');
  286. elm.addClassName('validation-failed');
  287. }
  288. if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
  289. var container = elm.up(Validation.defaultOptions.containerClassName);
  290. if (container && this.allowContainerClassName(elm)) {
  291. container.removeClassName('validation-passed');
  292. container.addClassName('validation-error');
  293. }
  294. }
  295. return false;
  296. } else {
  297. var advice = Validation.getAdvice(name, elm);
  298. this.hideAdvice(elm, advice);
  299. this.updateCallback(elm, 'passed');
  300. elm[prop] = '';
  301. elm.removeClassName('validation-failed');
  302. elm.addClassName('validation-passed');
  303. if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
  304. var container = elm.up(Validation.defaultOptions.containerClassName);
  305. if (container && !container.down('.validation-failed') && this.allowContainerClassName(elm)) {
  306. if (!Validation.get('IsEmpty').test(elm.value) || !this.isVisible(elm)) {
  307. container.addClassName('validation-passed');
  308. } else {
  309. container.removeClassName('validation-passed');
  310. }
  311. container.removeClassName('validation-error');
  312. }
  313. }
  314. return true;
  315. }
  316. } catch(e) {
  317. throw(e)
  318. }
  319. },
  320. isVisible : function(elm) {
  321. while(elm.tagName != 'BODY') {
  322. if(!$(elm).visible()) return false;
  323. elm = elm.parentNode;
  324. }
  325. return true;
  326. },
  327. getAdvice : function(name, elm) {
  328. return $('advice-' + name + '-' + Validation.getElmID(elm)) || $('advice-' + Validation.getElmID(elm));
  329. },
  330. createAdvice : function(name, elm, useTitle, customError) {
  331. var v = Validation.get(name);
  332. var errorMsg = useTitle ? ((elm && elm.title) ? elm.title : v.error) : v.error;
  333. if (customError) {
  334. errorMsg = customError;
  335. }
  336. if (jQuery.mage.__){
  337. errorMsg = jQuery.mage.__(errorMsg);
  338. }
  339. advice = '<div class="validation-advice" id="advice-' + name + '-' + Validation.getElmID(elm) +'" style="display:none">' + errorMsg + '</div>'
  340. Validation.insertAdvice(elm, advice);
  341. advice = Validation.getAdvice(name, elm);
  342. if($(elm).hasClassName('absolute-advice')) {
  343. var dimensions = $(elm).getDimensions();
  344. var originalPosition = Position.cumulativeOffset(elm);
  345. advice._adviceTop = (originalPosition[1] + dimensions.height) + 'px';
  346. advice._adviceLeft = (originalPosition[0]) + 'px';
  347. advice._adviceWidth = (dimensions.width) + 'px';
  348. advice._adviceAbsolutize = true;
  349. }
  350. return advice;
  351. },
  352. getElmID : function(elm) {
  353. return elm.id ? elm.id : elm.name;
  354. },
  355. reset : function(elm) {
  356. elm = $(elm);
  357. var cn = $w(elm.className);
  358. cn.each(function(value) {
  359. var prop = '__advice'+value.camelize();
  360. if(elm[prop]) {
  361. var advice = Validation.getAdvice(value, elm);
  362. if (advice) {
  363. advice.hide();
  364. }
  365. elm[prop] = '';
  366. }
  367. elm.removeClassName('validation-failed');
  368. elm.removeClassName('validation-passed');
  369. if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') {
  370. var container = elm.up(Validation.defaultOptions.containerClassName);
  371. if (container) {
  372. container.removeClassName('validation-passed');
  373. container.removeClassName('validation-error');
  374. }
  375. }
  376. });
  377. },
  378. add : function(className, error, test, options) {
  379. var nv = {};
  380. nv[className] = new Validator(className, error, test, options);
  381. Object.extend(Validation.methods, nv);
  382. },
  383. addAllThese : function(validators) {
  384. var nv = {};
  385. $A(validators).each(function(value) {
  386. nv[value[0]] = new Validator(value[0], value[1], value[2], (value.length > 3 ? value[3] : {}));
  387. });
  388. Object.extend(Validation.methods, nv);
  389. },
  390. get : function(name) {
  391. return Validation.methods[name] ? Validation.methods[name] : Validation.methods['_LikeNoIDIEverSaw_'];
  392. },
  393. methods : {
  394. '_LikeNoIDIEverSaw_' : new Validator('_LikeNoIDIEverSaw_','',{})
  395. }
  396. });
  397. Validation.add('IsEmpty', '', function(v) {
  398. return (v == '' || (v == null) || (v.length == 0) || /^\s+$/.test(v));
  399. });
  400. Validation.addAllThese([
  401. ['validate-no-html-tags', 'HTML tags are not allowed', function(v) {
  402. return !/<(\/)?\w+/.test(v);
  403. }],
  404. ['validate-select', 'Please select an option.', function(v) {
  405. return ((v != "none") && (v != null) && (v.length != 0));
  406. }],
  407. ['required-entry', 'This is a required field.', function(v) {
  408. return !Validation.get('IsEmpty').test(v);
  409. }],
  410. ['validate-number', 'Please enter a valid number in this field.', function(v) {
  411. return Validation.get('IsEmpty').test(v)
  412. || (!isNaN(parseNumber(v)) && /^\s*-?\d*(\.\d*)?\s*$/.test(v));
  413. }],
  414. ['validate-number-range', 'The value is not within the specified range.', function(v, elm) {
  415. if (Validation.get('IsEmpty').test(v)) {
  416. return true;
  417. }
  418. var numValue = parseNumber(v);
  419. if (isNaN(numValue)) {
  420. return false;
  421. }
  422. var reRange = /^number-range-(-?[\d.,]+)?-(-?[\d.,]+)?$/,
  423. result = true;
  424. $w(elm.className).each(function(name) {
  425. var m = reRange.exec(name);
  426. if (m) {
  427. result = result
  428. && (m[1] == null || m[1] == '' || numValue >= parseNumber(m[1]))
  429. && (m[2] == null || m[2] == '' || numValue <= parseNumber(m[2]));
  430. }
  431. });
  432. return result;
  433. }],
  434. ['validate-digits', 'Please use numbers only in this field. Please avoid spaces or other characters such as dots or commas.', function(v) {
  435. return Validation.get('IsEmpty').test(v) || !/[^\d]/.test(v);
  436. }],
  437. ['validate-digits-range', 'The value is not within the specified range.', function(v, elm) {
  438. if (Validation.get('IsEmpty').test(v)) {
  439. return true;
  440. }
  441. var numValue = parseNumber(v);
  442. if (isNaN(numValue)) {
  443. return false;
  444. }
  445. var reRange = /^digits-range-(-?\d+)?-(-?\d+)?$/,
  446. result = true;
  447. $w(elm.className).each(function(name) {
  448. var m = reRange.exec(name);
  449. if (m) {
  450. result = result
  451. && (m[1] == null || m[1] == '' || numValue >= parseNumber(m[1]))
  452. && (m[2] == null || m[2] == '' || numValue <= parseNumber(m[2]));
  453. }
  454. });
  455. return result;
  456. }],
  457. ['validate-range', 'The value is not within the specified range.', function(v, elm) {
  458. var minValue, maxValue;
  459. if (Validation.get('IsEmpty').test(v)) {
  460. return true;
  461. } else if (Validation.get('validate-digits').test(v)) {
  462. minValue = maxValue = parseNumber(v);
  463. } else {
  464. var ranges = /^(-?\d+)?-(-?\d+)?$/.exec(v);
  465. if (ranges) {
  466. minValue = parseNumber(ranges[1]);
  467. maxValue = parseNumber(ranges[2]);
  468. if (minValue > maxValue) {
  469. return false;
  470. }
  471. } else {
  472. return false;
  473. }
  474. }
  475. var reRange = /^range-(-?\d+)?-(-?\d+)?$/,
  476. result = true;
  477. $w(elm.className).each(function(name) {
  478. var validRange = reRange.exec(name);
  479. if (validRange) {
  480. var minValidRange = parseNumber(validRange[1]);
  481. var maxValidRange = parseNumber(validRange[2]);
  482. result = result
  483. && (isNaN(minValidRange) || minValue >= minValidRange)
  484. && (isNaN(maxValidRange) || maxValue <= maxValidRange);
  485. }
  486. });
  487. return result;
  488. }],
  489. ['validate-alpha', 'Please use letters only (a-z or A-Z) in this field.', function (v) {
  490. return Validation.get('IsEmpty').test(v) || /^[a-zA-Z]+$/.test(v)
  491. }],
  492. ['validate-code', 'Please use only lowercase letters (a-z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.', function (v) {
  493. return Validation.get('IsEmpty').test(v) || /^[a-z]+[a-z0-9_]+$/.test(v)
  494. }],
  495. ['validate-alphanum', 'Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.', function(v) {
  496. return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9]+$/.test(v)
  497. }],
  498. ['validate-alphanum-with-spaces', 'Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.', function(v) {
  499. return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9 ]+$/.test(v)
  500. }],
  501. ['validate-street', 'Please use only letters (a-z or A-Z), numbers (0-9), spaces and "#" in this field.', function(v) {
  502. return Validation.get('IsEmpty').test(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v)
  503. }],
  504. ['validate-phoneStrict', 'Please enter a valid phone number (Ex: 123-456-7890).', function(v) {
  505. return Validation.get('IsEmpty').test(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
  506. }],
  507. ['validate-phoneLax', 'Please enter a valid phone number (Ex: 123-456-7890).', function(v) {
  508. return Validation.get('IsEmpty').test(v) || /^((\d[-. ]?)?((\(\d{3}\))|\d{3}))?[-. ]?\d{3}[-. ]?\d{4}$/.test(v);
  509. }],
  510. ['validate-fax', 'Please enter a valid fax number (Ex: 123-456-7890).', function(v) {
  511. return Validation.get('IsEmpty').test(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
  512. }],
  513. ['validate-date', 'Please enter a valid date.', function(v) {
  514. var test = new Date(v);
  515. return Validation.get('IsEmpty').test(v) || !isNaN(test);
  516. }],
  517. ['validate-date-range', 'Make sure the To Date is later than or the same as the From Date.', function(v, elm) {
  518. var m = /\bdate-range-(\w+)-(\w+)\b/.exec(elm.className);
  519. if (!m || m[2] == 'to' || Validation.get('IsEmpty').test(v)) {
  520. return true;
  521. }
  522. var currentYear = new Date().getFullYear() + '';
  523. var normalizedTime = function(v) {
  524. v = v.split(/[.\/]/);
  525. if (v[2] && v[2].length < 4) {
  526. v[2] = currentYear.substr(0, v[2].length) + v[2];
  527. }
  528. return new Date(v.join('/')).getTime();
  529. };
  530. var dependentElements = Element.select(elm.form, '.validate-date-range.date-range-' + m[1] + '-to');
  531. return !dependentElements.length || Validation.get('IsEmpty').test(dependentElements[0].value)
  532. || normalizedTime(v) <= normalizedTime(dependentElements[0].value);
  533. }],
  534. ['validate-email', 'Please enter a valid email address (Ex: johndoe@domain.com).', function (v) {
  535. //return Validation.get('IsEmpty').test(v) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(v)
  536. //return Validation.get('IsEmpty').test(v) || /^[\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9][\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9\.]{1,30}[\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9]@([a-z0-9_-]{1,30}\.){1,5}[a-z]{2,4}$/i.test(v)
  537. return Validation.get('IsEmpty').test(v) || /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i.test(v)
  538. }],
  539. ['validate-emailSender', 'Please use only visible characters and spaces.', function (v) {
  540. return Validation.get('IsEmpty').test(v) || /^[\S ]+$/.test(v)
  541. }],
  542. ['validate-password', 'Please enter 6 or more characters. Leading and trailing spaces will be ignored.', function(v) {
  543. var pass=v.strip(); /*strip leading and trailing spaces*/
  544. return !(pass.length>0 && pass.length < 6);
  545. }],
  546. ['validate-admin-password', 'Please enter 7 or more characters, using both numeric and alphabetic.', function(v) {
  547. var pass=v.strip();
  548. if (0 == pass.length) {
  549. return true;
  550. }
  551. if (!(/[a-z]/i.test(v)) || !(/[0-9]/.test(v))) {
  552. return false;
  553. }
  554. return !(pass.length < 7);
  555. }],
  556. ['validate-cpassword', 'Please make sure your passwords match.', function(v) {
  557. var conf = $('confirmation') ? $('confirmation') : $$('.validate-cpassword')[0];
  558. var pass = false;
  559. if ($('password')) {
  560. pass = $('password');
  561. }
  562. var passwordElements = $$('.validate-password');
  563. for (var i = 0; i < passwordElements.size(); i++) {
  564. var passwordElement = passwordElements[i];
  565. if (passwordElement.up('form').id == conf.up('form').id) {
  566. pass = passwordElement;
  567. }
  568. }
  569. if ($$('.validate-admin-password').size()) {
  570. pass = $$('.validate-admin-password')[0];
  571. }
  572. return (pass.value == conf.value);
  573. }],
  574. ['validate-both-passwords', 'Please make sure your passwords match.', function(v, input) {
  575. var dependentInput = $(input.form[input.name == 'password' ? 'confirmation' : 'password']),
  576. isEqualValues = input.value == dependentInput.value;
  577. if (isEqualValues && dependentInput.hasClassName('validation-failed')) {
  578. Validation.test(this.className, dependentInput);
  579. }
  580. return dependentInput.value == '' || isEqualValues;
  581. }],
  582. ['validate-url', 'Please enter a valid URL. Protocol is required (http://, https:// or ftp://)', function (v) {
  583. v = (v || '').replace(/^\s+/, '').replace(/\s+$/, '');
  584. return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i.test(v)
  585. }],
  586. ['validate-clean-url', 'Please enter a valid URL (Ex: "http://www.example.com" or "www.example.com").', function (v) {
  587. return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v)
  588. }],
  589. ['validate-identifier', 'Please enter a valid URL Key (Ex: "example-page", "example-page.html" or "anotherlevel/example-page").', function (v) {
  590. return Validation.get('IsEmpty').test(v) || /^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/.test(v)
  591. }],
  592. ['validate-xml-identifier', 'Please enter a valid XML-identifier (Ex: something_1, block5, id-4).', function (v) {
  593. return Validation.get('IsEmpty').test(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v)
  594. }],
  595. ['validate-ssn', 'Please enter a valid social security number (Ex: 123-45-6789).', function(v) {
  596. return Validation.get('IsEmpty').test(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v);
  597. }],
  598. ['validate-zip-us', 'Please enter a valid zip code (Ex: 90602 or 90602-1234).', function(v) {
  599. return Validation.get('IsEmpty').test(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v);
  600. }],
  601. ['validate-zip-international', 'Please enter a valid zip code.', function(v) {
  602. //return Validation.get('IsEmpty').test(v) || /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v);
  603. return true;
  604. }],
  605. ['validate-date-au', 'Please use this date format: dd/mm/yyyy (Ex: "17/03/2006" for the 17th of March, 2006).', function(v) {
  606. if(Validation.get('IsEmpty').test(v)) return true;
  607. var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
  608. if(!regex.test(v)) return false;
  609. var d = new Date(v.replace(regex, '$2/$1/$3'));
  610. return ( parseInt(RegExp.$2, 10) == (1+d.getMonth()) ) &&
  611. (parseInt(RegExp.$1, 10) == d.getDate()) &&
  612. (parseInt(RegExp.$3, 10) == d.getFullYear() );
  613. }],
  614. ['validate-currency-dollar', 'Please enter a valid $ amount (Ex: $100.00).', function(v) {
  615. // [$]1[##][,###]+[.##]
  616. // [$]1###+[.##]
  617. // [$]0.##
  618. // [$].##
  619. return Validation.get('IsEmpty').test(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v)
  620. }],
  621. ['validate-one-required', 'Please select one of the options above.', function (v,elm) {
  622. var p = elm.parentNode;
  623. var options = p.getElementsByTagName('INPUT');
  624. return $A(options).any(function(elm) {
  625. return $F(elm);
  626. });
  627. }],
  628. ['validate-one-required-by-name', 'Please select one of the options.', function (v,elm) {
  629. var inputs = $$('input[name="' + elm.name.replace(/([\\"])/g, '\\$1') + '"]');
  630. var error = 1;
  631. for(var i=0;i<inputs.length;i++) {
  632. if((inputs[i].type == 'checkbox' || inputs[i].type == 'radio') && inputs[i].checked == true) {
  633. error = 0;
  634. }
  635. if(Validation.isOnChange && (inputs[i].type == 'checkbox' || inputs[i].type == 'radio')) {
  636. Validation.reset(inputs[i]);
  637. }
  638. }
  639. if( error == 0 ) {
  640. return true;
  641. } else {
  642. return false;
  643. }
  644. }],
  645. ['validate-not-negative-number', 'Please enter a number 0 or greater in this field.', function(v) {
  646. if (Validation.get('IsEmpty').test(v)) {
  647. return true;
  648. }
  649. v = parseNumber(v);
  650. return !isNaN(v) && v >= 0;
  651. }],
  652. ['validate-zero-or-greater', 'Please enter a number 0 or greater in this field.', function(v) {
  653. return Validation.get('validate-not-negative-number').test(v);
  654. }],
  655. ['validate-greater-than-zero', 'Please enter a number greater than 0 in this field.', function(v) {
  656. if (Validation.get('IsEmpty').test(v)) {
  657. return true;
  658. }
  659. v = parseNumber(v);
  660. return !isNaN(v) && v > 0;
  661. }],
  662. ['validate-state', 'Please select State/Province.', function(v) {
  663. return (v!=0 || v == '');
  664. }],
  665. ['validate-new-password', 'Please enter 6 or more characters. Leading and trailing spaces will be ignored.', function(v) {
  666. if (!Validation.get('validate-password').test(v)) return false;
  667. if (Validation.get('IsEmpty').test(v) && v != '') return false;
  668. return true;
  669. }],
  670. ['validate-cc-number', 'Please enter a valid credit card number.', function(v, elm) {
  671. // remove non-numerics
  672. var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_number')) + '_cc_type');
  673. if (ccTypeContainer && typeof Validation.creditCartTypes.get(ccTypeContainer.value) != 'undefined'
  674. && Validation.creditCartTypes.get(ccTypeContainer.value)[2] == false) {
  675. if (!Validation.get('IsEmpty').test(v) && Validation.get('validate-digits').test(v)) {
  676. return true;
  677. } else {
  678. return false;
  679. }
  680. }
  681. return validateCreditCard(v);
  682. }],
  683. ['validate-cc-type', 'Credit card number does not match credit card type.', function(v, elm) {
  684. // remove credit card number delimiters such as "-" and space
  685. elm.value = removeDelimiters(elm.value);
  686. v = removeDelimiters(v);
  687. var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_number')) + '_cc_type');
  688. if (!ccTypeContainer) {
  689. return true;
  690. }
  691. var ccType = ccTypeContainer.value;
  692. if (typeof Validation.creditCartTypes.get(ccType) == 'undefined') {
  693. return false;
  694. }
  695. // Other card type or switch or solo card
  696. if (Validation.creditCartTypes.get(ccType)[0]==false) {
  697. return true;
  698. }
  699. // Matched credit card type
  700. var ccMatchedType = '';
  701. Validation.creditCartTypes.each(function (pair) {
  702. if (pair.value[0] && v.match(pair.value[0])) {
  703. ccMatchedType = pair.key;
  704. throw $break;
  705. }
  706. });
  707. if(ccMatchedType != ccType) {
  708. return false;
  709. }
  710. if (ccTypeContainer.hasClassName('validation-failed') && Validation.isOnChange) {
  711. Validation.validate(ccTypeContainer);
  712. }
  713. return true;
  714. }],
  715. ['validate-cc-type-select', 'Card type does not match credit card number.', function(v, elm) {
  716. var ccNumberContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_type')) + '_cc_number');
  717. if (Validation.isOnChange && Validation.get('IsEmpty').test(ccNumberContainer.value)) {
  718. return true;
  719. }
  720. if (Validation.get('validate-cc-type').test(ccNumberContainer.value, ccNumberContainer)) {
  721. Validation.validate(ccNumberContainer);
  722. }
  723. return Validation.get('validate-cc-type').test(ccNumberContainer.value, ccNumberContainer);
  724. }],
  725. ['validate-cc-exp', 'Incorrect credit card expiration date.', function(v, elm) {
  726. var ccExpMonth = v;
  727. var ccExpYear = $(elm.id.substr(0,elm.id.indexOf('_expiration')) + '_expiration_yr').value;
  728. var currentTime = new Date();
  729. var currentMonth = currentTime.getMonth() + 1;
  730. var currentYear = currentTime.getFullYear();
  731. if (ccExpMonth < currentMonth && ccExpYear == currentYear) {
  732. return false;
  733. }
  734. return true;
  735. }],
  736. ['validate-cc-cvn', 'Please enter a valid credit card verification number.', function(v, elm) {
  737. var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_cid')) + '_cc_type');
  738. if (!ccTypeContainer) {
  739. return true;
  740. }
  741. var ccType = ccTypeContainer.value;
  742. if (typeof Validation.creditCartTypes.get(ccType) == 'undefined') {
  743. return false;
  744. }
  745. var re = Validation.creditCartTypes.get(ccType)[1];
  746. if (v.match(re)) {
  747. return true;
  748. }
  749. return false;
  750. }],
  751. ['validate-ajax', '', function(v, elm) { return true; }],
  752. ['validate-data', 'Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.', function (v) {
  753. if(v != '' && v) {
  754. return /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);
  755. }
  756. return true;
  757. }],
  758. ['validate-css-length', 'Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).', function (v) {
  759. if (v != '' && v) {
  760. return /^[0-9\.]+(px|pt|em|ex|%)?$/.test(v) && (!(/\..*\./.test(v))) && !(/\.$/.test(v));
  761. }
  762. return true;
  763. }],
  764. ['validate-length', 'Text length does not meet the specified text range.', function (v, elm) {
  765. var reMax = new RegExp(/^maximum-length-[0-9]+$/);
  766. var reMin = new RegExp(/^minimum-length-[0-9]+$/);
  767. var result = true;
  768. $w(elm.className).each(function(name, index) {
  769. if (name.match(reMax) && result) {
  770. var length = name.split('-')[2];
  771. result = (v.length <= length);
  772. }
  773. if (name.match(reMin) && result && !Validation.get('IsEmpty').test(v)) {
  774. var length = name.split('-')[2];
  775. result = (v.length >= length);
  776. }
  777. });
  778. return result;
  779. }],
  780. ['validate-percents', 'Please enter a number lower than 100.', {max:100}],
  781. ['required-file', 'Please select a file.', function(v, elm) {
  782. var result = !Validation.get('IsEmpty').test(v);
  783. if (result === false) {
  784. ovId = elm.id + '_value';
  785. if ($(ovId)) {
  786. result = !Validation.get('IsEmpty').test($(ovId).value);
  787. }
  788. }
  789. return result;
  790. }],
  791. ['validate-cc-ukss', 'Please enter issue number or start date for switch/solo card type.', function(v,elm) {
  792. var endposition;
  793. if (elm.id.match(/(.)+_cc_issue$/)) {
  794. endposition = elm.id.indexOf('_cc_issue');
  795. } else if (elm.id.match(/(.)+_start_month$/)) {
  796. endposition = elm.id.indexOf('_start_month');
  797. } else {
  798. endposition = elm.id.indexOf('_start_year');
  799. }
  800. var prefix = elm.id.substr(0,endposition);
  801. var ccTypeContainer = $(prefix + '_cc_type');
  802. if (!ccTypeContainer) {
  803. return true;
  804. }
  805. var ccType = ccTypeContainer.value;
  806. if(['SS','SM','SO'].indexOf(ccType) == -1){
  807. return true;
  808. }
  809. $(prefix + '_cc_issue').advaiceContainer
  810. = $(prefix + '_start_month').advaiceContainer
  811. = $(prefix + '_start_year').advaiceContainer
  812. = $(prefix + '_cc_type_ss_div').down('.adv-container');
  813. var ccIssue = $(prefix + '_cc_issue').value;
  814. var ccSMonth = $(prefix + '_start_month').value;
  815. var ccSYear = $(prefix + '_start_year').value;
  816. var ccStartDatePresent = (ccSMonth && ccSYear) ? true : false;
  817. if (!ccStartDatePresent && !ccIssue){
  818. return false;
  819. }
  820. return true;
  821. }]
  822. ]);
  823. function removeDelimiters (v) {
  824. v = v.replace(/\s/g, '');
  825. v = v.replace(/\-/g, '');
  826. return v;
  827. }
  828. function parseNumber(v)
  829. {
  830. if (typeof v != 'string') {
  831. return parseFloat(v);
  832. }
  833. var isDot = v.indexOf('.');
  834. var isComa = v.indexOf(',');
  835. if (isDot != -1 && isComa != -1) {
  836. if (isComa > isDot) {
  837. v = v.replace('.', '').replace(',', '.');
  838. }
  839. else {
  840. v = v.replace(',', '');
  841. }
  842. }
  843. else if (isComa != -1) {
  844. v = v.replace(',', '.');
  845. }
  846. return parseFloat(v);
  847. }
  848. /**
  849. * Hash with credit card types which can be simply extended in payment modules
  850. * 0 - regexp for card number
  851. * 1 - regexp for cvn
  852. * 2 - check or not credit card number trough Luhn algorithm by
  853. * function validateCreditCard which you can find above in this file
  854. */
  855. Validation.creditCartTypes = $H({
  856. // 'SS': [new RegExp('^((6759[0-9]{12})|(5018|5020|5038|6304|6759|6761|6763[0-9]{12,19})|(49[013][1356][0-9]{12})|(6333[0-9]{12})|(6334[0-4]\d{11})|(633110[0-9]{10})|(564182[0-9]{10}))([0-9]{2,3})?$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
  857. 'SO': [new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
  858. 'SM': [new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true],
  859. 'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
  860. 'MC': [new RegExp('^5[1-5][0-9]{14}$'), new RegExp('^[0-9]{3}$'), true],
  861. 'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],
  862. 'DI': [new RegExp('^6(011|4[4-9][0-9]|5[0-9]{2})[0-9]{12}$'), new RegExp('^[0-9]{3}$'), true],
  863. 'JCB': [new RegExp('^(3[0-9]{15}|(2131|1800)[0-9]{11})$'), new RegExp('^[0-9]{3,4}$'), true],
  864. 'OT': [false, new RegExp('^([0-9]{3}|[0-9]{4})?$'), false]
  865. });