1 /** 2 * @namespace The alv namespace. 3 */ 4 var alv = {}; 5 6 7 /** 8 * @namespace Utility module. 9 */ 10 alv.util = { 11 /** 12 * @description Trim leading and trailing whitespace characters from a string value. 13 * @param {String} pVal The value to be trimmed. 14 * @returns {String} The trimmed value of pVal. 15 */ 16 trim: function (pVal) { 17 return pVal.replace(/^\s+|\s+$/g, ""); 18 }, 19 /** 20 * @description Replace a character on position x within a given string. 21 * @param {String} pString The string that contains the character to be replaced. 22 * @param {Number} pIndex The position of the character that will be replaced. 23 * @param {String|Number} pChar The new character value. 24 * @returns {String} The string with one character replaced. 25 */ 26 replaceCharInString: function (pString, pIndex, pChar) { 27 return pString.substr(0, pIndex) + pChar.toString() + pString.substr(pIndex + pChar.toString().length); 28 }, 29 /** 30 * @description Get the value of an APEX page item if an ID selector is passed as parameter, otherwise return the parameter value. 31 * @param {String} pItem Can be either an APEX page item ID selector or a fixed value. 32 * @returns {String} The value of a page item or a fixed value. 33 */ 34 getPageItemValue: function (pItem) { 35 if (String(pItem).substring(0, 2) === "#P") { 36 return $(pItem).val(); 37 } 38 return pItem; 39 }, 40 /** 41 * @description Get the result of JavaScript expression. 42 * @param {String} pExpression A JavaScript expression in the form of a string that will be evaluated with eval(). 43 * @returns {Boolean} The boolean result of the expression. 44 */ 45 getConditionResult: function (pExpression) { 46 var expressionResult = true; 47 if (pExpression.length) { 48 expressionResult = eval(pExpression); 49 } 50 return expressionResult; 51 }, 52 /** 53 * @description Get the numeric value of a string. Return an empty string if pVal is empty. 54 * @param {String} pVal The numeric value as a string. 55 * @returns {Number|String} The numeric value of pVal or an empty string. 56 */ 57 getNumberFromString: function (pVal) { 58 if (String(pVal).length) { 59 return Number(pVal); 60 } 61 return ""; 62 }, 63 /** 64 * @description Format a DD/MM/YYYY date represented as a string to a date object. 65 * @param {String} pVal The date value as a string. 66 * @returns {Date} The date value as a date object. 67 */ 68 getDateFromString: function (pVal) { 69 var dateArray = pVal.split("/"); 70 var year = parseInt(dateArray[2]); 71 var month = parseInt(dateArray[1], 10); 72 var day = parseInt(dateArray[0], 10); 73 74 return new Date(year, month - 1, day); 75 }, 76 /** 77 * @description Convert a string date in a given date format to the DD/MM/YYYY date format. 78 * @param {String} pDate The date to be converted. 79 * @param {String} pDateFormat The date format of pDate. 80 * @returns {String} The date value in the DD/MM/YYYY format. 81 */ 82 convertDate: function (pDate, pDateFormat) { 83 var dateFormat = pDateFormat.toUpperCase(); 84 var dateFormatSeparator = dateFormat.replace(/[A-Z]+/g, ""); 85 var dateSeparator = pDate.replace(/\d+/g, ""); 86 var dayPart; 87 var monthPart; 88 var yearPart; 89 90 if (pDate.length === pDateFormat.length && dateSeparator === dateFormatSeparator) { 91 if (dateFormat.indexOf("DD") === -1) { 92 dayPart = "xx"; 93 } else { 94 dayPart = pDate.substring(dateFormat.indexOf("DD"), dateFormat.indexOf("DD") + 2); 95 } 96 97 if (dateFormat.indexOf("MM") === -1) { 98 monthPart = "xx"; 99 } else { 100 monthPart = pDate.substring(dateFormat.indexOf("MM"), dateFormat.indexOf("MM") + 2); 101 } 102 103 if (dateFormat.indexOf("YYYY") === -1) { 104 if (dateFormat.indexOf("RRRR") === -1) { 105 if (dateFormat.indexOf("YY") === -1) { 106 if (dateFormat.indexOf("RR") === -1) { 107 yearPart = "xxxx"; 108 } else { 109 yearPart = pDate.substring(dateFormat.indexOf("RR"), dateFormat.indexOf("RR") + 2); 110 } 111 } else { 112 yearPart = pDate.substring(dateFormat.indexOf("YY"), dateFormat.indexOf("YY") + 2); 113 } 114 } else { 115 yearPart = pDate.substring(dateFormat.indexOf("RRRR"), dateFormat.indexOf("RRRR") + 4); 116 } 117 } else { 118 yearPart = pDate.substring(dateFormat.indexOf("YYYY"), dateFormat.indexOf("YYYY") + 4); 119 } 120 } 121 122 return dayPart + "/" + monthPart + "/" + yearPart; 123 } 124 }; 125 126 127 /** 128 * @namespace Validators module. 129 */ 130 alv.validators = { 131 util: alv.util, 132 133 /** 134 * @description Check whether a value is empty. 135 * @param {String|Number|RegExp} pVal The value to be tested. 136 * @returns {Boolean} 137 */ 138 isEmpty: function (pVal) { 139 return pVal === ""; 140 }, 141 /** 142 * @description Check for equality between two values. 143 * @param {String} pVal The first value. 144 * @param {String} pVal2 The second value. 145 * @returns {Boolean} 146 */ 147 isEqual: function (pVal, pVal2) { 148 return pVal === pVal2; 149 }, 150 /** 151 * @description Check whether a value matches a regular expression. 152 * @param {String|Number} pVal The value to be tested. 153 * @param {String|RegExp} pRegex The regular expression. 154 * @returns {Boolean} 155 */ 156 regex: function (pVal, pRegex) { 157 return new RegExp(pRegex).test(pVal) || this.isEmpty(pVal); 158 }, 159 /** 160 * @description Check whether a value contains only alphanumeric characters. 161 * @param {String} pVal The value to be tested. 162 * @returns {Boolean} 163 */ 164 isAlphanumeric: function (pVal) { 165 return this.regex(pVal, /^[a-z0-9]+$/i); 166 }, 167 /** 168 * @description Check whether a value is a valid number. 169 * @param {Number} pVal The value to be tested. 170 * @returns {Boolean} 171 */ 172 isNumber: function (pVal) { 173 return this.regex(pVal, /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/); 174 }, 175 /** 176 * @description Check whether a value contains only digit characters. 177 * @param {String} pVal The value to be tested. 178 * @returns {Boolean} 179 */ 180 isDigit: function (pVal) { 181 return this.regex(pVal, /^\d+$/); 182 }, 183 /** 184 * @description Check whether a value is a valid e-mail address. 185 * @param {String} pVal The value to be tested. 186 * @returns {Boolean} 187 */ 188 isEmail: function (pVal) { 189 return this.regex(pVal, /^[^\s@]+@[^\s@]+\.[^\s@]+$/); 190 }, 191 /** 192 * @description Check whether a value is a valid URL. 193 * @param {String} pVal The value to be tested. 194 * @returns {Boolean} 195 */ 196 isUrl: function (pVal) { 197 return this.regex(pVal, /(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/); 198 }, 199 /** 200 * @description Check whether a value is a valid date. 201 * @param {String} pVal The value to be tested. 202 * @param {String} pDateFormat The date format of pVal. 203 * @returns {Boolean} 204 */ 205 isDate: function (pVal, pDateFormat) { 206 var dateRegex = new RegExp("^(3[01]|[12][0-9]|0?[1-9])/(1[0-2]|0?[1-9])/(?:[0-9]{2})?[0-9]{2}$"); 207 var convertedDate = this.util.convertDate(pVal, pDateFormat); 208 209 if (convertedDate.match(dateRegex)) { 210 var dateArray = convertedDate.split("/"); 211 var year = parseInt(dateArray[2]); 212 var month = parseInt(dateArray[1], 10); 213 var day = parseInt(dateArray[0], 10); 214 var date = new Date(year, month - 1, day); 215 216 if (((date.getMonth() + 1) === month) && (date.getDate() === day) && (date.getFullYear() === year)) { 217 return true; 218 } 219 } 220 return this.isEmpty(pVal); 221 }, 222 /** 223 * @description Check whether the character length of a value is not lower than pMin. 224 * @param {String} pVal The value to be tested. 225 * @param {Number} pMin The minimum character length. 226 * @returns {Boolean} 227 */ 228 minLength: function (pVal, pMin) { 229 return pVal.length >= pMin || this.isEmpty(pVal); 230 }, 231 /** 232 * @description Check whether the character length of a value is not higher than pMax. 233 * @param {String} pVal The value to be tested. 234 * @param {Number} pMax The maximum character length. 235 * @returns {Boolean} 236 */ 237 maxLength: function (pVal, pMax) { 238 return pVal.length <= pMax || this.isEmpty(pVal); 239 }, 240 /** 241 * @description Check whether the character length of a value lies between pMin and pMax. 242 * @param {String} pVal The value to be tested. 243 * @param {Number} pMin The minimum character length. 244 * @param {Number} pMax The maximum character length. 245 * @returns {Boolean} 246 */ 247 rangeLength: function (pVal, pMin, pMax) { 248 return this.minLength(pVal, pMin) && this.maxLength(pVal, pMax) || this.isEmpty(pVal); 249 }, 250 /** 251 * @description Check whether a numeric value is not lower than pMin. 252 * @param {Number} pVal The value to be tested. 253 * @param {Number} pMin The minimum number. 254 * @returns {Boolean} 255 */ 256 minNumber: function (pVal, pMin) { 257 if (!this.isEmpty(pVal) && !this.isEmpty(pMin)) { 258 if (this.isNumber(pVal) && this.isNumber(pMin)) { 259 return pVal >= pMin; 260 } 261 } 262 return true; 263 }, 264 /** 265 * @description Check whether a numeric value is not higher than pMax. 266 * @param {Number} pVal The value to be tested. 267 * @param {Number} pMax The maximum number. 268 * @returns {Boolean} 269 */ 270 maxNumber: function (pVal, pMax) { 271 if (!this.isEmpty(pVal) && !this.isEmpty(pMax)) { 272 if (this.isNumber(pVal) && this.isNumber(pMax)) { 273 return pVal <= pMax; 274 } 275 } 276 return true; 277 }, 278 /** 279 * @description Check whether a numeric value lies between pMin and pMax. 280 * @param {Number} pVal The value to be tested. 281 * @param {Number} pMin The minimum number. 282 * @param {Number} pMax The maximum number. 283 * @returns {Boolean} 284 */ 285 rangeNumber: function (pVal, pMin, pMax) { 286 if (!this.isEmpty(pVal) && !this.isEmpty(pMin) && !this.isEmpty(pMax)) { 287 if (this.isNumber(pVal) && this.isNumber(pMin) && this.isNumber(pMax)) { 288 if (pMax >= pMin) { 289 return this.minNumber(pVal, pMin) && this.maxNumber(pVal, pMax); 290 } 291 } 292 } 293 return true; 294 }, 295 /** 296 * @description Check whether the amount of selected checkboxes is not lower than pMin. 297 * @param {String} pChoices The ID selector of the checkbox group that will be validated. 298 * @param {Number} pMin The minimum amount of selected checkboxes. 299 * @param {Boolean} pEmptyAllowed Indicate whether it is allowed to have no checkbox selected. 300 * @returns {Boolean} 301 */ 302 minCheck: function (pChoices, pMin, pEmptyAllowed) { 303 var totalChecked = $(pChoices).filter(':checked').length; 304 305 if (pEmptyAllowed) { 306 return this.minNumber(totalChecked, pMin) || totalChecked === 0; 307 } else { 308 return this.minNumber(totalChecked, pMin); 309 } 310 }, 311 /** 312 * @description Check whether the amount of selected checkboxes is not higher than pMax. 313 * @param {String} pChoices The ID selector of the checkbox group that will be validated. 314 * @param {Number} pMax The maximum amount of selected checkboxes. 315 * @returns {Boolean} 316 */ 317 maxCheck: function (pChoices, pMax) { 318 var totalChecked = $(pChoices).filter(':checked').length; 319 return this.maxNumber(totalChecked, pMax) || totalChecked === 0; 320 }, 321 /** 322 * @description Check whether the amount of selected checkboxes lies pMin and pMax. 323 * @param {String} pChoices The ID selector of the checkbox group that will be validated. 324 * @param {Number} pMin The minimum amount of selected checkboxes. 325 * @param {Number} pMax The maximum amount of selected checkboxes. 326 * @returns {Boolean} 327 */ 328 rangeCheck: function (pChoices, pMin, pMax) { 329 var totalChecked = $(pChoices).filter(':checked').length; 330 return this.rangeNumber(totalChecked, pMin, pMax) || totalChecked === 0; 331 }, 332 /** 333 * @description Check whether a date does not lie before pMin. 334 * @param {String} pVal The date to be tested. 335 * @param {String} pMin The minimum date. 336 * @param {String} pDateFormat The date format. 337 * @returns {Boolean} 338 */ 339 minDate: function (pVal, pMin, pDateFormat) { 340 var date = new Date(); 341 var minimumDate = new Date(); 342 343 if (!this.isEmpty(pVal) && !this.isEmpty(pMin)) { 344 if (this.isDate(pVal, pDateFormat) && this.isDate(pMin, pDateFormat)) { 345 date = this.util.getDateFromString(this.util.convertDate(pVal, pDateFormat)); 346 minimumDate = this.util.getDateFromString(this.util.convertDate(pMin, pDateFormat)); 347 348 return date >= minimumDate; 349 } 350 } 351 return true; 352 }, 353 /** 354 * @description Check whether a date does not lie after pMax. 355 * @param {String} pVal The date to be tested. 356 * @param {String} pMax The maximum date. 357 * @param {String} pDateFormat The date format. 358 * @returns {Boolean} 359 */ 360 maxDate: function (pVal, pMax, pDateFormat) { 361 var date = new Date(); 362 var maximumDate = new Date(); 363 364 if (!this.isEmpty(pVal) && !this.isEmpty(pMax)) { 365 if (this.isDate(pVal, pDateFormat) && this.isDate(pMax, pDateFormat)) { 366 date = this.util.getDateFromString(this.util.convertDate(pVal, pDateFormat)); 367 maximumDate = this.util.getDateFromString(this.util.convertDate(pMax, pDateFormat)); 368 369 return date <= maximumDate; 370 } 371 } 372 return true; 373 }, 374 /** 375 * @description Check whether a date lies between pMin and pMax. 376 * @param {String} pVal The date to be tested. 377 * @param {String} pMin The minimum date. 378 * @param {String} pMax The maximum date. 379 * @param {String} pDateFormat The date format. 380 * @returns {Boolean} 381 */ 382 rangeDate: function (pVal, pMin, pMax, pDateFormat) { 383 var date = new Date(); 384 var minimumDate = new Date(); 385 var maximumDate = new Date(); 386 387 if (!this.isEmpty(pVal) && !this.isEmpty(pMin) && !this.isEmpty(pMax)) { 388 if (this.isDate(pVal, pDateFormat) && this.isDate(pMin, pDateFormat) && this.isDate(pMax, pDateFormat)) { 389 date = this.util.getDateFromString(this.util.convertDate(pVal, pDateFormat)); 390 minimumDate = this.util.getDateFromString(this.util.convertDate(pMin, pDateFormat)); 391 maximumDate = this.util.getDateFromString(this.util.convertDate(pMax, pDateFormat)); 392 393 if (maximumDate >= minimumDate) { 394 return date >= minimumDate && date <= maximumDate; 395 } 396 } 397 } 398 return true; 399 } 400 }; 401 402 403 /** 404 * @namespace The APEX Live Validation jQuery plugin. 405 */ 406 (function ($, util, validators) { 407 "use strict"; 408 409 $.fn.alv = function (method, options) { 410 var constants = { 411 'pluginId': "be.ctb.jq.alv", 412 'pluginName': "APEX Live Validation", 413 'pluginPrefix': "alv", 414 415 // apex related 416 'apexCheckboxClass': 'checkbox_group', 417 'apexRadioClass': 'radio_group', 418 'apexShuttleClass': 'shuttle' 419 }; 420 $.extend(constants, { 421 // element data keys 422 'validationEvents': constants.pluginPrefix + "-valEvents", 423 'validationResults': constants.pluginPrefix + "-valResults", 424 'origClickEvent': constants.pluginPrefix + "-origClickEvent", 425 426 // validation identifiers 427 'notEmptyClass': constants.pluginPrefix + "-notEmpty", 428 'itemTypeClass': constants.pluginPrefix + "-itemType", 429 'equalClass': constants.pluginPrefix + "-equal", 430 'regexClass': constants.pluginPrefix + "-regex", 431 'charLengthClass': constants.pluginPrefix + "-charLength", 432 'numberSizeClass': constants.pluginPrefix + "-numberSize", 433 'dateOrderClass': constants.pluginPrefix + "-dateOrder", 434 'totalCheckedClass': constants.pluginPrefix + "-totalChecked", 435 436 // css classes 437 'itemErrorClass': constants.pluginPrefix + "-item-error", 438 'labelErrorClass': constants.pluginPrefix + "-label-error", 439 'errorMsgClass': constants.pluginPrefix + "-error-msg" 440 }); 441 442 var settings = { 443 'validate': 'notEmpty', 444 'triggeringEvent': 'blur', 445 'condition': '', 446 'validationMinLength': 0, 447 'errorMsg': '', 448 'errorMsgLocation': 'after', 449 'allowWhitespace': true, 450 'itemType': '', 451 'dateFormat': '', 452 'min': '', 453 'max': '', 454 'equal': '', 455 'regex': '', 456 'formsToSubmit': '' 457 }; 458 459 var methods = { 460 /** 461 * @description Public function to validate one or more page items. 462 * @param {Object} options The validation settings. 463 */ 464 init: function (options) { 465 var element = $(this); 466 bindSettings(element, options); 467 init(element); 468 }, 469 /** 470 * @description Public function to validate one or more forms. 471 * @param {Object} options The validation settings. 472 */ 473 validateForm: function (options) { 474 var element = $(this); 475 bindSettings(element, options); 476 validateFormBeforeSubmit(element); 477 }, 478 /** 479 * @description Public function to remove existing validations from an element. 480 */ 481 remove: function () { 482 var element = $(this); 483 if (restorePluginSettings(element)) { 484 method(); 485 } 486 } 487 }; 488 489 function restorePluginSettings(element) { 490 var elem = $(element); 491 if (typeof elem.data(constants.pluginId) !== "undefined") { 492 $.extend(settings, elem.data(constants.pluginId)); 493 return true; 494 } 495 return false; 496 } 497 498 function extendSettings(options) { 499 if (options) { 500 $.extend(settings, options); 501 } 502 } 503 504 function bindSettings(element, options) { 505 extendSettings(options); 506 $(element).data(constants.pluginId, settings); 507 } 508 509 return $(this).each(function () { 510 if (methods[method]) { 511 return methods[method].call($(this), options); 512 } else if (typeof method === "object" || !method) { 513 return methods.init.call($(this), method); 514 } else { 515 $.error("Method " + method + " does not exist on jQuery. " + constants.pluginName); 516 return false; 517 } 518 }); 519 520 function init(element) { 521 var elem = $(element); 522 var elemSelector = '#' + elem.attr('id'); 523 var bodyElem = $('body'); 524 var triggeringEvent = settings.triggeringEvent + '.' + constants.pluginPrefix; 525 var changeEvent = 'change' + '.' + constants.pluginPrefix; 526 527 switch (settings.validate) { 528 case 'notEmpty': 529 if (elem.hasClass(constants.apexCheckboxClass) || 530 elem.hasClass(constants.apexRadioClass) || 531 elem.hasClass(constants.apexShuttleClass) || 532 elem.prop('tagName') === 'SELECT' || 533 elem.attr('type') === 'file') { 534 if (settings.triggeringEvent !== 'change') { 535 triggeringEvent = triggeringEvent + ' ' + changeEvent; 536 } 537 } 538 bodyElem.delegate(elemSelector, triggeringEvent, isEmptyHandler); 539 break; 540 case 'itemType': 541 if (settings.itemType === 'date') { 542 if (settings.triggeringEvent !== 'change') { 543 triggeringEvent = triggeringEvent + ' ' + changeEvent; 544 } 545 } 546 bodyElem.delegate(elemSelector, triggeringEvent, itemTypeHandler); 547 break; 548 case 'equal': 549 bodyElem.delegate(elemSelector, triggeringEvent, isEqualHandler); 550 break; 551 case 'regex': 552 bodyElem.delegate(elemSelector, triggeringEvent, regexHandler); 553 break; 554 case 'charLength': 555 bodyElem.delegate(elemSelector, triggeringEvent, charLengthHandler); 556 break; 557 case 'numberSize': 558 bodyElem.delegate(elemSelector, triggeringEvent, numberSizeHandler); 559 break; 560 case 'dateOrder': 561 if (settings.triggeringEvent !== 'change') { 562 triggeringEvent = triggeringEvent + ' ' + changeEvent; 563 } 564 bodyElem.delegate(elemSelector, triggeringEvent, dateOrderHandler); 565 break; 566 case 'totalChecked': 567 bodyElem.delegate(elemSelector, changeEvent, totalCheckedHandler); 568 break; 569 default: 570 } 571 572 addValidationEvent(elem, triggeringEvent); 573 return element; 574 } 575 576 function addValidationEvent(pElem, pEvent) { 577 var elem = $(pElem); 578 var elemValidationEvents = elem.data(constants.validationEvents); 579 var eventExists = false; 580 581 if (typeof elemValidationEvents !== "undefined") { 582 $.each(elemValidationEvents.split(" "), function (index, value) { 583 if (value === pEvent) { 584 eventExists = true; 585 } 586 }); 587 if (!eventExists) { 588 elem.data(constants.validationEvents, elemValidationEvents + " " + pEvent); 589 } 590 } else { 591 elem.data(constants.validationEvents, pEvent); 592 } 593 } 594 595 596 // VALIDATION HANDLERS 597 function isEmptyHandler() { 598 var itemEmpty; 599 var emptyMsg = setMsg(settings.errorMsg, "value required"); 600 601 if (allowValidation(this, constants.notEmptyClass)) { 602 if ($(this).hasClass(constants.apexCheckboxClass) || 603 $(this).hasClass(constants.apexRadioClass)) { 604 itemEmpty = !validators.minCheck($(this).find(':checkbox, :radio'), 1, false); 605 } else if ($(this).hasClass(constants.apexShuttleClass)) { 606 itemEmpty = !$(this).find('select.shuttle_right').children().length; 607 } else if ($(this).prop('tagName') === 'SELECT' || $(this).attr('type') === 'file') { 608 itemEmpty = validators.isEmpty(this.value); 609 } else { 610 if (settings.allowWhitespace) { 611 itemEmpty = validators.isEmpty(this.value); 612 } else { 613 itemEmpty = validators.isEmpty(util.trim(this.value)); 614 } 615 } 616 617 if (itemEmpty && util.getConditionResult(settings.condition)) { 618 addValidationResult($(this), constants.notEmptyClass, "0"); 619 showMessage(this, emptyMsg); 620 } else { 621 addValidationResult($(this), constants.notEmptyClass, "1"); 622 hideMessage(this); 623 } 624 } 625 } 626 627 function isEqualHandler() { 628 var equalMsg = setMsg(settings.errorMsg, "values do not equal"); 629 630 if (allowValidation(this, constants.equalClass)) { 631 if (validators.minLength(this.value, settings.validationMinLength)) { 632 if (!validators.isEqual(this.value, $(settings.equal).val()) && util.getConditionResult(settings.condition)) { 633 addValidationResult($(this), constants.equalClass, "0"); 634 showMessage(this, equalMsg); 635 } else { 636 addValidationResult($(this), constants.equalClass, "1"); 637 hideMessage(this); 638 } 639 } 640 } 641 } 642 643 function regexHandler() { 644 var regexMsg = setMsg(settings.errorMsg, "invalid value"); 645 646 if (allowValidation(this, constants.regexClass)) { 647 if (validators.minLength(this.value, settings.validationMinLength)) { 648 if (!validators.regex(this.value, settings.regex) && util.getConditionResult(settings.condition)) { 649 addValidationResult($(this), constants.regexClass, "0"); 650 showMessage(this, regexMsg); 651 } else { 652 addValidationResult($(this), constants.regexClass, "1"); 653 hideMessage(this); 654 } 655 } 656 } 657 } 658 659 function itemTypeHandler() { 660 var itemTypeOk; 661 var itemTypeErrorMsg; 662 663 if (allowValidation(this, constants.itemTypeClass)) { 664 if (validators.minLength(this.value, settings.validationMinLength)) { 665 switch (settings.itemType) { 666 case 'alphanumeric': 667 itemTypeOk = validators.isAlphanumeric(this.value); 668 itemTypeErrorMsg = setMsg(settings.errorMsg, "not an alphanumeric value"); 669 break; 670 case 'number': 671 itemTypeOk = validators.isNumber(this.value); 672 itemTypeErrorMsg = setMsg(settings.errorMsg, "not a valid number"); 673 break; 674 case 'digit': 675 itemTypeOk = validators.isDigit(this.value); 676 itemTypeErrorMsg = setMsg(settings.errorMsg, "not a valid digit combination"); 677 break; 678 case 'email': 679 itemTypeOk = validators.isEmail(this.value); 680 itemTypeErrorMsg = setMsg(settings.errorMsg, "not a valid e-mail address"); 681 break; 682 case 'url': 683 itemTypeOk = validators.isUrl(this.value); 684 itemTypeErrorMsg = setMsg(settings.errorMsg, "not a valid URL"); 685 break; 686 case 'date': 687 itemTypeOk = validators.isDate(this.value, settings.dateFormat); 688 itemTypeErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "not a valid date (&1)"), settings.dateFormat); 689 break; 690 default: 691 } 692 693 if (!itemTypeOk && util.getConditionResult(settings.condition)) { 694 addValidationResult($(this), constants.itemTypeClass, "0"); 695 showMessage(this, itemTypeErrorMsg); 696 } else { 697 addValidationResult($(this), constants.itemTypeClass, "1"); 698 hideMessage(this); 699 } 700 } 701 } 702 } 703 704 function charLengthHandler() { 705 var charLengthOk; 706 var charLengthErrorMsg; 707 708 if (allowValidation(this, constants.charLengthClass)) { 709 if (validators.minLength(this.value, settings.validationMinLength)) { 710 if (validators.isEmpty(settings.max)) { 711 charLengthOk = validators.minLength(this.value, settings.min); 712 charLengthErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "value length too short - min. &1"), settings.min); 713 } else if (validators.isEmpty(settings.min)) { 714 charLengthOk = validators.maxLength(this.value, settings.max); 715 charLengthErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "value length too long - max. &1"), settings.max); 716 } else { 717 charLengthOk = validators.rangeLength(this.value, settings.min, settings.max); 718 charLengthErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "invalid value length - between &1 and &2 only"), settings.min, settings.max); 719 } 720 721 if (!charLengthOk && util.getConditionResult(settings.condition)) { 722 addValidationResult($(this), constants.charLengthClass, "0"); 723 showMessage(this, charLengthErrorMsg); 724 } else { 725 addValidationResult($(this), constants.charLengthClass, "1"); 726 hideMessage(this); 727 } 728 } 729 } 730 } 731 732 function numberSizeHandler() { 733 var numberSizeOk; 734 var numberSizeErrorMsg; 735 var value = util.getNumberFromString(this.value); 736 var min = util.getNumberFromString(util.getPageItemValue(settings.min)); 737 var max = util.getNumberFromString(util.getPageItemValue(settings.max)); 738 739 if (allowValidation(this, constants.numberSizeClass)) { 740 if (validators.minLength(this.value, settings.validationMinLength)) { 741 if (validators.isEmpty(settings.max)) { 742 numberSizeOk = validators.minNumber(value, min); 743 numberSizeErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "number too small - min. &1"), min); 744 } else if (validators.isEmpty(settings.min)) { 745 numberSizeOk = validators.maxNumber(value, max); 746 numberSizeErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "number too large - max. &1"), max); 747 } else { 748 numberSizeOk = validators.rangeNumber(value, min, max); 749 numberSizeErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "invalid number size - between &1 and &2 only"), min, max); 750 } 751 752 if (!numberSizeOk && util.getConditionResult(settings.condition)) { 753 addValidationResult($(this), constants.numberSizeClass, "0"); 754 showMessage(this, numberSizeErrorMsg); 755 } else { 756 addValidationResult($(this), constants.numberSizeClass, "1"); 757 hideMessage(this); 758 } 759 } 760 } 761 } 762 763 function totalCheckedHandler() { 764 var totalCheckedOk; 765 var totalCheckedErrorMsg; 766 var choices = $(this).find(':checkbox, :radio'); 767 768 if (allowValidation(this, constants.totalCheckedClass)) { 769 if (validators.isEmpty(settings.max)) { 770 totalCheckedOk = validators.minCheck(choices, settings.min, true); 771 totalCheckedErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "please select at least &1 choice(s)"), settings.min); 772 } else if (validators.isEmpty(settings.min)) { 773 totalCheckedOk = validators.maxCheck(choices, settings.max); 774 totalCheckedErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "please select no more than &1 choice(s)"), settings.max); 775 } else { 776 totalCheckedOk = validators.rangeCheck(choices, settings.min, settings.max); 777 totalCheckedErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "please select between &1 and &2 choice(s)"), settings.min, settings.max); 778 } 779 780 if (!totalCheckedOk && util.getConditionResult(settings.condition)) { 781 addValidationResult($(this), constants.totalCheckedClass, "0"); 782 showMessage(this, totalCheckedErrorMsg); 783 } else { 784 addValidationResult($(this), constants.totalCheckedClass, "1"); 785 hideMessage(this); 786 } 787 } 788 } 789 790 function dateOrderHandler() { 791 var dateOrderOk; 792 var dateOrderErrorMsg; 793 var min = util.getPageItemValue(settings.min); 794 var max = util.getPageItemValue(settings.max); 795 796 if (allowValidation(this, constants.dateOrderClass)) { 797 if (validators.minLength(this.value, settings.validationMinLength)) { 798 if (validators.isEmpty(settings.max)) { 799 dateOrderOk = validators.minDate(this.value, min, settings.dateFormat); 800 dateOrderErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "this date should lie after &1"), min); 801 } else if (validators.isEmpty(settings.min)) { 802 dateOrderOk = validators.maxDate(this.value, max, settings.dateFormat); 803 dateOrderErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "this date should lie before &1"), max); 804 } else { 805 dateOrderOk = validators.rangeDate(this.value, min, max, settings.dateFormat); 806 dateOrderErrorMsg = replaceMsgVars(setMsg(settings.errorMsg, "this date should lie between &1 and &2"), min, max); 807 } 808 809 if (!dateOrderOk && util.getConditionResult(settings.condition)) { 810 addValidationResult($(this), constants.dateOrderClass, "0"); 811 showMessage(this, dateOrderErrorMsg); 812 } else { 813 addValidationResult($(this), constants.dateOrderClass, "1"); 814 hideMessage(this); 815 } 816 } 817 } 818 } 819 820 821 // ERROR MESSAGE 822 function showMessage(pElem, pMessage) { 823 var elem = $(pElem); 824 var errorMsgHtml = "<span class=\"" + constants.errorMsgClass + " " + pElem.id + "\">" + pMessage + "</span>"; 825 826 if (elem.hasClass(constants.itemErrorClass)) { 827 var errorMsgElem = $('span.' + constants.errorMsgClass + '.' + pElem.id); 828 var errorMsgElemIndex = errorMsgElem.index(); 829 var elemIndex = elem.index(); 830 831 if (errorMsgElemIndex < elemIndex && settings.errorMsgLocation === 'before') { 832 errorMsgElem.text(pMessage); 833 } else if (errorMsgElemIndex < elemIndex && settings.errorMsgLocation === 'after') { 834 errorMsgElem.remove(); 835 elem.after(errorMsgHtml); 836 } else if (errorMsgElemIndex > elemIndex && settings.errorMsgLocation === 'after') { 837 errorMsgElem.text(pMessage); 838 } else { 839 errorMsgElem.remove(); 840 elem.before(errorMsgHtml); 841 } 842 } else { 843 elem.addClass(constants.itemErrorClass); 844 $('[for=' + pElem.id + ']').addClass(constants.labelErrorClass); 845 if (settings.errorMsgLocation === 'before') { 846 elem.before(errorMsgHtml); 847 } else { 848 elem.after(errorMsgHtml); 849 } 850 } 851 } 852 853 function hideMessage(pElem) { 854 var elem = $(pElem); 855 if (elem.hasClass(constants.itemErrorClass)) { 856 elem.removeClass(constants.itemErrorClass); 857 $('[for=' + pElem.id + ']').removeClass(constants.labelErrorClass); 858 $('span.' + constants.errorMsgClass + '.' + pElem.id).remove(); 859 } 860 } 861 862 function setMsg(customMsg, defaultMsg) { 863 if (!validators.isEmpty(customMsg)) { 864 return customMsg; 865 } 866 return defaultMsg; 867 } 868 869 function replaceMsgVars(pMessage) { 870 var errorMsg = pMessage; 871 for (var i = 1, j = arguments.length; i < j; i++) { 872 errorMsg = errorMsg.replace("&" + i, arguments[i]); 873 } 874 return errorMsg; 875 } 876 877 878 // ERROR CONTROL 879 function allowValidation(pElem, pKey) { 880 var allowValidation = true; 881 var elem = $(pElem); 882 var elemValidationResults = elem.data(constants.validationResults); 883 884 if (typeof elemValidationResults !== "undefined") { 885 if (elemValidationResults.indexOf(pKey) === -1) { 886 $.each(elemValidationResults.split(" "), function (index, value) { 887 if (allowValidation === true && value.slice(-1) !== "1") { 888 allowValidation = false; 889 } 890 }); 891 } else { 892 elem.removeData(constants.validationResults); 893 } 894 } else { 895 addValidationResult(pElem, pKey, "1"); 896 } 897 898 return allowValidation; 899 } 900 901 function addValidationResult(pElem, pValidation, pResult) { 902 var elem = $(pElem); 903 var elemValidationResults = elem.data(constants.validationResults); 904 var resultExists = false; 905 var validationResult = pValidation + ":" + pResult; 906 907 if (typeof elemValidationResults !== "undefined") { 908 $.each(elemValidationResults.split(" "), function (index, value) { 909 if (value.substr(0, value.indexOf(":")) === pValidation) { 910 var resultIndex = elemValidationResults.indexOf(value) + value.length - 1; 911 elemValidationResults = util.replaceCharInString(elemValidationResults, resultIndex, pResult); 912 elem.data(constants.validationResults, elemValidationResults); 913 resultExists = true; 914 } 915 }); 916 if (!resultExists) { 917 elem.data(constants.validationResults, elemValidationResults + " " + validationResult); 918 } 919 } else { 920 elem.data(constants.validationResults, validationResult); 921 } 922 } 923 924 925 // FORM VALIDATION 926 function formHasErrors(pForms) { 927 var formHasErrors = false; 928 var formElem; 929 var formElems = $(pForms).find('input, textarea, select, fieldset'); 930 931 $.each(formElems, function () { 932 formElem = $(this); 933 if (typeof formElem.data(constants.validationEvents) !== "undefined") { 934 $.each(formElem.data(constants.validationEvents).split(" "), function (index, value) { 935 formElem.trigger(value); 936 }); 937 } 938 }); 939 940 if (formElems.hasClass(constants.itemErrorClass)) { 941 $(formElems).filter('.' + constants.itemErrorClass).first().focus(); 942 formHasErrors = true; 943 } 944 945 return formHasErrors; 946 } 947 948 function validateFormBeforeSubmit(pFiringElem) { 949 var firingElem = $(pFiringElem); 950 var origClickEvent; 951 var fixErrorsMsg = setMsg(settings.errorMsg, "Please fix all errors before continuing"); 952 var bodyElem = $('body'); 953 var messageBoxId = "#alv-msg-box"; 954 var msgBox = '<div class="alv-alert-msg"><a href="#" class="alv-close" onclick="$(\'' + messageBoxId + '\').children().fadeOut();return false;">x</a><p>' + fixErrorsMsg + '</p></div>'; 955 956 if (firingElem.length) { 957 if (firingElem.prop('tagName') === "A") { 958 origClickEvent = firingElem.attr('href'); 959 firingElem.data(constants.origClickEvent, origClickEvent); 960 firingElem.removeAttr('href'); 961 } else { 962 origClickEvent = firingElem.attr('onclick'); 963 firingElem.data(constants.origClickEvent, origClickEvent); 964 firingElem.removeAttr('onclick'); 965 } 966 967 bodyElem.delegate('#' + firingElem.attr('id'), 'click', function () { 968 if (!formHasErrors(settings.formsToSubmit)) { 969 eval($(this).data(constants.origClickEvent)); 970 } else { 971 if (!$(messageBoxId).length) { 972 bodyElem.append('<div id="' + messageBoxId.substring(1) + '"></div>'); 973 } 974 $(messageBoxId).html(msgBox); 975 } 976 }); 977 } 978 } 979 } 980 })(jQuery, alv.util, alv.validators);