1function isTrue(value) { 2 if (value == null || value === undefined) { 3 return false; 4 } 5 6 switch (typeof(value)) { 7 case "boolean": return value; 8 case "number": return (value > 0); 9 case "string": 10 switch(value.trim().toLowerCase()) { 11 case "true": 12 case "1": 13 case "on": 14 case "yes": 15 return true; 16 default: 17 return false; 18 } 19 default: return false; 20 } 21} 22function isEmpty(value) { 23 if (value == null || value === undefined) { 24 return true; 25 } 26 27 switch(typeof value) { 28 case "number": 29 case "boolean": 30 case "bigint": 31 case "function": 32 return false; 33 case "string": 34 return value.trim().length === 0; 35 case "object": 36 if (Array.isArray(value)) { 37 return (value.length == 0); 38 } 39 return Object.keys(value).length === 0; 40 default: 41 return true; 42 } 43 /* Old code using jQuery - deprecated with 14.7 44 if (!jQuery || (typeof(value) === "number") || (typeof(value) === "boolean")) return false; 45 return ( 46 ($.isPlainObject(value) && $.isEmptyObject(value)) || 47 ($.isArray(value) && value.length == 0) || 48 (typeof(value) == "string" && ($.trim(value) === "")) || 49 (!value) 50 ); 51 */ 52} 53 54function isNumber(value) { 55 return (!isEmpty(value) && !isNaN(value)); 56} 57function isPositiveNumber(value) { 58 return isNumber(value)?(parseInt(value) > 0):false; 59} 60function isPositiveFloat(value) { 61 return isNumber(value)?(parseFloat(value) > 0):false; 62} 63function isNonZeroNumber(value) { 64 return isNumber(value)?(parseFloat(value) != 0):false; 65} 66function containsNumber(value) { 67 if (value == null) 68 return false; 69 else if (isNumber(value)) 70 return true; 71 else if (typeof(value) === "string") 72 return (/\d/.test(value)); 73 else 74 return false; 75} 76function replaceEmpty(object, replace) { 77 return isEmpty(object)?replace:object; 78} 79 80/* Methods for currency amounts and quantities */ 81function stripCurrency(val) { 82 if (!isEmpty(val) && (typeof(val) === "string")) { 83 val = val.replace(/[^0-9-\.]+/g,""); 84 } 85 return val; 86} 87function formatAmount(val, precision) { 88 if (!isEmpty(val)) { 89 if (!precision) precision = 2; 90 val = accounting.unformat(val); 91 return accounting.toFixed(val,precision); 92 } 93 return val; 94} 95function isValidAmount(val, allowCommas) { 96 if (typeof allowCommas === "undefined" || allowCommas === null) allowCommas = false; 97 var regEx = (!allowCommas) ? /^[0-9]+(?:\.[0-9]{1,2})?$/ : /^[+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]{1,2})?$/; 98 return regEx.test(val); 99} 100function isValidQuantity(val,allowUnlimited) { 101 if (isEmpty(val)) return false; 102 if (typeof allowUnlimited === "undefined" || allowUnlimited === null) allowUnlimited = true; 103 var regEx = (!allowUnlimited) ? /^[+-]?[0-9]*$/ : /^[+-]?[0-9]*$|^(UNL)$/; 104 return regEx.test(val.toUpperCase()); 105} 106 107function isValidDate(value) { 108 if (value == null) { 109 return false; 110 } else if (moment.isDate(value)) { 111 return !isNaN(value.getTime()); 112 } else if (moment.isMoment(value)) { 113 return !isNaN(value.toDate().getTime()); 114 } else if (typeof value === 'string' || typeof value === 'number') { 115 return !isNaN(moment(value).toDate().getTime()); 116 } else { 117 return false; 118 } 119} 120 121function isUrlValid(url) { 122 return /^(https?):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(url); 123} 124 125// Note: `enforceNumberField` and `enforceMoneyField` moved here from dynforms.js 126function enforceNumberField(field) { 127 field.on('keydown', function (e) { 128 // Allow: backspace, delete, tab, escape, enter and . 129 if ( 130 $.inArray(e.keyCode, [46, 8, 9, 27, 13]) !== -1 || 131 // Allow: Ctrl+A 132 (e.keyCode == 65 && e.ctrlKey === true) || 133 // Allow: home, end, left, right 134 (e.keyCode >= 35 && e.keyCode <= 39) 135 ) { 136 // let it happen, don't do anything 137 return; 138 } 139 // If ctrl or alt keys, just return so those keys are not blocked (ctrl+r or ctrl+F5 reloads); 140 if (e.altKey || e.ctrlKey) return true; 141 // Ensure that it is a number and stop the keypress 142 if ( 143 (e.shiftKey || e.keyCode < 48 || e.keyCode > 57) && 144 (e.keyCode < 96 || e.keyCode > 105) 145 ) { 146 e.preventDefault(); 147 } 148 }); 149} 150 151function enforceMoneyField(field) { 152 field.on('keydown', function (e) { 153 // Allow: backspace, delete, tab, escape, enter and . 154 if ( 155 $.inArray(e.keyCode, [46, 8, 9, 27, 13, 110, 190]) !== -1 || 156 // Allow: Ctrl+A 157 (e.keyCode == 65 && e.ctrlKey === true) || 158 // Allow: home, end, left, right 159 (e.keyCode >= 35 && e.keyCode <= 39) 160 ) { 161 // let it happen, don't do anything 162 return; 163 } 164 // If ctrl or alt keys, just return so those keys are not blocked (ctrl+r or ctrl+F5 reloads); 165 if (e.altKey || e.ctrlKey) return true; 166 // Ensure that it is a number and stop the keypress 167 if ( 168 (e.shiftKey || e.keyCode < 48 || e.keyCode > 57) && 169 (e.keyCode < 96 || e.keyCode > 105) 170 ) { 171 e.preventDefault(); 172 } 173 }); 174} 175 176function convertToDate(value, options) { 177 let format = (options && options.format) ? options.format : 'MM/DD/YYYY'; 178 if (value == null) { 179 return null; 180 } else if (moment.isDate(value)) { 181 return value; 182 } else if (moment.isMoment(value)) { 183 return value.toDate(); 184 } else if (typeof value === 'string' || typeof value === 'number') { 185 return moment(value, format).toDate(); 186 } 187 return null; 188} 189 190/* HTML5 Local Storage functions 191 * Local Storage are NOT cookies and never sent to the server.
192 * window.localStorage - stores data with no expiration date 193 */ 194function getValueFromLocalStorage(name) { 195 try { 196 if (typeof Storage !== 'undefined' && window.localStorage) { 197 return window.localStorage.getItem(name); 198 } 199 } catch (e) { 200 console.warn('Local storage is not available:', e); 201 } 202 return null; 203} 204function saveValueInLocalStorage(name,value) { 205 try { 206 if (typeof Storage !== 'undefined' && window.localStorage) { 207 window.localStorage.setItem(name, value); 208 } 209 } catch (e) { 210 console.warn('Local storage is not available:', e); 211 } 212} 213function getObjectFromLocalStorage(name) { 214 try { 215 if (typeof Storage !== 'undefined' && window.localStorage) { 216 return JSON.parse(window.localStorage.getItem(name)); 217 } 218 } catch (e) { 219 console.warn('Local storage is not available:', e); 220 } 221 return {}; 222} 223function saveObjectInLocalStorage(name,object) { 224 try { 225 if (typeof Storage !== 'undefined' && window.localStorage) { 226 window.localStorage.setItem(name, JSON.stringify(object)); 227 } 228 } catch (e) { 229 console.warn('Local storage is not available:', e); 230 } 231} 232function removeObjectFromLocalStorage(name) { 233 try { 234 if (typeof Storage !== 'undefined' && window.localStorage) { 235 window.localStorage.removeItem(name); 236 } 237 } catch (e) { 238 console.warn('Local storage is not available:', e); 239 } 240} 241 242/* HTML5 Local Storage functions 243 * Local Storage are NOT cookies and never sent to the server. 244 * window.sessionStorage - stores data for a SINGLE SESSION ONLY 245 */ 246function getValueFromSessionStorage(name) { 247 try { 248 if (typeof Storage !== 'undefined' && window.sessionStorage) { 249 return window.sessionStorage.getItem(name); 250 } 251 } catch (e) { 252 console.warn('Session storage is not available:', e); 253 } 254 return null; 255} 256function saveValueInSessionStorage(name,value) { 257 try { 258 if (typeof Storage !== 'undefined' && window.sessionStorage) { 259 window.sessionStorage.setItem(name, value); 260 } 261 } catch (e) { 262 console.warn('Session storage is not available:', e); 263 } 264} 265function getObjectFromSessionStorage(name) { 266 try { 267 if (typeof Storage !== 'undefined' && window.sessionStorage) { 268 return JSON.parse(window.sessionStorage.getItem(name)); 269 } 270 } catch (e) { 271 console.warn('Session storage is not available:', e); 272 } 273 return {}; 274} 275function saveObjectInSessionStorage(name,object) { 276 try { 277 if (typeof Storage !== 'undefined' && window.sessionStorage) { 278 window.sessionStorage.setItem(name, JSON.stringify(object)); 279 } 280 } catch (e) { 281 console.warn('Session storage is not available:', e); 282 } 283} 284function removeObjectFromSessionStorage(name) { 285 try { 286 if (typeof Storage !== 'undefined' && window.sessionStorage) { 287 window.sessionStorage.removeItem(name); 288 } 289 } catch (e) { 290 console.warn('Session storage is not available:', e); 291 } 292} 293 294function getUniqueId(seed,max) { 295 max = (isNumber(max))?parseInt(max):60; 296 return (((!seed?'':seed) + Math.ceil(new Date().getTime() + (Math.random() * 100))).substring(0,max)); 297} 298 299function updateSiteMgrSetting(key,value) { 300 $.ajax({ 301 traditional : true, 302 url : '/ver2/updateSiteMgrSetting', 303 data: { key: key, value: value }, 304 type : 'POST' 305 }).done(function(data) { 306 console.log('updateSiteMgrSetting: success!'); 307 }).fail(function(xhr, textStatus, errorThrown) { 308 console.log('updateSiteMgrSetting - error:', errorThrown); 309 }); 310} 311function updateUserOption(key,value) { 312 $.ajax({ 313 traditional : true, 314 url : '/ver2/updateUserOption', 315 data: { key: key, value: value }, 316 type : 'POST' 317 }).done(function(data) { 318 console.log('updateUserOption: success!'); 319 }).fail(function(xhr, textStatus, errorThrown) { 320 console.log('updateUserOption - error:', errorThrown); 321 }); 322} 323/* 324 * update multiple UserOptions. 325 * example call: updateUserOptions(['UserOptionName:UserOptionsValue',...]); 326 */ 327function updateUserOptions(kvList) { 328 console.log('updateUserOptions - kvList:',kvList); 329 $.ajax({ 330 traditional : true, 331 url : '/ver2/updateUserOptions', 332 data: { 'keyValueList': kvList}, 333 type : 'POST' 334 }).done(function(data) { 335 console.log('updateUserOptions: success!'); 336 }).fail(function(xhr, textStatus, errorThrown) { 337 console.log('updateUserOptions - error:', errorThrown); 338 }); 339} 340 341function enableSubmitOnEnterKey($form) { 342 if ($form == null) { 343 console.error('utils.js:enableSubmitOnEnterKey - form is null'); 344 return; 345 } 346 if (!($form instanceof jQuery)) 347 $form = $("#" + $form); 348 349 if ($form.length) { 350 $form.keydown(function(e) { 351 if (((e.keyCode ? e.keyCode : e.which) == 13) && (e.target.nodeName === "INPUT") && (e.target.type !== "textarea")) { 352 e.preventDefault(); 353 $form.submit(); 354 } 355 }); 356 } 357} 358 359function disableSubmitOnEnterKey() { 360 //Intercept enter key to prevent inadvertent form submission. 361 window.addEventListener("keydown", function(e) { 362 if ((e.keyCode ? e.keyCode : e.which) == 13) { 363 //target only input areas except for textarea and the search box 364 if(e.target.nodeName === "INPUT" && e.target.type !== "textarea" && e.target.id !== "header-search-text") { 365 e.preventDefault(); 366 return false; 367 } 368 } 369 }, true); 370} 371 372function initSelect2($sel, options) { 373 if ($sel == null) { 374 console.error('utils.js:initSelect2 - sel is null') 375 return; 376 } 377 if (!($sel instanceof jQuery)) 378 $sel = $("#" + $sel); 379 380 if ($sel.length > 0) { 381 382 if (!options.hasOwnProperty('language')) { 383 if ((typeof msbDefaultMessages !== 'undefined') && msbDefaultMessages.hasOwnProperty('select2')) { 384 if (msbDefaultMessages.select2.hasOwnProperty('inputTooShort')) { 385 var funcOverrideInputTooShort = function() { 386 if (options.hasOwnProperty('minimumInputLength')) { 387 var message = msbDefaultMessages.select2.inputTooShort; 388 return message.replace("{0}",options.minimumInputLength); 389 } else { 390 return msbDefaultMessages.select2.inputTooShort; 391 } 392 }; 393 } 394 if (msbDefaultMessages.select2.hasOwnProperty('noResults')) { 395 var funcOverrideNoResults = function() { 396 return msbDefaultMessages.select2.noResults; 397 }; 398 } 399 400 if ((typeof funcOverrideInputTooShort !== 'undefined') && (typeof funcOverrideNoResults !== 'undefined')) { 401 options.language = {inputTooShort: funcOverrideInputTooShort, noResults: funcOverrideNoResults}; 402 } else if ((typeof funcOverrideInputTooShort !== 'undefined')) { 403 options.language = {inputTooShort: funcOverrideInputTooShort}; 404 } else if ((typeof funcOverrideNoResults !== 'undefined')) { 405 options.language = {noResults: funcOverrideNoResults}; 406 } 407 } 408 } 409 410 options = $.extend({ 411 allowClear: true, 412 delay: 250, 413 minimumInputLength: 0, 414 minimumResultsForSearch: 10, 415 multiple: false, 416 placeholder: '-- Select --', 417 theme: null, 418 width: null, 419 widthFull: false, // Kaleida/Vega Setting 420 minWidth: '', 421 maxWidth: '', 422 language: {}, 423 dropdownParent: null 424 }, options); 425 426 if (isBootstrap5()) { 427 if (!options.theme) 428 options.theme = "bootstrap-5"; 429 } else { 430 if (!options.theme) 431 options.theme = "bootstrap"; 432 options.width = '100%';
432 // Do not set for bootstrap-5! 433 } 434 435 if (isTrue(options.widthFull)) { 436 if ($sel.parent().hasClass('input-group')) { 437 $sel.parent().css('width', '100%'); 438 } else { 439 $sel.css('width', '100%'); 440 } 441 } 442 443 if (!isEmpty(options.width)) 444 $sel.css('width', options.width); 445 if (!isEmpty(options.minWidth)) 446 $sel.css('min-width', options.minWidth); 447 if (!isEmpty(options.maxWidth)) 448 $sel.css('max-width', options.maxWidth); 449 450 var opts = { 451 theme: options.theme, 452 language: options.language, 453 minimumInputLength: options.minimumInputLength, 454 minimumResultsForSearch: options.minimumResultsForSearch, 455 multiple: options.multiple, 456 placeholder: options.placeholder, 457 allowClear: options.allowClear, 458 dropdownParent: options.dropdownParent, 459 escapeMarkup: function (markup) { return htmlEncode(markup); } // let our custom formatter work 460 }; 461 if (options.hasOwnProperty('templateSelection')) { 462 opts.templateSelection = options.templateSelection; 463 } 464 if (options.hasOwnProperty('templateResult')) { 465 opts.templateResult = options.templateResult; 466 } 467 468 $sel.select2(opts); 469 } 470} 471 472function initMultipleSelect2($sel, placeholder) { 473 if (!($sel instanceof jQuery)) 474 $sel = $("#" + $sel); 475 476 initSelect2($sel, {placeholder: placeholder, multiple: true, allowClear: false}); 477} 478 479function initAjaxSelect2($sel, options) { 480 if($sel == null) { 481 console.error('utils.js:initAjaxSelect2 - select is null'); 482 return; 483 } 484 if (!options.hasOwnProperty("ajaxUrl") || isEmpty(options.ajaxUrl)) { 485 console.error('utils.js:initAjaxSelect2 - ajaxUrl not provided!'); 486 return; 487 } 488 489 if (!($sel instanceof jQuery)) 490 $sel = $("#" + $sel); 491 492 /* setup select2 plugin for Product Group selection */ 493 if ($sel.length > 0) { 494 495 if ($sel.hasClass("select2-hidden-accessible")) { 496 $sel.select2('destroy'); 497 } 498 499 options = $.extend(true, { 500 allowClear: true, 501 delay:250, 502 loadingText: 'Loading', 503 minimumInputLength: 0, 504 placeholder: '', 505 theme: null, 506 multiple: false, 507 width: '100%', 508 minWidth: '', 509 maxWidth: '', 510 request: {} 511 }, options); 512 513 if (isBootstrap5()) { 514 if (!options.theme) 515 options.theme = "bootstrap-5"; 516 } else { 517 if (!options.theme) 518 options.theme = "bootstrap"; 519 } 520 521 if (!isEmpty(options.width)) 522 $sel.css('width', options.width); 523 if (!isEmpty(options.minWidth)) 524 $sel.css('min-width', options.minWidth); 525 if (!isEmpty(options.maxWidth)) 526 $sel.css('max-width', options.maxWidth); 527 528 $sel.select2({ 529 language: getSelect2Language(), 530 theme: options.theme, 531 minimumInputLength: options.minimumInputLength, 532 //quietMillis: 100, 533 multiple: options.multiple, 534 placeholder: options.placeholder, 535 allowClear: options.allowClear, 536 ajax: { 537 dataType: 'json', 538 type: 'POST', 539 url: options.ajaxUrl, 540 cache: false, 541 delay: options.delay, //milliseconds to wait for the user to stop typing before making ajax call 542 data : function(params) { 543 var reqData = { 544 'searchText': params.term || '', 545 'currentPage': params.page || 1 546 }; 547 if ($.isFunction(options.request)) { 548 reqData = $.extend(true, reqData, options.request(reqData)); 549 } else { 550 reqData = $.extend(true, reqData, options.request); 551 } 552 //console.log('initAjaxSelect2 ajaxUrl:',options.ajaxUrl,' reqData:',reqData); 553 return reqData; 554 }, 555 processResults: function (data, params) { 556 //console.log('initAjaxSelect2 - data:',data,' - params:',params); 557 if (data && isTrue(data.loginRequired)) { 558 window.location.reload(true); 559 } 560 var ajaxResults = { results:[], pagination: { more: false } } 561 if (data && data.listItems && data.listItems.length > 0) { 562 var arResults = []; 563 $.each(data.listItems, function(idx,item) { 564 arResults.push({'id':item.id || item.value, 'text':item.text}); 565 }); 566 ajaxResults.results = arResults; 567 568 if ('morePages' in data) 569 ajaxResults.pagination.more = isTrue(data.morePages) 570 else if ('pagesComplete' in data) 571 ajaxResults.pagination.more = !isTrue(data.pagesComplete) 572 else 573 ajaxResults.pagination.more = true; 574 } 575 return ajaxResults; 576 } 577 }, 578 escapeMarkup: function (markup) { 579 if (!isEmpty(markup)) { 580 markup = markup.replaceAll('&','&'); 581 markup = htmlEncode(markup); 582 } 583 return markup; 584 } 585 }); 586 } 587} 588 589 590function getSelect2Language() { 591 var select2Language = {}; 592 if ((typeof msbDefaultMessages !== 'undefined') && msbDefaultMessages.hasOwnProperty('select2')) { 593 if (msbDefaultMessages.select2.hasOwnProperty('inputTooShort')) { 594 var funcOverrideInputTooShort = function() { 595 var message = msbDefaultMessages.select2.inputTooShort; 596 return message.replace("{0}","2"); 597 }; 598 } 599 if (msbDefaultMessages.select2.hasOwnProperty('noResults')) { 600 var funcOverrideNoResults = function() { 601 return msbDefaultMessages.select2.noResults; 602 }; 603 } 604 605 if ((typeof funcOverrideInputTooShort !== 'undefined') && (typeof funcOverrideNoResults !== 'undefined')) { 606 select2Language = {inputTooShort: funcOverrideInputTooShort, noResults: funcOverrideNoResults}; 607 } else if ((typeof funcOverrideInputTooShort !== 'undefined')) { 608 select2Language = {inputTooShort: funcOverrideInputTooShort}; 609 } else if ((typeof funcOverrideNoResults !== 'undefined')) { 610 select2Language = {noResults: funcOverrideNoResults}; 611 } 612 } 613 614 select2Language = $.extend(true,select2Language,{loadingMore: function () { return null; }}); 615 return select2Language; 616} 617 618function clearSelectToggleProps($sel, options){ 619 if ($sel == null) { 620 console.error('utils.js:clearSelectToggleProps - sel is null') 621 return; 622 } 623 if (!($sel instanceof jQuery)) $sel = $("#" + $sel); 624 options = $.extend(true, {}, {text:'', value:'', enable:true, cursorClass:'cursor-not-allowed', select2:true, chosen:false}, options); 625 if (isTrue(options.chosen)) options.select2 = false; 626
627 $sel.empty().append($('<option></option>', {value:options.value, text:options.text})); 628 $sel.prop('disabled',(options.enable?false:true)); 629 630 if (options.enable) { 631 if (options.select2) $sel.parent().find('.select2-container').removeClass(options.cursorClass); 632 else if (options.chosen) $sel.parent().find('.chosen-single').removeClass(options.cursorClass); 633 else $sel.removeClass(options.cursorClass); 634 } else { 635 if (options.select2) $sel.parent().find('.select2-container').addClass(options.cursorClass); 636 else if (options.chosen) $sel.parent().find('.chosen-single').addClass(options.cursorClass); 637 else $sel.addClass(options.cursorClass); 638 } 639} 640 641 642function initDatatable($sel, settings) { 643 if ($sel == null) { 644 console.error('utils.js:datatable is null') 645 return; 646 } 647 if (!($sel instanceof jQuery)) 648 $sel = $("#" + $sel); 649 650 var defaultSettings = { 651 dom: 'Blftipr', 652 "buttons": [ 653 { 654 text: '<i class="fas fa-sync"></i>', 655 action: function ( e, dt, node, config ) { 656 dt.ajax.reload(); 657 } 658 }, 659 { 660 extend: 'colvis', 661 text: '<i class="fas fa-list fa-fw"></i>', 662 postfixButtons: ['colvisRestore'], 663 columns: ':not(.noVis)' 664 }, 665 ], 666 "language": { 667 "lengthMenu": "_MENU_", 668 "processing": '<i class="fa fa-spinner fa-spin fa-3x fa-fw"></i><span class="processing">Loading...</span> ', 669 "search": '<i class="fas fa-search"></i>', 670 "searchPlaceholder": "Search", 671 "info": "Showing _START_ to _END_ of _TOTAL_", 672 "infoEmpty": "" 673 }, 674 lengthMenu: [[50, 100, 500, -1], [50, 100, 500, "All"]], 675 order: [[ 1, "desc" ]], 676 stateSave: true, 677 serverSide: true, 678 processing: true, 679 ajax : { 680 url: 'placeholder', 681 contentType: 'application/json', 682 "type": "POST", 683 "data": function(data, settings) { 684 if (typeof getFormObjs !== 'undefined' && $.isFunction(getFormObjs)) { 685 data = $.extend( true, {}, data, getFormObjs() ); 686 if (typeof saveFilters !== 'undefined' && $.isFunction(saveFilters)) { 687 saveFilters(); 688 } 689 } 690 //console.log('setupChildTable.ajax request ',JSON.stringify(data)); 691 return JSON.stringify(data); 692 }, 693 cache: false, 694 "dataSrc": function ( json ) { 695 //console.log('setupChildTable.dataSrc json=',json); 696 if (json.hasOwnProperty('loginRequired') && isTrue(json.loginRequired)) { 697 window.location.reload(true); 698 } 699 if (json.hasOwnProperty('errors') && Array.isArray(json.errors) && json.errors.length) { 700 var out = (Array.isArray(json.errors)) ? json.errors.join('<br>') : json.errors; 701 $pageObjects.alerts.error.text.html(out); 702 $pageObjects.alerts.error.area.show(); 703 } 704 if (json.hasOwnProperty('messages') && Array.isArray(json.messages) && json.messages.length) { 705 var out = (Array.isArray(json.messages)) ? json.messages.join('<br>') : json.messages; 706 $pageObjects.alerts.info.text.html(out); 707 $pageObjects.alerts.info.area.show(); 708 } 709 /* Tell DataTables where the row data array is located. 710 * We use an object so we can return info other than row data (such as errors, etc...) 711 */ 712 console.log(json); 713 return json.data; 714 } 715 }, 716 fixedHeader: true, 717 columnDefs: [ 718 { 719 defaultContent: '', 720 targets: '_all' 721 } 722 ], 723 select: { 724 style: 'multi', 725 selector: '.select-checkbox', 726 items: 'row', 727 }, 728 responsive: { 729 details: { 730 type: 'column', 731 target: 0 732 } 733 }, 734 735 drawCallback: function(settings) { 736 var pagination = $(this).closest('.dataTables_wrapper').find('.dataTables_paginate'); 737 pagination.toggle(this.api().page.info().pages > 1); 738 } 739 }; 740 741 settings = $.extend(true, defaultSettings, settings); 742 $selDT = $sel.DataTable(settings); 743 744 $selDT.on('draw',function(){ 745 $selDT.columns.adjust(); 746 }); 747 748 return $selDT; 749} 750 751function initDateTimePicker($dateInput, options) { 752 if ($dateInput == null) { 753 console.error('utils.js:initDateTimePicker - $dateInput is null'); 754 return; 755 } 756 if (!($dateInput instanceof jQuery)) 757 $dateInput = $("#" + $dateInput); 758 759 if ($dateInput.length) { 760 $dateInput.addClass("cursor-pointer"); 761 762 /* onChangeCallback - can be a function to call or a callback object as follows: 763 * { 764 * 'callback': 'a function to call', 765 * 'argument': 'argument for callback function' 766 * } 767 */ 768 options = $.extend(true, { 769 format: 'MM/DD/YYYY', 770 viewMode: 'days', // 'days', 'months', 'years', 'decades' 771 showTodayButton: true, 772 showClear: true, 773 showClose: true, 774 useCurrent: false, 775 startDate: null, 776 defaultStartDate: null, 777 minDate: moment().subtract(2, 'years').startOf('year').toDate(), 778 maxDate: moment().endOf('day').toDate(), 779 widgetPositioning: { horizontal: 'auto', vertical: 'auto' }, 780 onChangeCallback: { 781 'callback': null, 782 'argument': null 783 }, 784 reset: { 785 element: '', 786 action: '' 787 } 788 }, options); 789 790 // Ensure that viewMode is valid
791 if (isEmpty(options.viewMode) || (options.viewMode != 'days' && options.viewMode != 'months' && options.viewMode != 'years' && options.viewMode != 'decades')) 792 options.viewMode = 'days'; 793 794 // Convert all of the provided date values to instances of Date 795 options.startDate = convertToDate(options.startDate, options); 796 options.defaultStartDate = convertToDate(options.defaultStartDate, options); 797 options.minDate = convertToDate(options.minDate, options); 798 options.maxDate = convertToDate(options.maxDate, options); 799 800 // Replace any invalid dates to prevent errors when initializing datetimepicker 801 if (!isValidDate(options.startDate)) { 802 $dateInput.val(''); 803 if (isValidDate(options.defaultStartDate)) { 804 options.startDate = options.defaultStartDate; 805 } else { 806 options.startDate = null; 807 } 808 } 809 if (!isValidDate(options.minDate)) 810 options.minDate = moment().subtract(99, 'years').startOf('year').toDate(); 811 if (!isValidDate(options.maxDate)) 812 options.maxDate = moment().add(99, 'years').endOf('year').toDate(); 813 814 $dateInput.datetimepicker({ 815 format: options.format, 816 viewMode: options.viewMode, 817 showTodayButton: options.showTodayButton, 818 showClear: options.showClear, 819 showClose: options.showClose, 820 useCurrent: options.useCurrent, 821 defaultDate: options.startDate, 822 minDate: options.minDate, 823 maxDate: options.maxDate, 824 widgetPositioning: options.widgetPositioning, 825 icons: { 826 time: "far fa-clock", 827 date: "fas fa-calendar-alt" 828 }, 829 tooltips: (typeof msbApp !== 'undefined') ? msbApp.getDateTimePickerTooltips() : {}, 830 locale: (typeof msbApp !== 'undefined') ? msbApp.getMomentSupportedLocale() : 'en' 831 }); 832 $dateInput.on("dp.change", function(e) { 833 if (options.onChangeCallback) { 834 if (typeof options.onChangeCallback == "function") { 835 return options.onChangeCallback(e); 836 } else if (options.onChangeCallback.hasOwnProperty("callback") && (typeof options.onChangeCallback.callback == "function")) { 837 return options.onChangeCallback.callback(options.onChangeCallback.argument || e); 838 } 839 } 840 }); 841 } 842} 843 844function initStartEndDateTimePicker($startDate, $endDate, options) { 845 if ($startDate == null) { 846 console.error('admin/common.js:initStartEndDateTimePicker - $startDate is null'); 847 return; 848 } 849 if ($endDate == null) { 850 console.error('admin/common.js:initStartEndDateTimePicker - $endDate is null'); 851 return; 852 } 853 854 if (!($startDate instanceof jQuery)) 855 $startDate = $("#" + $startDate); 856 if (!($endDate instanceof jQuery)) 857 $endDate = $("#" + $endDate); 858 859 if ($startDate.length && $endDate.length) { 860 $startDate.addClass("cursor-pointer"); 861 $endDate.addClass("cursor-pointer"); 862 863 /* onChangeCallback - can be a function to call or a callback object as follows: 864 * { 865 * 'callback': 'a function to call', 866 * 'argument': 'argument for callback function' 867 * } 868 */ 869 options = $.extend(true, { 870 format:$("#pageValuesDatePickerFormat").val() , 871 viewMode: 'days', // 'days', 'months', 'years', 'decades' 872 showTodayButton: true, 873 showClear: true, 874 showClose: true, 875 useCurrent: false, 876 startDate: null, 877 endDate: null, 878 defaultStartDate: null, 879 defaultEndDate: null, 880 minDate: moment().subtract(2, 'years').startOf('year').toDate(), 881 maxDate: moment().endOf('day').toDate(), 882 widgetPositioning: { horizontal: 'auto', vertical: 'auto' }, 883 onChangeCallback: { 884 'callback': null, 885 'argument': null 886 }, 887 reset: { 888 element: '', 889 action: '' 890 } 891 }, options); 892 893 //console.log('initStartEndDateTimePicker - options:',options) 894 895 896 // Ensure that viewMode is valid 897 if (isEmpty(options.viewMode) || (options.viewMode != 'days' && options.viewMode != 'months' && options.viewMode != 'years' && options.viewMode != 'decades')) 898 options.viewMode = 'days'; 899 900 // Convert all of the provided date values to instances of Date 901 options.startDate = convertToDate(options.startDate, options); 902 options.endDate = convertToDate(options.endDate, options); 903 options.defaultStartDate = convertToDate(options.defaultStartDate, options); 904 options.defaultEndDate = convertToDate(options.defaultEndDate, options); 905 options.minDate = convertToDate(options.minDate, options); 906 options.maxDate = convertToDate(options.maxDate, options); 907
908 // Replace any invalid dates to prevent errors when initializing datetimepicker 909 if (!isValidDate(options.startDate)) { 910 $startDate.val(''); 911 if (isValidDate(options.defaultStartDate)) { 912 options.startDate = options.defaultStartDate; 913 } else { 914 options.startDate = null; 915 } 916 } 917 if (!isValidDate(options.endDate)) { 918 $endDate.val(''); 919 if (isValidDate(options.defaultEndDate)) { 920 options.endDate = options.defaultEndDate; 921 } else { 922 options.endDate = null; 923 } 924 } 925 if (!isValidDate(options.minDate)) 926 options.minDate = moment().subtract(99, 'years').startOf('year').toDate(); 927 if (!isValidDate(options.maxDate)) 928 options.maxDate = moment().add(99, 'years').endOf('year').toDate(); 929 930 $startDate.datetimepicker({ 931 format: options.format, 932 viewMode: options.viewMode, 933 showTodayButton: options.showTodayButton, 934 showClear: options.showClear, 935 showClose: options.showClose, 936 useCurrent: options.useCurrent, 937 defaultDate: options.startDate, 938 minDate: options.minDate, 939 maxDate: (options.endDate) ? options.endDate : options.maxDate, 940 widgetPositioning: options.widgetPositioning, 941 icons: { 942 time: "far fa-clock", 943 date: "fas fa-calendar-alt" 944 }, 945 tooltips: (typeof msbApp !== 'undefined') ? msbApp.getDateTimePickerTooltips() : {}, 946 locale: (typeof msbApp !== 'undefined') ? msbApp.getMomentSupportedLocale() : 'en' 947 }); 948 $endDate.datetimepicker({ 949 format: options.format, 950 viewMode: options.viewMode, 951 showTodayButton: options.showTodayButton, 952 showClear: options.showClear, 953 showClose: options.showClose, 954 useCurrent: options.useCurrent, 955 defaultDate: options.endDate, 956 minDate: (options.startDate) ? options.startDate : options.minDate, 957 maxDate: options.maxDate, 958 widgetPositioning: options.widgetPositioning, 959 icons: { 960 time: "far fa-clock", 961 date: "fas fa-calendar-alt" 962 }, 963 tooltips: (typeof msbApp !== 'undefined') ? msbApp.getDateTimePickerTooltips() : {}, 964 locale: (typeof msbApp !== 'undefined') ? msbApp.getMomentSupportedLocale() : 'en' 965 }); 966 $startDate.on("dp.change", function(e) { 967 if (e.date) 968 $endDate.data("DateTimePicker").minDate(e.date); 969 else if (options.minDate) 970 $endDate.data("DateTimePicker").minDate(options.minDate); 971 972 if (options.onChangeCallback) { 973 if (typeof options.onChangeCallback == "function") { 974 return options.onChangeCallback(e); 975 } else if (options.onChangeCallback.hasOwnProperty("callback") && (typeof options.onChangeCallback.callback == "function")) { 976 return options.onChangeCallback.callback(options.onChangeCallback.argument || e); 977 } 978 } 979 }); 980 $endDate.on("dp.change", function(e) { 981 if (e.date) 982 $startDate.data("DateTimePicker").maxDate(e.date); 983 else if (options.maxDate) 984 $startDate.data("DateTimePicker").maxDate(options.maxDate); 985 986 if (options.onChangeCallback) { 987 if (typeof options.onChangeCallback == "function") { 988 return options.onChangeCallback(e); 989 } else if (options.onChangeCallback.hasOwnProperty("callback") && (typeof options.onChangeCallback.callback == "function")) { 990 return options.onChangeCallback.callback(options.onChangeCallback.argument || e); 991 } 992 } 993 }); 994 995 if (options.reset.element != '' && options.reset.action != '') { 996 $reset = $("#" + options.reset.element); 997 if ($reset.length) { 998 $reset.on(options.reset.action, function() { 999 resetStartEndDateTimePicker($startDate, $endDate, options); 1000 }); 1001 } 1002 } 1003 } 1004} 1005 1006function resetStartEndDateTimePicker($startDate, $endDate, options) { 1007 if ($startDate == null) { 1008 console.error('admin/common.js:resetStartEndDateTimePicker - $startDate is null'); 1009 return; 1010 } 1011 if ($endDate == null) { 1012 console.error('admin/common.js:resetStartEndDateTimePicker - $endDate is null'); 1013 return; 1014 } 1015 1016 if (!($startDate instanceof jQuery)) 1017 $startDate = $("#" + $startDate); 1018 if (!($endDate instanceof jQuery)) 1019 $endDate = $("#" + $endDate); 1020 1021 if ($startDate.length && $endDate.length) { 1022 options = $.extend(true, { 1023 defaultStartDate: null, 1024 defaultEndDate: null, 1025 minDate: moment().subtract(2, 'years').startOf('year').toDate(), 1026 maxDate: moment().endOf('day').toDate() 1027 }, options); 1028 1029 // Convert all of the provided date values to instances of Date 1030 options.defaultStartDate = convertToDate(options.defaultStartDate, options); 1031 options.defaultEndDate = convertToDate(options.defaultEndDate, options); 1032 options.minDate = convertToDate(options.minDate, options); 1033 options.maxDate = convertToDate(options.maxDate, options); 1034
1035 // Replace any invalid dates to prevent errors when initializing datetimepicker 1036 if (!isValidDate(options.defaultStartDate)) 1037 options.defaultStartDate = null; 1038 if (!isValidDate(options.defaultEndDate)) 1039 options.defaultEndDate = null; 1040 if (!isValidDate(options.minDate)) 1041 options.minDate = moment().subtract(99, 'years').startOf('year').toDate(); 1042 if (!isValidDate(options.maxDate)) 1043 options.maxDate = moment().add(99, 'years').endOf('year').toDate(); 1044 1045 // Reset the min and max dates before attempting to reset the dates 1046 $startDate.data("DateTimePicker").minDate(options.minDate); 1047 $startDate.data("DateTimePicker").maxDate((options.defaultEndDate) ? options.defaultEndDate : options.maxDate); 1048 $endDate.data("DateTimePicker").minDate((options.defaultStartDate) ? options.defaultStartDate : options.minDate); 1049 $endDate.data("DateTimePicker").maxDate(options.maxDate); 1050 1051 if (options.defaultStartDate) { 1052 $startDate.data("DateTimePicker").date(options.defaultStartDate); 1053 } else { 1054 $startDate.data("DateTimePicker").clear(); 1055 } 1056 1057 if (options.defaultEndDate) { 1058 $endDate.data("DateTimePicker").date(options.defaultEndDate); 1059 } else { 1060 $endDate.data("DateTimePicker").clear(); 1061 } 1062 } 1063} 1064 1065function showDateTimePicker(id) { 1066 $("#"+id).data("DateTimePicker").show(); 1067} 1068 1069/* Update a URL string to Append or replace a parameter */ 1070function appendUrlParam(url, param, value) { 1071 if (!url) throw new Error('Missing required parameter: url'); 1072 var hash = {}, 1073 parser = document.createElement('a'); 1074 parser.href = url; 1075 var parameters = parser.search.split(/\?|&/); 1076 1077 for (var i=0; i < parameters.length; i++) { 1078 if (!parameters[i]) continue; 1079 var ary = parameters[i].split('='); 1080 hash[ary[0]] = ary[1]; 1081 } 1082 hash[param] = value; 1083 1084 var list = []; 1085 Object.keys(hash).forEach(function (key) { 1086 list.push(key + '=' + hash[key]); 1087 }); 1088 1089 parser.search = '?' + list.join('&'); 1090 //console.log('appendUrlParam: url:'+parser.href); 1091 return msbApp.sanitizeHtml(parser.href); 1092} 1093/* Retrieve a URL parameter */ 1094function getUrlParam(k){ 1095 var p={}; 1096 location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){p[k]=v}) 1097 return k?p[k]:null; 1098} 1099 1100/* Bootstrap */ 1101function getBootstrapVersion() { 1102 let result = 3; 1103 try { 1104 result = (window.bootstrap ? bootstrap.Tooltip.VERSION.split('.')[0] : result); 1105 } catch (exception) { 1106 result = 3; 1107 } 1108 return result; 1109} 1110function isBootstrap5() { 1111 return (getBootstrapVersion() == 5); 1112} 1113 1114function handleBoostrap5Validation(event) { 1115 if (this.checkValidity() === false) { 1116 event.preventDefault(); 1117 event.stopPropagation(); 1118 } 1119 this.classList.add('was-validated'); 1120} 1121function initBootstrap5Validation() { 1122 if (!shouldDisableBootstrapValidator()) { 1123 // Fetch all the forms we want to apply custom Bootstrap validation styles to 1124 let forms = document.getElementsByClassName('needs-validation'); 1125 // Loop over them and prevent submission 1126 Array.prototype.filter.call(forms, function(form) { 1127 form.addEventListener('submit', handleBoostrap5Validation, false); 1128 }); 1129 } 1130} 1131function disableBootstrap5Validation() { 1132 let forms = document.getElementsByClassName('needs-validation'); 1133 Array.prototype.filter.call(forms, function(form) { 1134 form.removeEventListener('submit', handleBoostrap5Validation, false); 1135 }); 1136} 1137 1138function initBootstrapValidator($form, options) { 1139 1140 var datePickerFormat = "MM/DD/YYYY"; 1141 if($("#pageValuesDatePickerFormat") && $("#pageValuesDatePickerFormat").val()) { 1142 datePickerFormat = $("#pageValuesDatePickerFormat").val(); 1143 } 1144 if ($form == null) { 1145 console.error('utils.js:initBootstrapValidator - form is null') 1146 return; 1147 } 1148 if (!($form instanceof jQuery)) 1149 $form = $("#" + $form); 1150 1151 if ($form.length) { 1152 if (!shouldDisableBootstrapValidator()) { 1153 $form.validator('destroy'); 1154 1155 const expDateEmptyOptionIndex = "-1"; 1156 1157 var newOptions = $.extend(true, { 1158 feedback: { 1159 success: 'fa-check-circle', 1160 error: '' 1161 }, 1162 custom: { 1163 match: function($el) { 1164 if ($el.is(":hidden")) 1165 return; 1166 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1167 return; 1168 1169 var matchFieldID = $el.attr('data-match'); 1170 var $matchField = $(matchFieldID); 1171 if (!($matchField instanceof jQuery)) 1172 $matchField = $("#"+matchFieldID); 1173 if (!$matchField.length) 1174 return; 1175 1176 return $el.val() !== $matchField.val(); 1177 }, 1178 nomatch: function($el) {
1179 if ($el.is(":hidden")) 1180 return; 1181 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1182 return; 1183 1184 var noMatchFieldID = $el.attr('data-nomatch'); 1185 var $noMatchField = $(noMatchFieldID); 1186 if (!($noMatchField instanceof jQuery)) 1187 $noMatchField = $("#"+noMatchFieldID); 1188 if (!$noMatchField.length) 1189 return; 1190 1191 return $el.val() === $noMatchField.val(); 1192 }, 1193 minlength: function($el) { 1194 if ($el.is(":hidden")) 1195 return; 1196 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1197 return; 1198 1199 var val = $el.val(); 1200 if (isTrue($el.attr('data-minlength-squish'))) 1201 val = squishString(val); 1202 var minlength = $el.attr('data-minlength'); 1203 return val.length < minlength; 1204 }, 1205 passwordpattern: function($el) { 1206 if ($el.is(":hidden")) 1207 return; 1208 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1209 return; 1210 1211 var val = $el.val(); 1212 const re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()+=-\?;,./{}|\":<>\[\]\\\' ~_])/; 1213 return !re.test(val) 1214 }, 1215 passwordwhitespace: function($el) { 1216 if ($el.is(":hidden")) 1217 return; 1218 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1219 return; 1220 1221 var val = $el.val(); 1222 if (val == null) { 1223 return; 1224 } 1225 return val.trim().length !== val.length; 1226 }, 1227 maxlength: function($el) { 1228 if ($el.is(":hidden")) 1229 return; 1230 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1231 return; 1232 1233 var val = $el.val(); 1234 if (isTrue($el.attr('data-maxlength-squish'))) 1235 val = squishString(val); 1236 var maxlength = $el.attr('data-maxlength'); 1237 return val.length > maxlength; 1238 }, 1239 greaterthan: function($el) { 1240 if ($el.is(":hidden")) 1241 return; 1242 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1243 return; 1244 1245 var valueToBeGreaterThan = $el.attr('data-greaterthan'); 1246 if (!isNumber(valueToBeGreaterThan) || !isNumber($el.val())) 1247 return; 1248 1249 return parseFloat($el.val()) <= parseFloat(valueToBeGreaterThan); 1250 }, 1251 greaterthanorequalto: function($el) { 1252 if ($el.is(":hidden")) 1253 return; 1254 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1255 return; 1256 1257 var valueToBeGreaterThanOrEqualTo = $el.attr('data-greaterthanorequalto'); 1258 if (!isNumber(valueToBeGreaterThanOrEqualTo) || !isNumber($el.val())) 1259 return; 1260 1261 return parseFloat($el.val()) < parseFloat(valueToBeGreaterThanOrEqualTo); 1262 }, 1263 lessthan: function($el) { 1264 if ($el.is(":hidden")) 1265 return; 1266 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1267 return; 1268 1269 var valueToBeLessThan = $el.attr('data-lessthan'); 1270 if (!isNumber(valueToBeLessThan) || !isNumber($el.val())) 1271 return; 1272 1273 return parseFloat($el.val()) >= parseFloat(valueToBeLessThan); 1274 }, 1275 lessthanorequalto: function($el) { 1276 if ($el.is(":hidden")) 1277 return; 1278 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1279 return; 1280 1281 var valueToBeLessThanOrEqualTo = $el.attr('data-lessthanorequalto'); 1282 if (!isNumber(valueToBeLessThanOrEqualTo) || !isNumber($el.val())) 1283 return; 1284 1285 return parseFloat($el.val()) > parseFloat(valueToBeLessThanOrEqualTo); 1286 }, 1287 between: function($el) { 1288 if ($el.is(":hidden")) 1289 return; 1290 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1291 return; 1292 1293 var value = parseFloat($el.val()); 1294 var range = $el.attr('data-between'); 1295 var min = parseFloat(range.split('-')[0]); 1296 var max = parseFloat(range.split('-')[1]); 1297 1298 if (!isNumber(value) || !isNumber(min) || !isNumber(max)) 1299 return; 1300 1301 //May have a minimum, but no maximum 1302 if (max > min && (value < min || value > max)) 1303 { 1304 $el.attr('data-between-error', $el.attr('data-range-error')); 1305 return true; 1306 } 1307 else if (value < min) 1308 { 1309 $el.attr('data-between-error', $el.attr('data-min-error')); 1310 return true; 1311 } 1312 1313 return false; 1314 }, 1315 numeric: function($el) {
1316 if ($el.is(":hidden")) 1317 return; 1318 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1319 return; 1320 1321 return !isNumber($el.val()); 1322 }, 1323 containsnumber: function($el) { 1324 if ($el.is(":hidden")) 1325 return; 1326 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1327 return; 1328 1329 return !containsNumber($el.val()); 1330 }, 1331 date: function($el) { 1332 if ($el.is(":hidden")) 1333 return; 1334 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1335 return; 1336 1337 return !moment($el.val(), datePickerFormat, true).isValid(); 1338 }, 1339 dateafter: function($el) { 1340 if ($el.is(":hidden")) 1341 return; 1342 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1343 return; 1344 if (($el.attr('data-date') || $el.attr('data-date') !== undefined) && !moment($el.val(), datePickerFormat, true).isValid()) 1345 return; 1346 1347 var startDateFieldID = $el.attr('data-dateafter'); 1348 var $startDateField = $(startDateFieldID); 1349 if (!($startDateField instanceof jQuery)) 1350 $startDateField = $("#"+startDateFieldID); 1351 if (!$startDateField.length || $startDateField.val().length == 0 || !moment($startDateField.val(), datePickerFormat, true).isValid()) 1352 return; 1353 1354 return !isValidDateRange($startDateField.val(), $el.val()); 1355 }, 1356 datebefore: function($el) { 1357 if ($el.is(":hidden")) 1358 return; 1359 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1360 return; 1361 if (($el.attr('data-date') || $el.attr('data-date') !== undefined) && !moment($el.val(), datePickerFormat, true).isValid()) 1362 return; 1363 1364 var endDateFieldID = $el.attr('data-datebefore'); 1365 var $endDateField = $(endDateFieldID); 1366 if (!($endDateField instanceof jQuery)) 1367 $endDateField = $("#"+endDateFieldID); 1368 if (!$endDateField.length || $endDateField.val().length == 0 || !moment($endDateField.val(), datePickerFormat, true).isValid()) 1369 return; 1370 1371 return !isValidDateRange($el.val(), $endDateField.val()); 1372 }, 1373 dualrequired: function($el) { 1374 if ($el.is(":hidden")) 1375 return; 1376 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1377 return; 1378 1379 var dualRequiredFieldID = $el.attr('data-dualrequired'); 1380 var $dualRequiredField = $(dualRequiredFieldID); 1381 if (!($dualRequiredField instanceof jQuery)) 1382 $dualRequiredField = $("#" + dualRequiredFieldID); 1383 if (!$dualRequiredField.length) 1384 return; 1385 1386 var notEmptyOptionRequired = !!$el.attr('notEmptyOptionRequired'); 1387 var dualFieldNotEmptyOptionRequired = !!$dualRequiredField.attr('notEmptyOptionRequired'); 1388 1389 return $el.val().length == 0 || $dualRequiredField.val().length == 0 1390 || (notEmptyOptionRequired && $el.val() == expDateEmptyOptionIndex) 1391 || (dualFieldNotEmptyOptionRequired && $dualRequiredField.val() == expDateEmptyOptionIndex); 1392 }, 1393 routingnumber: function($el) { 1394 if ($el.is(":hidden")) 1395 return; 1396 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1397 return; 1398 1399 var value = $el.val(); 1400 var previousValue = $el.attr('data-routingnumber'); 1401 1402 if (value != previousValue) { 1403 if (!isValidRoutingNumber(value)) 1404 return true; 1405 } 1406 1407 return; 1408 }, 1409 checkingacctnumber: function($el) { 1410 if ($el.is(":hidden")) 1411 return; 1412 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1413 return; 1414 1415 var value = $el.val(); 1416 var previousValue = $el.attr('data-checkingacctnumber'); 1417 1418 if (value != previousValue) { 1419 var formattedValue = ""; 1420 for (var i = 0; i < value.length; i++) { 1421 if (isNumber(value.charAt(i))) 1422 formattedValue = formattedValue + value.charAt(i); 1423 } 1424 return (formattedValue.length < 1 || formattedValue.length > 17); 1425 } 1426 1427 return; 1428 }, 1429 creditcardlength: function($el) {
1430 if ($el.is(":hidden")) 1431 return; 1432 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1433 return; 1434 1435 var data = $el.attr('data-creditcardnumber').split(';'); 1436 if (data.length < 2) 1437 return; 1438 1439 var cardTypeFieldID = data[0]; 1440 var $cardTypeField = $(cardTypeFieldID); 1441 if (!($cardTypeField instanceof jQuery)) 1442 $cardTypeField = $("#"+cardTypeFieldID); 1443 if (!$cardTypeField.length) 1444 return; 1445 var previousValue = data[1]; 1446 1447 if ($el.val() != previousValue) { 1448 var cardType = getCardType($el, $cardTypeField); 1449 if (!isValidCreditCardLength($el, cardType)) 1450 return true; 1451 } 1452 return; 1453 }, 1454 creditcardcvvlength: function($el) { 1455 if ($el.is(":hidden")) 1456 return; 1457 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1458 return; 1459 1460 var cardType = 'invalid'; 1461 1462 if (!isEmpty($el.attr('data-cardtypefield-id'))) { 1463 var cardTypeFieldID = $el.attr('data-cardtypefield-id'); 1464 var $cardTypeField = $(cardTypeFieldID); 1465 if (!($cardTypeField instanceof jQuery)) 1466 $cardTypeField = $("#"+cardTypeFieldID); 1467 if (!$cardTypeField.length) 1468 return; 1469 1470 cardType = $cardTypeField.val(); 1471 } else { 1472 var cardSelectID = $el.attr('data-cardselect-id'); 1473 var selectedOption = $(cardSelectID + ' option:selected').text().toLowerCase(); 1474 if (selectedOption.indexOf('visa') >= 0) 1475 cardType = 'visa'; 1476 else if (selectedOption.indexOf('mastercard') >= 0) 1477 cardType = 'mastercard'; 1478 else if (selectedOption.indexOf('discover') >= 0) 1479 cardType = 'discover'; 1480 else if (selectedOption.indexOf('american express') >= 0) 1481 cardType = 'amex'; 1482 } 1483 1484 1485 if (!isValidCvvLength($el, cardType)) 1486 return true; 1487 1488 return; 1489 }, 1490 creditcardnumber: function($el) { 1491 if ($el.is(":hidden")) 1492 return; 1493 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1494 return; 1495 1496 var data = $el.attr('data-creditcardnumber').split(';'); 1497 if (data.length < 2) 1498 return; 1499 1500 var cardTypeFieldID = data[0]; 1501 var $cardTypeField = $(cardTypeFieldID); 1502 if (!($cardTypeField instanceof jQuery)) 1503 $cardTypeField = $("#"+cardTypeFieldID); 1504 if (!$cardTypeField.length) 1505 return; 1506 1507 var cardType = getCardType($el, $cardTypeField); 1508 var cardTypeMap = JSON.parse($el.attr('data-cardtypemap')); 1509 var acceptedCardTypes = $el.attr('data-acceptedcardtypes'); 1510 1511 // Don't throw an error here, because it will be a different error message - will handle in another validation 1512 if (isEmpty(cardType) || (!isEmpty(acceptedCardTypes) && JSON.stringify(acceptedCardTypes).indexOf(cardType) < 0) || !isValidCreditCardLength($el, cardType)) 1513 return; 1514 1515 var value = $el.val(); 1516 var previousValue = data[1]; 1517 1518 if (value != previousValue) { 1519 // Code translated from BaseActionSupport.isValidCreditCardNumber() 1520 if (!isValidCreditCardNumber(value)) 1521 return true; 1522 1523 if (cardType == "visa" && value.indexOf("4") != 0) 1524 return true; 1525 else if (cardType == "mastercard" && (value.indexOf("5") != 0 && value.indexOf("2") != 0)) 1526 return true; 1527 else if (cardType == "amex" && value.indexOf("3") != 0) 1528 return true; 1529 else if (cardType == "discover" && value.indexOf("6") != 0) 1530 return true; 1531 } 1532 1533 return; 1534 }, 1535 acceptedcardtypes: function($el) { 1536 if ($el.is(":hidden")) 1537 return; 1538 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1539 return; 1540 1541 var data = $el.attr('data-creditcardnumber').split(';'); 1542 if (data.length < 2) 1543 return; 1544 1545 var cardTypeFieldID = data[0]; 1546 var $cardTypeField = $(cardTypeFieldID); 1547 if (!($cardTypeField instanceof jQuery)) 1548 $cardTypeField = $("#"+cardTypeFieldID); 1549 if (!$cardTypeField.length) 1550 return; 1551 1552 var acceptedCardTypes = $el.attr('data-acceptedcardtypes'); 1553 if (!acceptedCardTypes.length) 1554 return; 1555 1556 var cardType = getCardType($el, $cardTypeField); 1557 if (isEmpty(cardType)) 1558 return; 1559 1560 if (acceptedCardTypes.indexOf(cardType) < 0) 1561 { 1562 var cardTypeMap = JSON.parse($el.attr('data-cardtypemap')); 1563 var cardTypeMapError = $el.attr('data-acceptedcardtypeserror-text'); 1564 1565 if (!isEmpty(cardTypeMap) && !isEmpty(cardTypeMapError) && !isEmpty(cardTypeMap[cardType])) 1566 $el.attr('data-acceptedcardtypes-error', cardTypeMapError.replace('{0}', cardTypeMap[cardType])); 1567 else 1568 $el.attr('data-acceptedcardtypes-error', $el.attr('data-creditcardnumber-error')); 1569 1570 return true; 1571 } 1572 1573 return; 1574 }, 1575 paymentmethods_expirationdatemonth: function($el) {
1576 if ($el.is(":hidden")) 1577 return; 1578 1579 var expYearFieldID = $el.attr('data-paymentmethods_expirationdatemonth'); 1580 var $expYearField = $(expYearFieldID); 1581 if (!($expYearField instanceof jQuery)) 1582 $expYearField = $("#" + expYearFieldID); 1583 if (!$expYearField.length) 1584 return; 1585 1586 var notEmptyOptionRequired = $el.attr('notEmptyOptionRequired'); 1587 var yearFieldNotEmptyOptionRequired = $expYearField.attr('notEmptyOptionRequired'); 1588 var selectedMonth = $el.val(); 1589 var selectedYear = $expYearField.val(); 1590 if (selectedMonth.length == 0 || selectedYear.length == 0 1591 || (yearFieldNotEmptyOptionRequired && $expYearField.val() == expDateEmptyOptionIndex) 1592 || (notEmptyOptionRequired && $el.val() == expDateEmptyOptionIndex)) { 1593 return; 1594 } 1595 1596 if (isNumber(selectedMonth) && isNumber(selectedYear)) { 1597 var date = new Date(); 1598 return selectedYear < date.getFullYear() || (selectedYear == date.getFullYear() && selectedMonth < date.getMonth()); 1599 } 1600 1601 return; 1602 }, 1603 paymentmethods_expirationdateyear: function($el) { 1604 if ($el.is(":hidden")) 1605 return; 1606 1607 var expMonthFieldID = $el.attr('data-paymentmethods_expirationdateyear'); 1608 var $expMonthField = $(expMonthFieldID); 1609 if (!($expMonthField instanceof jQuery)) 1610 $expMonthfield = $("#" + expMonthFieldID); 1611 1612 if (!$expMonthField.length) 1613 return; 1614 1615 var notEmptyOptionRequired = $el.attr('notEmptyOptionRequired'); 1616 var monthFieldNotEmptyOptionRequired = $expMonthField.attr('notEmptyOptionRequired'); 1617 var selectedYear = $el.val(); 1618 var selectedMonth = $expMonthField.val(); 1619 if (selectedYear.length == 0 || selectedMonth.length == 0 1620 || (monthFieldNotEmptyOptionRequired && $expMonthField.val() == expDateEmptyOptionIndex) 1621 || (notEmptyOptionRequired && selectedYear == expDateEmptyOptionIndex)) { 1622 return; 1623 } 1624 1625 if (isNumber(selectedYear) && isNumber(selectedMonth)) { 1626 var date = new Date(); 1627 return selectedYear < date.getFullYear() || (selectedYear == date.getFullYear() && selectedMonth < date.getMonth()); 1628 } 1629 1630 return; 1631 }, 1632 editautopaysettings_lowbalance: function($el) { 1633 if ($el.is(":hidden")) 1634 return; 1635 1636 var name = $el.attr('data-editautopaysettings_lowbalance'); 1637 var $fieldToCheck = $("input[name='"+name+"']:checked"); 1638 if (!$fieldToCheck.length || $fieldToCheck.is(":hidden")) 1639 return; 1640 1641 var lowBalanceThreshold = $fieldToCheck.val(); 1642 if (lowBalanceThreshold == "other") { 1643 $fieldToCheck = $("input[name='OTHER_"+name+"']"); 1644 if ($fieldToCheck.val().length == 0 || !isNumber($fieldToCheck.val())) 1645 return; 1646 lowBalanceThreshold = $fieldToCheck.val(); 1647 1648 var maxLowBalanceThreshold = $fieldToCheck.attr("data-lessthanorequalto"); 1649 if (maxLowBalanceThreshold !== undefined && maxLowBalanceThreshold != null && isNumber(maxLowBalanceThreshold) && isNumber(lowBalanceThreshold) && (lowBalanceThreshold * 1 > maxLowBalanceThreshold * 1)) 1650 return; 1651 } 1652 1653 if ($el.is("[type='radio']") && !$el.is(":checked")) 1654 $el = $("input[name='"+$el.attr("name")+"']:checked"); 1655 if ($el.val() == 0) 1656 return; 1657 1658 var paymentAmount = $el.val(); 1659 if (paymentAmount == "other") { 1660 $el = $("input[name='OTHER_"+$el.attr("name")+"']") 1661 if ($el.val().length == 0 || !isNumber($el.val())) 1662 return; 1663 paymentAmount = $el.val(); 1664 } 1665 1666 if (!isNumber(lowBalanceThreshold) || !isNumber(paymentAmount)) 1667 return; 1668 1669 lowBalanceThreshold = lowBalanceThreshold * 2; 1670 if (paymentAmount < lowBalanceThreshold) { 1671 var fieldName = $el.attr("name"); 1672 if (fieldName.indexOf("OTHER_") == 0) 1673 fieldName = fieldName.substring(6); 1674 1675 $("input[name*='"+fieldName+"']").each(function() { 1676 var $this = $(this); 1677 var dataValidate = $this.attr("data-validate"); 1678 if (!dataValidate || dataValidate === undefined || dataValidate != "false") 1679 $this.attr('data-editautopaysettings_lowbalance-error', 1680 pageValues.messages.invalidLowBalancePayment + " " + pageValues.currencySymbol + lowBalanceThreshold.toFixed(2)); 1681 });
1682 1683 return true; 1684 } 1685 1686 return; 1687 }, 1688 phonenumber: function($el) { 1689 if ($el.is(":hidden")) 1690 return; 1691 1692 var val = $el.val().replace(/\D/g,'') 1693 var isRequired = ($el.attr('required') !== undefined); 1694 return ((isRequired || val.length > 0) && val.length != 10); 1695 }, 1696 editlowbalancesettings_lowbalance: function($el) { 1697 if ($el.is(":hidden")) 1698 return; 1699 1700 var paymentAmount = $el.val(); 1701 if (paymentAmount.length == 0 || !isNumber(paymentAmount)) 1702 return; 1703 1704 var lowBalanceFieldID = $el.attr('data-editlowbalancesettings_lowbalance'); 1705 var $lowBalanceField = $(lowBalanceFieldID); 1706 if (!($lowBalanceField instanceof jQuery)) 1707 $lowBalanceField = $("#"+lowBalanceFieldID); 1708 if (!$lowBalanceField.length || $lowBalanceField.is(":hidden")) 1709 return; 1710 1711 var lowBalanceThreshold = $lowBalanceField.val(); 1712 if (lowBalanceThreshold.length == 0 || !isNumber(lowBalanceThreshold)) 1713 return; 1714 1715 lowBalanceThreshold = lowBalanceThreshold * 2; 1716 if (paymentAmount < lowBalanceThreshold) { 1717 $el.attr('data-editlowbalancesettings_lowbalance-error', 1718 pageValues.messages.invalidLowBalancePayment + " " + pageValues.currencySymbol + lowBalanceThreshold.toFixed(2)); 1719 return true; 1720 } 1721 1722 return; 1723 }, 1724 emailaddress: function($el) { 1725 if ($el.is(":hidden")) 1726 return; 1727 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1728 return; 1729 1730 return !performEmailRegex($el.val()); 1731 }, 1732 singleemailaddress: function($el) { 1733 if ($el.is(":hidden")) 1734 return; 1735 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1736 return; 1737 1738 return !isValidEmail($el.val()); 1739 }, 1740 username: function($el) { 1741 if ($el.is(":hidden")) 1742 return; 1743 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1744 return; 1745 1746 return $el.val().indexOf(';') >= 0; 1747 }, 1748 unlimitedornumeric: function($el) { 1749 if ($el.is(":hidden")) 1750 return; 1751 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) 1752 return; 1753 1754 return (!isNumber($el.val()) && $el.val().toUpperCase() != "UNL"); 1755 }, 1756 multicheckbox_range: ValidatorClass.create(function (genSuperFn, createClass) { 1757 var _super = genSuperFn(MultiCheckboxRange); 1758 1759 function MultiCheckboxRange(el) { 1760 var _this = _super.call(this, el); 1761 _this.canShowError = false; 1762 var rangeArr = el.getAttribute('data-multicheckbox_range').split(':'); 1763 _this.min = normalizeMinMax(rangeArr[0]); 1764 _this.max = normalizeMinMax(rangeArr[1]); 1765 return _this; 1766 } 1767 1768 function normalizeMinMax(input) { 1769 var value = Number(input) || 0; 1770 return value >= 0 ? value : 0; 1771 } 1772 1773 return createClass(MultiCheckboxRange, { 1774 validate: function (el, stateless) { 1775 if ($(el).is(':hidden')) return; 1776 if (stateless) this.canShowError = true; 1777 1778 var numChecked = 0; 1779 $('input[type="checkbox"]', el).each(function () { 1780 if ($(this).is(':checked')) numChecked++; 1781 }); 1782 1783 var passes = 1784 (this.min <= 0 || numChecked >= this.min) && 1785 (this.max <= 0 || numChecked <= this.max); 1786 this.canShowError = this.canShowError || passes; 1787 return this.canShowError ? !passes : false; 1788 }, 1789 }); 1790 }) 1791 } 1792 }, options); 1793 1794 $form.validator(newOptions); 1795 1796 // Initialize required checkbox groups 1797 $("input.required-check-group").click(function() { 1798 var name = $(this).attr('name'); 1799 if (!name || name === undefined || isEmpty(name)) 1800 return; 1801 1802 var checked = false; 1803 $('[name="' + name + '"]').each(function(){ 1804 checked = $(this).is(':checked'); 1805 if (checked) 1806 return false; // Break the loop 1807 }); 1808 1809 $('[name="' + name + '"]').each(function(){ 1810 $(this).attr('data-validate', !checked); 1811 }); 1812 1813 updateBootstrapValidator($form); 1814 }); 1815 1816 $("input.required-check-group").each(function() { 1817 var name = $(this).attr('name'); 1818 if (!name || name === undefined || isEmpty(name)) 1819 return; 1820 1821 var checked = false; 1822 $('[name="' + name + '"]').each(function(){ 1823 checked = $(this).is(':checked'); 1824 if (checked) 1825 return false; // Break the loop 1826 }); 1827 1828 $('[name="' + name + '"]').each(function(){ 1829 $(this).attr('data-validate', !checked); 1830 }); 1831 }); 1832 updateBootstrapValidator($form); 1833 1834 function normalizeMinMax(input) { 1835 var value = Number(input) || 0; 1836 return value >= 0 ? value : 0; 1837 } 1838 1839 $('[data-multicheckbox_range]').on('change', 'input[type="checkbox"]', function(e) { 1840 $(this).closest('[data-multicheckbox_range]').trigger('change.bs.validator'); 1841 }); 1842 } 1843 else { 1844 $form.attr('novalidate', true) // Disable automatic native validation 1845 } 1846 } 1847} 1848 1849function updateBootstrapValidator($form) { 1850 if ($form == null) { 1851 console.error('utils.js:updateBootstrapValidator - form is null') 1852 return; 1853 } 1854 if (!($form instanceof jQuery)) 1855 $form = $("#" + $form); 1856 1857 if ($form.length) { 1858 if (!shouldDisableBootstrapValidator()) { 1859 $form.validator('update'); 1860 } 1861 else { 1862 $form.attr('novalidate', true) // Disable automatic native validation 1863 } 1864 } 1865} 1866 1867function validateBootstrapValidator($form, $el, focusError) { 1868 if ($form == null) { 1869 console.error('utils.js:validateBootstrapValidator - form is null') 1870 return; 1871 } 1872 if (!($form instanceof jQuery)) 1873 $form = $("#" + $form); 1874 1875 if ($form.length) { 1876 if (!shouldDisableBootstrapValidator()) { 1877 if ($el != null && ($el || $el !== undefined)) { 1878 if (!($el instanceof jQuery)) 1879 $el = $("#" + $el); 1880 1881 if ($el.length) { 1882 $form.validator('validate', $el, focusError); 1883 } else { 1884 $form.validator('validate', null, focusError); 1885 } 1886 } else { 1887 $form.validator('validate', null, focusError); 1888 } 1889 } 1890 else { 1891 $form.attr('novalidate', true) // Disable automatic native validation 1892 } 1893 } 1894} 1895 1896function destroyBootstrapValidator($form) { 1897 if ($form == null) { 1898 console.error('utils.js:destroyBootstrapValidator - form is null') 1899 return; 1900 } 1901 if (!($form instanceof jQuery)) 1902 $form = $("#" + $form); 1903 1904 if ($form.length) { 1905 if (!shouldDisableBootstrapValidator()) { 1906 $form.validator('destroy'); 1907 } 1908 else { 1909 $form.attr('novalidate', true) // Disable automatic native validation 1910 } 1911 } 1912} 1913 1914function shouldDisableBootstrapValidator() { 1915 if ($("#sessionNoValidate").length || window.location.href.indexOf("novalidate") != -1) 1916 return true; 1917 1918 try { 1919 if (typeof developerOptions !== 'undefined') { 1920 if ('enableValidator' in developerOptions) { 1921 return !developerOptions.enableValidator; 1922 } 1923 } 1924 1925 if (typeof getDeveloperOptions !== 'undefined') { 1926 if (!getDeveloperOptions().enableValidator) 1927 return true; 1928 } 1929 1930 if (window.msbDevOptions) { 1931 return !msbDevOptions.validatorIsEnabled(); 1932 } 1933 } catch (exception) {} 1934 1935 return false; 1936} 1937 1938function performEmailRegex(email) { 1939 //Regex translated from BaseActionSupport.validateEmailAddress 1940 var emailValidator = /\S+@\S+\.\S+/; 1941 return emailValidator.test(email); 1942} 1943 1944/** 1945 * Tests if a given string is a valid email. This function is not intended to 1946 * validate a string which may contain one or more emails. Use a different 1947 * function for that. 1948 * 1949 * @param {string} email 1950 * @returns true is given string is an email, else false 1951 */ 1952var isValidEmail; 1953isValidEmail = (function() { 1954 const singleEmailRegex =/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/; 1955 return function (email) { 1956 if (typeof email === 'string') { 1957 return singleEmailRegex.test(email.toLowerCase()); 1958 } 1959 return false; 1960 } 1961})(); 1962 1963function isValidRoutingNumber(routingNumber) { 1964 // Code translated from CustomValidator.isBankRoutingNumber() 1965 if (routingNumber.length != 9) 1966 return false; 1967 1968 var multiples = [3, 7, 1]; 1969 var sum = 0; 1970 var counter = 0; 1971 1972 for (var i = 0; i < routingNumber.length; i++) { 1973 if (!isNumber(routingNumber.charAt(i))) 1974 return false; 1975 1976 var numericDigit = parseInt(routingNumber.charAt(i), 10); 1977 sum = sum + (numericDigit * multiples[counter]); 1978 counter = (counter + 1) % 3; 1979 } 1980 1981 if (sum == 0 || sum % 10 != 0) 1982 return false; 1983 1984 return true; 1985} 1986 1987function isValidCreditCardNumber(cardNumber) { 1988 // Code translated from org.apache.commons.validator.routines.CreditCardValidator.isValid() 1989 var formattedCardNumber = ""; 1990 for (var i = 0; i < cardNumber.length; i++) { 1991 if (isNumber(cardNumber.charAt(i))) 1992 formattedCardNumber = formattedCardNumber + cardNumber.charAt(i); 1993 } 1994 1995 if (isEmpty(formattedCardNumber)) 1996 return false; 1997 1998 if (!performRegexCheck(formattedCardNumber)) 1999 return false; 2000 2001 if (!performLuhnCheck(formattedCardNumber)) 2002 return false; 2003 2004 return true; 2005} 2006 2007function getCardType($el, $cardTypeField) { 2008 // if the card type field already has a value, default to that first 2009 var cardType = $cardTypeField.val(); 2010 var value = $el.val(); 2011 2012 // If the page does not allow card type to change, then return the value we found 2013 if (typeof pageValues !== 'undefined' && typeof pageValues.canSetCardType !== 'undefined' && !isTrue(pageValues.canSetCardType)) 2014 return cardType; 2015 2016 if (value.length > 0) 2017 { 2018 // if we're changing the card number set a dummy value 2019 if (value.indexOf("*") < 0)
2020 cardType = 'invalid'; 2021 2022 if (value.indexOf("4") == 0) 2023 cardType = "visa"; 2024 else if (value.indexOf("5") == 0 || value.indexOf("2") == 0) 2025 cardType = "mastercard"; 2026 else if (value.indexOf("3") == 0) 2027 cardType = "amex"; 2028 else if (value.indexOf("6") == 0) 2029 cardType = "discover"; 2030 } 2031 else 2032 cardType = 'invalid'; 2033 2034 return cardType; 2035} 2036 2037function isValidCreditCardLength($el, cardType) { 2038 var value = $el.val(); 2039 if (cardType == 'visa' && value.length != 13 && value.length < 16) 2040 return false; 2041 else if ((cardType == 'mastercard' || cardType == 'discover') && value.length < 16) 2042 return false; 2043 else if (cardType == 'amex' && value.length < 15) 2044 return false; 2045 return true; 2046} 2047 2048function isValidCvvLength($el, cardType) { 2049 var value = $el.val(); 2050 var cvvError = $el.attr('data-creditcardcvvlength-text'); 2051 if ('mastercard|visa|discover'.indexOf(cardType) >= 0 && value.length != 3) { 2052 $el.attr('data-creditcardcvvlength-error', cvvError.replace('{0}', '3')); 2053 return false; 2054 } 2055 else if (cardType == 'amex' && value.length != 4) { 2056 $el.attr('data-creditcardcvvlength-error', cvvError.replace('{0}', '4')); 2057 return false; 2058 } 2059 return true; 2060} 2061 2062function setCardIcon($el, cardType) { 2063 if (typeof pageValues !== 'undefined' && typeof pageValues.canSetCardType !== 'undefined' && !isTrue(pageValues.canSetCardType)) 2064 return; 2065 2066 var cardIconID = $el.attr('data-creditcardiconid'); 2067 if (isEmpty(cardIconID)) 2068 return; 2069 2070 var $cardIcon = $(cardIconID); 2071 if (!($cardIcon instanceof jQuery)) 2072 $cardIcon = $("#"+cardIconID); 2073 if (!$cardIcon.length) 2074 return; 2075 2076 var iconClass = 'far fa-credit-card'; 2077 2078 if (!isEmpty(cardType) && cardType != 'invalid') 2079 iconClass = 'fab fa-cc-' + cardType; 2080 2081 $cardIcon.attr('class', iconClass); 2082} 2083 2084function setCardType($el) 2085{ 2086 if (typeof pageValues !== 'undefined' && typeof pageValues.canSetCardType !== 'undefined' && !isTrue(pageValues.canSetCardType)) 2087 return; 2088 2089 var data = $el.attr('data-creditcardnumber').split(';'); 2090 if (data.length < 2) 2091 return; 2092 2093 var cardTypeFieldID = data[0]; 2094 var $cardTypeField = $(cardTypeFieldID); 2095 if (!($cardTypeField instanceof jQuery)) 2096 $cardTypeField = $("#"+cardTypeFieldID); 2097 if (!$cardTypeField.length) 2098 return; 2099 2100 // Since we are no longer using a select, set the hidden value manually 2101 var cardType = getCardType($el, $cardTypeField); 2102 2103 setCardIcon($el, cardType); 2104 $cardTypeField.val(cardType); 2105} 2106 2107function performRegexCheck(cardNumber) { 2108 // Code translated from org.apache.commons.validator.routines.CreditCardValidator 2109 if (/^(3[47]\d{13})$/.test(cardNumber)) // American Express 2110 return true; 2111 else if (/^(6011\d{12})$/.test(cardNumber) || /^(64[4-9]\d{13})$/.test(cardNumber) || /^(65\d{14})$/.test(cardNumber)) // Discover 2112 return true; 2113 else if (/^(5[1-5]\d{14})$/.test(cardNumber) || /^(2221\d{12})$/.test(cardNumber) || /^(222[2-9]\d{12})$/.test(cardNumber) 2114 || /^(22[3-9]\d{13})$/.test(cardNumber) || /^(2[3-6]\d{14})$/.test(cardNumber) || /^(27[01]\d{13})$/.test(cardNumber) 2115 || /^(2720\d{12})$/.test(cardNumber)) // Mastercard 2116 return true; 2117 else if (/^(4)(\d{12}|\d{15})$/.test(cardNumber)) // Visa 2118 return true; 2119 else 2120 return false; 2121} 2122 2123function performLuhnCheck(cardNumber) { 2124 // Code translated from org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit.isValid() 2125 if (isEmpty(cardNumber)) 2126 return false; 2127 2128 var positionWeight = [2, 1]; 2129 var total = 0; 2130 2131 for (var i = 0; i < cardNumber.length; i++) { 2132 if (!isNumber(cardNumber.charAt(i))) 2133 return false; 2134 2135 var numericDigit = parseInt(cardNumber.charAt(i), 10); 2136 var weightedValue = numericDigit * positionWeight[(cardNumber.length - i) % 2]; 2137 if (weightedValue > 9) 2138 weightedValue = weightedValue - 9; 2139 total = total + weightedValue; 2140 } 2141 2142 if (total == 0 || total % 10 != 0) 2143 return false; 2144 2145 return true; 2146} 2147 2148 2149function isValidDateRange(startDate, endDate, formatMask){ 2150 2151 if(!formatMask || formatMask === undefined) 2152 formatMask = $("#pageValuesDatePickerFormat").val(); 2153 2154 var start = moment(startDate, formatMask, true) 2155 var end = moment(endDate, formatMask, true) 2156 2157 if (start.isValid() && end.isValid()) 2158 return end.diff(start) >= 0; 2159 else
2160 return false; 2161} 2162 2163function initRetypeFieldValidator($form, $field, $retypeField, showFeedback) { 2164 if (!shouldDisableBootstrapValidator()) { 2165 if (!($form instanceof jQuery)) 2166 $form = $("#" + $form); 2167 if (!($field instanceof jQuery)) 2168 $field = $("#" + $field); 2169 if (!($retypeField instanceof jQuery)) 2170 $retypeField = $("#" + $retypeField); 2171 2172 $field.on('change keyup blur', function(e) { 2173 if (!isEmpty($retypeField.val())) { 2174 $retypeField.change(); 2175 } 2176 }); 2177 2178 $retypeField.on('change keyup blur', function(e) { 2179 validateBootstrapValidator($form, $retypeField, false); 2180 2181 if ($retypeField.is(":visible")) { 2182 if (isTrue(showFeedback)) { 2183 if (!isEmpty($retypeField.val()) && $retypeField.val() === $field.val()) { 2184 $field.closest('.form-group').addClass('has-feedback'); 2185 $field.siblings('.form-control-feedback').show(); 2186 } else { 2187 $field.closest('.form-group').removeClass('has-feedback has-success'); 2188 $field.siblings('.form-control-feedback').hide(); 2189 } 2190 2191 updateBootstrapValidator($form); 2192 validateBootstrapValidator($form, $field, false); 2193 } 2194 } 2195 }); 2196 } 2197} 2198function initNoMatchFieldValidator($form, $field, $noMatchField) { 2199 if (!shouldDisableBootstrapValidator()) { 2200 if (!($form instanceof jQuery)) 2201 $form = $("#" + $form); 2202 if (!($field instanceof jQuery)) 2203 $field = $("#" + $field); 2204 if (!($noMatchField instanceof jQuery)) 2205 $noMatchField = $("#" + $noMatchField); 2206 2207 $field.on('change keyup blur', function(e) { 2208 if (!isEmpty($noMatchField.val())) { 2209 $noMatchField.change(); 2210 } 2211 }); 2212 2213 $noMatchField.on('change keyup blur', function(e) { 2214 validateBootstrapValidator($form, $noMatchField, false); 2215 }); 2216 } 2217} 2218function toggleFieldError(selector, error) { 2219 if (isEmpty(selector)) return false; 2220 if (typeof error === 'undefined') error = ''; 2221 2222 if ($.isArray(selector)) { 2223 $.each(selector, function(idx, val) { 2224 toggleFieldError(val); // Recursively call toggleFieldError 2225 }); 2226 } else { 2227 var $field = (!(selector instanceof jQuery)) ? $(selector) : selector; 2228 $field.each(function(idx) { 2229 var $this = $(this), $frmGroup = $this.closest(".form-group"); 2230 2231 if ($frmGroup.hasClass("has-feedback")) { 2232 $frmGroup.data("hasFeedback", true); 2233 } 2234 2235 $frmGroup.removeClass("has-error has-success"); 2236 if (!isEmpty(error)) { 2237 $frmGroup.removeClass("has-feedback").addClass("has-error").find(".help-block").html(error); 2238 } else { 2239 $frmGroup.find(".help-block").empty(); 2240 if (isTrue($frmGroup.data("hasFeedback"))) { 2241 $frmGroup.addClass("has-feedback") 2242 $this.siblings('.form-control-feedback').show(); 2243 } else { 2244 $this.siblings('.form-control-feedback').hide(); 2245 } 2246 } 2247 }); 2248 } 2249} 2250 2251function getBrowserLocale() { 2252 return (navigator.languages && navigator.languages.length)?navigator.languages[0]:navigator.language; 2253} 2254 2255var $memObj = $("<div></div>"); 2256function htmlEncode(value) { 2257 if (isEmpty(value)) { 2258 return ''; 2259 } else if ($.isArray(value)) { 2260 $.each(value, function(idx, val) { 2261 value[idx] = htmlEncode(val); // Recursively call encode 2262 }); 2263 return value; 2264 } else if ($.type(value) === "string") { 2265 return $memObj.empty().text(value).html(); 2266 } else { 2267 // If it's not an array or string don't corrupt it! 2268 return value; 2269 } 2270} 2271function htmlDecode(value) { 2272 if (isEmpty(value)) { 2273 return ''; 2274 } else if ($.isArray(value)) { 2275 $.each(value, function(idx, val) { 2276 value[idx] = htmlDecode(val); // Recursively call decode 2277 }); 2278 return value; 2279 } else if ($.type(value) === "string") { 2280 /* First attempt to use DOMParser if it is supported by the browser. 2281 * innerHTML approach can execute script when value is img tag with 2282 * onerror attribute, for example: <img src="xss" onerror="alert(1)"> 2283 * NOTE: This will also strip leading whitespace */ 2284 try { 2285 var doc = new DOMParser().parseFromString(value, 'text/html'); 2286 return (doc.body.textContent || ""); 2287 } catch (exception) {} 2288 2289 /* If browser does not support DOMParser, fall back to innerHTML approach. 2290 * jQuery will parse and execute script when using html() (at least in IE). 2291 * Use the safer alternative [innerHTML] */ 2292 $memObj.empty(); 2293 try { $memObj[0].innerHTML = value; } catch (exception) {} 2294 return $memObj.text(); 2295 } else { 2296 // If it's not an array or string don't corrupt it! 2297 return value; 2298 } 2299} 2300 2301function stripHtml(value) { 2302 /* This method was the same as htmlDecode. It has been maintained to 2303 * avoid breaking any existing references, but its functionality has 2304 * been forwarded along to htmlDecode. [ECoggin, 8/1/19] */ 2305 return htmlDecode(value); 2306} 2307 2308function squishString(val){ 2309 val = val.trim(); 2310 return val.replace(/ +/g, ' '); 2311} 2312 2313function escapeUnsafeCharacters(val) { 2314 if (isEmpty(val)) 2315 return ""; 2316 val = val.replace(/[`']/g, "'"); 2317 val = val.replace(/=/g, "="); 2318 return val; 2319} 2320 2321/* Creates and returns a new debounced version of the passed function that will postpone 2322 * its execution until after wait milliseconds have elapsed 2323 */ 2324function debounce(fn, delay) { 2325 var timer = null; 2326 return function () { 2327 var context = this, args = arguments; 2328 clearTimeout(timer); 2329 timer = setTimeout(function () {
2330 fn.apply(context, args); 2331 }, delay); 2332 }; 2333} 2334function isElementInScroll($elem) { 2335 if ((typeof $elem === 'undefined') || $elem == null) { 2336 return false; 2337 } 2338 2339 if (!($elem instanceof jQuery)) 2340 $elem = $("#" + $elem); 2341 2342 if ($elem.length) { 2343 var $win = $(window), 2344 docViewTop = $win.scrollTop(), 2345 docViewBottom = docViewTop + $win.height(), 2346 elemTop = $elem.offset().top, 2347 elemBottom = elemTop + $elem.height(); 2348 /* 2349 console.log('docViewTop: '+docViewTop); 2350 console.log('docViewBottom: '+docViewBottom); 2351 console.log('elemTop: '+elemTop); 2352 console.log('elemBottom: '+elemBottom); 2353 */ 2354 return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); 2355 } 2356 return false; 2357} 2358function isElementInView($elem) { 2359 if ((typeof $elem === 'undefined') || $elem == null) { 2360 return false; 2361 } 2362 if (!($elem instanceof jQuery)) 2363 $elem = $("#" + $elem); 2364 2365 //return $elem.offset().top - $(window).scrollTop() < $elem.height() ; 2366 var result = false, docViewTop = $(window).scrollTop(), 2367 docViewBottom = docViewTop + $(window).height(); 2368 2369 //Support for multiple elements $(".many-elements") 2370 $elem.each(function() { 2371 var $this = $(this), 2372 elemTop = $this.offset().top, elemBottom = elemTop + $this.height(); 2373 2374 if ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)) { 2375 result = true; 2376 return false; //break loop 2377 } 2378 }); 2379 2380 return result; 2381} 2382/* Desktop Notifications WIP (Only supported in Chrome/Firefox as of OCT-2017 */ 2383function showDesktopNotification(options){ 2384 var notify;
2385 2386 options = $.extend({ 2387 title: "MSB Notification", 2388 body: "Some message from MSB", //"" 2389 //icon: "https://test.www.myschoolbucks.com/images/msb_logo.png", 2390 //icon: "https://test.www.myschoolbucks.com/images/favicon-32x32.png", 2391 delay:10000, //milliseconds delay to hide notification (default 10 seconds) 2392 nonSupportMessage: '', //'This browser does not support desktop notifications!', //alert if the browser does not support desktop notification (empty means no alert) 2393 onclick: function () { notify.close(); } 2394 }, options); 2395 2396 if (!("Notification" in window)) { 2397 if (!isEmpty(options.nonSupportMessage)) 2398 alert(options.nonSupportMessage); 2399 } else if (Notification.permission === "granted") { 2400 notify = new Notification(options.title, options); 2401 if (options.delay > 0) { 2402 notify.onshow=function(){ 2403 setTimeout(notify.close.on(notify), options.delay); 2404 } 2405 } 2406 notify.onclick = options.onclick; 2407 } else if (Notification.permission !== 'denied') { 2408 Notification.requestPermission(function (permission) { 2409 if (permission === "granted") { 2410 notify = new Notification(options.title, options); 2411 } 2412 }); 2413 } 2414} 2415 2416function initSummernote($field, options) { 2417 if ($field == null) { 2418 console.error('utils.js:initSummernote - field is null') 2419 return; 2420 } 2421 if (!($field instanceof jQuery)) 2422 $field = $("#" + $field); 2423 2424 if ($field.length) { 2425 options = $.extend({ 2426 placeholder: '', 2427 height: 150, 2428 minHeight: null, 2429 maxHeight: null, 2430 focus: false, 2431 style: ['style', 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript', 'clear'], 2432 color: ['color'], 2433 para: ['ul', 'ol', 'paragraph'], 2434 insert: ['hr'], //'link','table' 2435 misc: ['undo', 'redo', 'codeview'], //'fullscreen' 2436 callbacks: {} 2437 }, options); 2438 2439 $field.summernote({ 2440 disableDragAndDrop: true, 2441 fontNames: ['Arial', 'Arial Black', 'Comic Sans MS', 'Courier New'], 2442 lang: (typeof msbApp !== 'undefined') ? msbApp.getScriptLang() : 'en-US', 2443 height: options.height, 2444 minHeight: options.minHeight, 2445 maxHeight: options.maxHeight, 2446 focus: options.focus, 2447 placeholder: options.placeholder, 2448 toolbar: [ 2449 ['style', options.style], 2450 ['color', options.color], 2451 ['para', options.para], 2452 ['insert', options.insert], 2453 ['misc', options.misc] 2454 ], 2455 callbacks: options.callbacks, 2456 dialogsInBody: true 2457 }); 2458 } 2459} 2460 2461function isSummernote($field) { 2462 if ($field == null) { 2463 console.error('utils.js:isSummernote - field is null') 2464 return false; 2465 } 2466 if (!($field instanceof jQuery)) 2467 $field = $("#" + $field); 2468 2469 if ($field.length) { 2470 return ($field.data().summernote != undefined); 2471 } 2472 2473 return false; 2474} 2475 2476function isSummernoteEmpty($field) { 2477 if ($field == null) { 2478 console.error('utils.js:isSummernoteEmpty - field is null') 2479 return true; 2480 } 2481 if (!($field instanceof jQuery)) 2482 $field = $("#" + $field); 2483 2484 if ($field.length) { 2485 if (isSummernote($field)) { 2486 return (isEmpty($field.val()) || ($field.val() == "<span class=\"p-span\"><br></span>") || $field.summernote('isEmpty')); 2487 } else { 2488 return (isEmpty($field.val())); 2489 } 2490 } 2491 2492 return true; 2493} 2494 2495function getCleanSummernoteValue($field) { 2496 if ($field == null) { 2497 console.error('utils.js:getCleanSummernoteValue - field is null') 2498 return ""; 2499 } 2500 if (!($field instanceof jQuery)) 2501 $field = $("#" + $field); 2502 2503 if ($field.length) { 2504 if (isSummernote($field)) { 2505 if (isSummernoteEmpty($field)) { 2506 return ""; 2507 } else { 2508 /* Summernote uses <p> tags for line breaks, but we don't want to use them 2509 * in order to avoid unnecessary spacing and because the content might 2510 * already be inside a <p> tag on the parent side. Instead, replace the <p> 2511 * tags with styled <span> tags 2512 */ 2513 var value = $field.val(); 2514 value = value.replace(/<p>/g, "<span class=\"p-span\">") 2515 value = value.replace(/<\/p>/g, "</span>"); 2516 return value; 2517 } 2518 } else { 2519 return $field.val(); 2520 } 2521 } 2522 2523 return ""; 2524} 2525 2526function destroySummernote($field) { 2527 if ($field == null) { 2528 console.error('utils.js:destroySummernote - field is null') 2529 return; 2530 } 2531 if (!($field instanceof jQuery)) 2532 $field = $("#" + $field); 2533 2534 if ($field.length && isSummernote($field)) { 2535 $field.summernote('destroy'); 2536 $field.val(''); // Clear value after destroying Summernote to avoid bug in IE 2537 } 2538} 2539 2540function sortSelectByText($sel) { 2541 if ($sel == null) { 2542 console.error('utils.js:sortSelectByText - sel is null') 2543 return; 2544 } 2545 if (!($sel instanceof jQuery)) 2546 $sel = $("#" + $sel); 2547 2548 if ($sel.length) { 2549 $sel.html($sel.find('option').sort(function(x, y) { 2550 return ($(x).text() > $(y).text()) ? 1 : -1; 2551 })); 2552 } 2553} 2554 2555function mapURLParams(data) 2556{ 2557 var newUrl = ''; 2558 $.each(data, function(key, val){ 2559 if (isEmpty(newUrl)) 2560 newUrl += '?'; 2561 else 2562 newUrl += '&'; 2563 newUrl += key + "=" + val; 2564 }); 2565 2566 return newUrl; 2567} 2568 2569function reloadSelect2(data, $control) 2570{ 2571 clearSelectToggleProps($control, $control.data('select2').options.options); 2572 var arHtml = []; 2573 $.each(data, function(key,val){ 2574 arHtml.push('<option value="'+key+'">'+val+'</option>'); 2575 }); 2576 $control.append(arHtml.join(''));
2577 2578 $control.select2($control.data('select2').options.options); 2579} 2580 2581// Takes 2 select2 controls and makes them mutually exclusive 2582function initXOrSelect2s($select1, $select2) 2583{ 2584 $select1.change(function(){ 2585 var $this = $(this); 2586 2587 if ($this.val() === $select2.val()) 2588 $select2.val(''); 2589 2590 $select2.find('option').removeAttr('disabled'); 2591 $select2.find('option[value*="' + $this.val() + '"').attr('disabled', 'disabled'); 2592 $select2.select2($select2.data('select2').options.options); 2593 }) 2594 2595 $select2.change(function(){ 2596 var $this = $(this); 2597 2598 if ($this.val() === $select1.val()) 2599 $select1.val(''); 2600 2601 $select1.find('option').removeAttr('disabled'); 2602 $select1.find('option[value*="' + $this.val() + '"').attr('disabled', 'disabled'); 2603 $select1.select2($select1.data('select2').options.options); 2604 }) 2605 2606 $select1.change(); 2607 $select2.change(); 2608} 2609 2610function getDateTimePickerDefaultsBS5() { 2611 return { 2612 startDate: null, startDateDefault:null, startDateButtons:{}, 2613 endDate: null, endDateDefault:null, 2614 minDate: moment().subtract(99, 'years').startOf('day'), 2615 maxDate: moment().add(99, 'years').endOf('day'), 2616 format: 'MM/DD/YYYY', 2617 widgetPositioning: { horizontal: "auto", vertical: "bottom" }, 2618 onChangeCallback: null 2619 //,debug:true //for troubleshooting style, the date picker to stay open after a blur event 2620 }; 2621} 2622 2623function initDateTimePickerBS5($input, options) { 2624 options = $.extend(true, getDateTimePickerDefaultsBS5(), options); 2625 //console.log('initDateTimePickerBS5 options:',options) 2626 2627 var $parent = $input.parent(); 2628 var $target = getDateTimePickerTargetBS5($input); 2629 // We need to set attributes to make this all work 2630 // Is this a stand alone input, or an input group? 2631 if ($parent.hasClass('input-group')) { 2632 if (isEmpty($parent.attr('id'))) 2633 $parent.attr('id', 'dtp' + $input.attr('id')).attr('data-target-input', 'nearest'); 2634 $input.attr('data-target', '#' + $parent.attr('id')); 2635 2636 var $inputAddOn = isBootstrap5() ? $parent.find(".input-group-text") : $parent.find(".input-group-append"); 2637 if ($inputAddOn.length) 2638 $inputAddOn.attr('data-toggle', 'datetimepicker').attr('data-target', $input.attr('data-target')); 2639 } else { 2640 $input.attr('data-target', '#' + $input.attr('id')); 2641 } 2642 $input.attr('data-toggle', 'datetimepicker'); 2643 if (!$input.hasClass('datetimepicker-input')) 2644 $input.addClass('datetimepicker-input'); 2645 2646 $target.datetimepicker(options); 2647} 2648 2649function initStartEndDateTimePickersBS5($startDate, $endDate, options) { 2650 options = $.extend(true, getDateTimePickerDefaultsBS5(), options); 2651 2652 if (options.endDate != null) { 2653 initDateTimePickerBS5($startDate, $.extend(true, {}, options, {useCurrent:false, maxDate:options.endDate, defaultDate:options.startDateDefault, buttons:options.startDateButtons})); 2654 } else { 2655 initDateTimePickerBS5($startDate, $.extend(true, {}, options, {defaultDate:options.startDateDefault, buttons:options.startDateButtons})); 2656 } 2657 2658 if (options.startDate != null) { 2659 initDateTimePickerBS5($endDate, $.extend(true, {}, options, {useCurrent:false, minDate:options.startDate})); 2660 } else { 2661 initDateTimePickerBS5($endDate, $.extend(true, {useCurrent: false}, options)); 2662 } 2663 2664 if (!options.msbSkipChangeListeners) { 2665 const $startDateTarget = getDateTimePickerTargetBS5($startDate), 2666 $endDateTarget = getDateTimePickerTargetBS5($endDate); 2667 2668 $startDateTarget.on("change.datetimepicker", function (e) { 2669 if (e.date) 2670 $endDateTarget.datetimepicker('minDate', e.date); 2671 else if (options.minDate) 2672 $endDateTarget.datetimepicker('minDate', options.minDate); 2673 if (options.onChangeCallback && _.isFunction(options.onChangeCallback)) { 2674 options.onChangeCallback(e); 2675 } 2676 }); 2677 2678 $endDateTarget.on("change.datetimepicker", function (e) { 2679 if (e.date) 2680 $startDateTarget.datetimepicker('maxDate', e.date); 2681 else if (options.maxDate) 2682 $startDateTarget.datetimepicker('maxDate', options.maxDate); 2683 if (options.onChangeCallback && _.isFunction(options.onChangeCallback)) { 2684 options.onChangeCallback(e); 2685 } 2686 }); 2687 } 2688} 2689 2690function resetStartEndDateTimePickersBS5($startDate, $endDate) { 2691 $startDate.val(''); 2692 $endDate.val(''); 2693 const $startDateTarget = getDateTimePickerTargetBS5($startDate), 2694 $endDateTarget = getDateTimePickerTargetBS5($endDate); 2695 $startDateTarget.datetimepicker('destroy'); 2696 $endDateTarget.datetimepicker('destroy'); 2697} 2698 2699function getDateTimePickerTargetBS5($input) { 2700 if ($input.parent().hasClass('input-group')) 2701 return $input.parent(); 2702 return $input; 2703} 2704 2705// Usage of this function is discouraged. Use only as a last resort. 2706function isIe11() { 2707 var ua = window.navigator.userAgent; 2708 if (ua.indexOf('Trident/') > 0) { 2709 var rv = ua.indexOf('rv:'); 2710 // IE 11 => return version number 2711 return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10) === 11; 2712 } 2713 return false; 2714} 2715 2716 2717/** 2718 * ('{0} is {1}', ['cat', 'dog']) => 'cat is dog' 2719 * 2720 * @param {string} str Resource string which contains values like {0}, {1}, etc. 2721 * @param {string[]}
2721 replacementsArr Array of strings which should be used to 2722 * replace {0}, etc. The length of this array should match the number of 2723 * replacements that will occur. 2724 * @returns String generated from replacing {0}, etc. with values in 2725 * replacementsArr 2726 */ 2727function resourceReplaceParams(str, replacementsArr) { 2728 let ret = ''; 2729 let currentChunkStart = 0; 2730 2731 str.replace(/\{(\d+)\}/g, function (match, captureGroup, offset) { 2732 const replacementNo = Number(captureGroup); 2733 2734 ret += str.substring(currentChunkStart, offset); 2735 2736 if (replacementNo >= 0 && replacementNo < replacementsArr.length) { 2737 ret += replacementsArr[replacementNo]; 2738 } else { 2739 ret += match; 2740 } 2741 currentChunkStart = offset + match.length; 2742 }); 2743 2744 if (currentChunkStart > 0) { 2745 ret += str.substring(currentChunkStart); 2746 } 2747 2748 return ret; 2749} 2750 2751function checkBackgroundingStatus(clientKey, origin, itemCount, callback) { 2752 $.ajax({ 2753 traditional : true, 2754 url : '/ver2/admin/scheduledjobs/checkbackgroundingstatus', 2755 data: { clientKey: clientKey, origin: origin, itemCount: itemCount }, 2756 type : 'POST' 2757 }).done(function(data) { 2758 //console.log('checkBackgroundingStatus: success!'); 2759 if (data && isTrue(data.shouldBackground) && !isTrue(data.canBackground)) { 2760 if (data.messages) { 2761 var out = (Array.isArray(data.messages)) ? data.messages.join('<br>') : data.messages; 2762 msbApp.notify({message:out},{delay:0,type:'danger'}); 2763 } 2764 msbApp.hideWaitingDialog(); 2765 2766 } else if (_.isFunction(callback)) { callback(); } 2767 2768 }).fail(function(xhr, textStatus, errorThrown) { 2769 console.log('checkBackgroundingStatus - error:', errorThrown); 2770 }); 2771} 2772 2773function emptyDomElement(elm) { 2774 if (elm) { 2775 while(elm.firstChild) elm.removeChild(elm.firstChild); 2776 } 2777} 2778 2779/* Param String phone number 2780 * Returns formated phone Number 2781 * landline number - 2-digit area code followed by a non-breaking space. Then write the rest of the number in 2 chunks of 4 digits. e.g. 02 5550 4321 2782 * mobile number - 1 chunk of 4 digits and then 2 chunks of 3 digits. e.g. 0491 570 159 2783 * */ 2784function formatAuPhone(value) { 2785 2786 if (!isEmpty(value)) { 2787 var formated_phone = value.replace(/\D/g,''); 2788 var areaCode = value.substr(0,2).replace(/\D/g,''); 2789 var phoneNumber = value.replace(/\D/g,''); 2790 var secondChunk =""; 2791 var thirdChunk =""; 2792 if(areaCode == '04' || areaCode == '05') { 2793 firstChunk = phoneNumber.substring(0,4); 2794 if(phoneNumber.substring(4,7) != '' && phoneNumber.substring(4,7).length <= 3){ 2795 secondChunk = phoneNumber.substring(4,7); 2796 } 2797 if(phoneNumber.substring(7,10) != '' && phoneNumber.substring(7,10).length <= 3){ 2798 thirdChunk = phoneNumber.substring(7,10); 2799 } 2800 if(formated_phone.length == 10) { 2801 formated_phone = firstChunk+ " "+secondChunk+ " "+ thirdChunk; 2802 } 2803 } 2804 if(areaCode == '02' || areaCode == '03' || areaCode == '07' || areaCode == '08' ) { 2805 firstChunk = phoneNumber.substring(0,2); 2806 if(phoneNumber.substring(2,6) != '' && phoneNumber.substring(2,6).length <= 4){ 2807 secondChunk = phoneNumber.substring(2,6); 2808 } 2809 if(phoneNumber.substring(6,10) != '' && phoneNumber.substring(6,10).length <= 4){ 2810 thirdChunk = phoneNumber.substring(6,10); 2811 } 2812 if(formated_phone.length == 10) { 2813 formated_phone = firstChunk+ " "+secondChunk+ " "+ thirdChunk; 2814 } 2815 } 2816 return formated_phone; 2817 } 2818} 2819 2820/* Param String phone number 2821 * Returns formated phone Number 2822 *mobile number - 1 chunk of 4 digits and then 2 chunks of 3 digits. e.g. 0491 570 159 2823 * */ 2824function formatAuMobile(value) { 2825 2826 if (!isEmpty(value)) { 2827 var formated_phone = value.replace(/\D/g,''); 2828 var areaCode = value.substr(0,2).replace(/\D/g,''); 2829 var phoneNumber = value.replace(/\D/g,''); 2830 var secondChunk =""; 2831 var thirdChunk =""; 2832 if(areaCode == '04' || areaCode == '05') { 2833 firstChunk = phoneNumber.substring(0,4); 2834 if(phoneNumber.substring(4,7) != '' && phoneNumber.substring(4,7).length <= 3){ 2835 secondChunk = phoneNumber.substring(4,7); 2836 } 2837 if(phoneNumber.substring(7,10) != '' && phoneNumber.substring(7,10).length <= 3){ 2838 thirdChunk = phoneNumber.substring(7,10); 2839 } 2840 if(formated_phone.length == 10) { 2841 formated_phone = firstChunk+ " "+secondChunk+ " "+ thirdChunk; 2842 } 2843 } 2844 return formated_phone; 2845 } 2846} 2847 2848function initBootstrap5Validator($form, options) { 2849 var datePickerFormat = "MM/DD/YYYY"; 2850 if($("#pageValuesDatePickerFormat") && $("#pageValuesDatePickerFormat").val()) { 2851 datePickerFormat = $("#pageValuesDatePickerFormat").val(); 2852 } 2853 if ($form == null) { 2854 console.error('utils.js:initBootstrap5Validator - form is null') 2855 return; 2856 } 2857 if (!($form instanceof jQuery)) 2858 $form = $("#" + $form); 2859 2860 if ($form.length) { 2861 if (!shouldDisableBootstrapValidator()) { 2862 // Add Bootstrap 5 validation class 2863 $form.addClass('needs-validation'); 2864 $form.attr('novalidate', true); // Disable default browser validation 2865 2866 // Default options for Bootstrap 5 2867 var newOptions = $.extend(true, { 2868 feedback: { 2869 success: 'fa-check-circle', 2870 error: '' 2871 } 2872 }, options); 2873 2874 // Custom validation functions for Bootstrap 5 2875 var customValidators = { 2876 match: function($el) {
2877 if ($el.is(":hidden")) return null; 2878 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2879 2880 var matchFieldID = $el.attr('data-match'); 2881 var $matchField = $(matchFieldID); 2882 if (!($matchField instanceof jQuery)) 2883 $matchField = $("#"+matchFieldID); 2884 if (!$matchField.length) return null; 2885 2886 return $el.val() !== $matchField.val() ? $el.attr('data-match-error') || 'Fields do not match' : null; 2887 }, 2888 nomatch: function($el) { 2889 if ($el.is(":hidden")) return null; 2890 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2891 2892 var noMatchFieldID = $el.attr('data-nomatch'); 2893 var $noMatchField = $(noMatchFieldID); 2894 if (!($noMatchField instanceof jQuery)) 2895 $noMatchField = $("#"+noMatchFieldID); 2896 if (!$noMatchField.length) return null; 2897 2898 return $el.val() === $noMatchField.val() ? $el.attr('data-nomatch-error') || 'Fields must not match' : null; 2899 }, 2900 minlength: function($el) { 2901 if ($el.is(":hidden")) return null; 2902 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2903 2904 var val = $el.val(); 2905 if (isTrue($el.attr('data-minlength-squish'))) 2906 val = squishString(val); 2907 var minlength = $el.attr('data-minlength'); 2908 return val.length < minlength ? $el.attr('data-minlength-error') || ('Minimum length is ' + minlength) : null; 2909 }, 2910 passwordpattern: function($el) { 2911 if ($el.is(":hidden")) return null; 2912 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2913 2914 var val = $el.val(); 2915 const re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()+=-\?;,./{}|\":<>\[\]\\\' ~_])/; 2916 return !re.test(val) ? $el.attr('data-passwordpattern-error') || 'Password must contain uppercase, lowercase, number and special character' : null; 2917 }, 2918 passwordwhitespace: function($el) { 2919 if ($el.is(":hidden")) return null; 2920 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2921 2922 var val = $el.val(); 2923 if (val == null) return null; 2924 return val.trim().length !== val.length ? $el.attr('data-passwordwhitespace-error') || 'Password cannot contain leading or trailing spaces' : null; 2925 }, 2926 maxlength: function($el) { 2927 if ($el.is(":hidden")) return null; 2928 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2929 2930 var val = $el.val(); 2931 if (isTrue($el.attr('data-maxlength-squish'))) 2932 val = squishString(val); 2933 var maxlength = $el.attr('data-maxlength'); 2934 return val.length > maxlength ? $el.attr('data-maxlength-error') || ('Maximum length is ' + maxlength) : null; 2935 }, 2936 greaterthan: function($el) { 2937 if ($el.is(":hidden")) return null; 2938 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2939 2940 var valueToBeGreaterThan = $el.attr('data-greaterthan'); 2941 if (!isNumber(valueToBeGreaterThan) || !isNumber($el.val())) return null; 2942 2943 return parseFloat($el.val()) <= parseFloat(valueToBeGreaterThan) ? $el.attr('data-greaterthan-error') || ('Value must be greater than ' + valueToBeGreaterThan) : null; 2944 }, 2945 greaterthanorequalto: function($el) { 2946 if ($el.is(":hidden")) return null; 2947 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2948 2949 var valueToBeGreaterThanOrEqualTo = $el.attr('data-greaterthanorequalto'); 2950 if (!isNumber(valueToBeGreaterThanOrEqualTo) || !isNumber($el.val())) return null; 2951 2952 return parseFloat($el.val()) < parseFloat(valueToBeGreaterThanOrEqualTo) ? $el.attr('data-greaterthanorequalto-error') || ('Value must be greater than or equal to ' + valueToBeGreaterThanOrEqualTo) : null; 2953 }, 2954 lessthan: function($el) { 2955 if ($el.is(":hidden")) return null; 2956 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2957 2958 var valueToBeLessThan = $el.attr('data-lessthan'); 2959 if (!isNumber(valueToBeLessThan) || !isNumber($el.val())) return null; 2960 2961 return parseFloat($el.val()) >= parseFloat(valueToBeLessThan) ? $el.attr('data-lessthan-error') || ('Value must be less than ' + valueToBeLessThan) : null; 2962 }, 2963 lessthanorequalto: function($el) {
2964 if ($el.is(":hidden")) return null; 2965 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2966 2967 var valueToBeLessThanOrEqualTo = $el.attr('data-lessthanorequalto'); 2968 if (!isNumber(valueToBeLessThanOrEqualTo) || !isNumber($el.val())) return null; 2969 2970 return parseFloat($el.val()) > parseFloat(valueToBeLessThanOrEqualTo) ? $el.attr('data-lessthanorequalto-error') || ('Value must be less than or equal to ' + valueToBeLessThanOrEqualTo) : null; 2971 }, 2972 between: function($el) { 2973 if ($el.is(":hidden")) return null; 2974 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2975 2976 var value = parseFloat($el.val()); 2977 var range = $el.attr('data-between'); 2978 var min = parseFloat(range.split('-')[0]); 2979 var max = parseFloat(range.split('-')[1]); 2980 2981 if (!isNumber(value) || !isNumber(min) || !isNumber(max)) return null; 2982 2983 if (max > min && (value < min || value > max)) { 2984 return $el.attr('data-range-error') || ('Value must be between ' + min + ' and ' + max); 2985 } else if (value < min) { 2986 return $el.attr('data-min-error') || ('Value must be greater than ' + min); 2987 } 2988 2989 return null; 2990 }, 2991 numeric: function($el) { 2992 if ($el.is(":hidden")) return null; 2993 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 2994 2995 return !isNumber($el.val()) ? $el.attr('data-numeric-error') || 'Must be a number' : null; 2996 }, 2997 containsnumber: function($el) { 2998 if ($el.is(":hidden")) return null; 2999 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3000 3001 return !containsNumber($el.val()) ? $el.attr('data-containsnumber-error') || 'Must contain a number' : null; 3002 }, 3003 date: function($el) { 3004 if ($el.is(":hidden")) return null; 3005 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3006 3007 return !moment($el.val(), datePickerFormat, true).isValid() ? $el.attr('data-date-error') || 'Invalid date' : null; 3008 }, 3009 dateafter: function($el) { 3010 if ($el.is(":hidden")) return null; 3011 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3012 if (($el.attr('data-date') || $el.attr('data-date') !== undefined) && !moment($el.val(), datePickerFormat, true).isValid()) return null; 3013 3014 var startDateFieldID = $el.attr('data-dateafter'); 3015 var $startDateField = $(startDateFieldID); 3016 if (!($startDateField instanceof jQuery)) 3017 $startDateField = $("#"+startDateFieldID); 3018 if (!$startDateField.length || $startDateField.val().length == 0 || !moment($startDateField.val(), datePickerFormat, true).isValid()) return null; 3019 3020 return !isValidDateRange($startDateField.val(), $el.val()) ? $el.attr('data-dateafter-error') || 'Date must be after start date' : null; 3021 }, 3022 datebefore: function($el) { 3023 if ($el.is(":hidden")) return null; 3024 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3025 if (($el.attr('data-date') || $el.attr('data-date') !== undefined) && !moment($el.val(), datePickerFormat, true).isValid()) return null; 3026 3027 var endDateFieldID = $el.attr('data-datebefore'); 3028 var $endDateField = $(endDateFieldID); 3029 if (!($endDateField instanceof jQuery)) 3030 $endDateField = $("#"+endDateFieldID); 3031 if (!$endDateField.length || $endDateField.val().length == 0 || !moment($endDateField.val(), datePickerFormat, true).isValid()) return null; 3032 3033 return !isValidDateRange($el.val(), $endDateField.val()) ? $el.attr('data-datebefore-error') || 'Date must be before end date' : null; 3034 }, 3035 dualrequired: function($el) { 3036 if ($el.is(":hidden")) return null; 3037 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3038 3039 var dualRequiredFieldID = $el.attr('data-dualrequired'); 3040 var $dualRequiredField = $(dualRequiredFieldID); 3041 if (!($dualRequiredField instanceof jQuery)) 3042 $dualRequiredField = $("#"+dualRequiredFieldID); 3043 if (!$dualRequiredField.length) return null; 3044 3045 return ($el.val().length == 0 || $dualRequiredField.val().length == 0) ? $el.attr('data-dualrequired-error') || 'Both fields are required' : null; 3046 }, 3047 emailaddress: function($el) {
3048 if ($el.is(":hidden")) return null; 3049 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3050 3051 return !performEmailRegex($el.val()) ? $el.attr('data-emailaddress-error') || 'Invalid email address' : null; 3052 }, 3053 singleemailaddress: function($el) { 3054 if ($el.is(":hidden")) return null; 3055 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3056 3057 return !isValidEmail($el.val()) ? $el.attr('data-singleemailaddress-error') || 'Invalid email address' : null; 3058 }, 3059 username: function($el) { 3060 if ($el.is(":hidden")) return null; 3061 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3062 3063 return $el.val().indexOf(';') >= 0 ? $el.attr('data-username-error') || 'Username cannot contain semicolons' : null; 3064 }, 3065 phonenumber: function($el) { 3066 if ($el.is(":hidden")) return null; 3067 3068 var val = $el.val().replace(/\D/g,'') 3069 var isRequired = ($el.attr('required') !== undefined); 3070 return ((isRequired || val.length > 0) && val.length != 10) ? $el.attr('data-phonenumber-error') || 'Phone number must be 10 digits' : null; 3071 }, 3072 unlimitedornumeric: function($el) { 3073 if ($el.is(":hidden")) return null; 3074 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3075 3076 return (!isNumber($el.val()) && $el.val().toUpperCase() != "UNL") ? $el.attr('data-unlimitedornumeric-error') || 'Must be a number or UNL' : null; 3077 }, 3078 routingnumber: function($el) { 3079 if ($el.is(":hidden")) return null; 3080 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3081 3082 var value = $el.val(); 3083 var previousValue = $el.attr('data-routingnumber'); 3084 3085 if (value != previousValue) { 3086 if (!isValidRoutingNumber(value)) 3087 return $el.attr('data-routingnumber-error') || 'Invalid routing number'; 3088 } 3089 3090 return null; 3091 }, 3092 checkingacctnumber: function($el) { 3093 if ($el.is(":hidden")) return null; 3094 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3095 3096 var value = $el.val(); 3097 var previousValue = $el.attr('data-checkingacctnumber'); 3098 3099 if (value != previousValue) { 3100 var formattedValue = ""; 3101 for (var i = 0; i < value.length; i++) { 3102 if (isNumber(value.charAt(i))) 3103 formattedValue = formattedValue + value.charAt(i); 3104 } 3105 return (formattedValue.length < 1 || formattedValue.length > 17) ? $el.attr('data-checkingacctnumber-error') || 'Account number must be between 1 and 17 digits' : null; 3106 } 3107 3108 return null; 3109 }, 3110 creditcardlength: function($el) { 3111 if ($el.is(":hidden")) return null; 3112 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3113 3114 var data = $el.attr('data-creditcardnumber').split(';'); 3115 if (data.length < 2) return null; 3116 3117 var cardTypeFieldID = data[0]; 3118 var $cardTypeField = $(cardTypeFieldID); 3119 if (!($cardTypeField instanceof jQuery)) 3120 $cardTypeField = $("#"+cardTypeFieldID); 3121 if (!$cardTypeField.length) return null; 3122 var previousValue = data[1]; 3123 3124 if ($el.val() != previousValue) { 3125 var cardType = getCardType($el, $cardTypeField); 3126 if (!isValidCreditCardLength($el, cardType)) 3127 return $el.attr('data-creditcardlength-error') || 'Invalid credit card length'; 3128 } 3129 return null; 3130 }, 3131 creditcardcvvlength: function($el) { 3132 if ($el.is(":hidden")) return null; 3133 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3134 3135 var cardType = 'invalid'; 3136 3137 if (!isEmpty($el.attr('data-cardtypefield-id'))) { 3138 var cardTypeFieldID = $el.attr('data-cardtypefield-id'); 3139 var $cardTypeField = $(cardTypeFieldID); 3140 if (!($cardTypeField instanceof jQuery)) 3141 $cardTypeField = $("#"+cardTypeFieldID); 3142 if (!$cardTypeField.length) return null; 3143 3144 cardType = $cardTypeField.val(); 3145 } else { 3146 var cardSelectID = $el.attr('data-cardselect-id'); 3147 var selectedOption = $(cardSelectID + ' option:selected').text().toLowerCase(); 3148 if (selectedOption.indexOf('visa') >= 0) 3149 cardType = 'visa'; 3150 else if (selectedOption.indexOf('mastercard') >= 0) 3151 cardType = 'mastercard'; 3152 else if (selectedOption.indexOf('discover') >= 0) 3153 cardType = 'discover'; 3154 else if (selectedOption.indexOf('american express') >= 0) 3155 cardType = 'amex'; 3156 } 3157 3158 if (!isValidCvvLength($el, cardType)) 3159 return $el.attr('data-creditcardcvvlength-error') || 'Invalid CVV length'; 3160 3161 return null; 3162 }, 3163 creditcardnumber: function($el) {
3164 if ($el.is(":hidden")) return null; 3165 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3166 3167 var data = $el.attr('data-creditcardnumber').split(';'); 3168 if (data.length < 2) return null; 3169 3170 var cardTypeFieldID = data[0]; 3171 var $cardTypeField = $(cardTypeFieldID); 3172 if (!($cardTypeField instanceof jQuery)) 3173 $cardTypeField = $("#"+cardTypeFieldID); 3174 if (!$cardTypeField.length) return null; 3175 3176 var cardType = getCardType($el, $cardTypeField); 3177 var acceptedCardTypes = $el.attr('data-acceptedcardtypes'); 3178 3179 // Don't throw an error here if card type is invalid or not accepted - will be handled by acceptedcardtypes validator 3180 if (isEmpty(cardType) || (!isEmpty(acceptedCardTypes) && JSON.stringify(acceptedCardTypes).indexOf(cardType) < 0) || !isValidCreditCardLength($el, cardType)) 3181 return null; 3182 3183 var value = $el.val(); 3184 var previousValue = data[1]; 3185 3186 if (value != previousValue) { 3187 if (!isValidCreditCardNumber(value)) 3188 return $el.attr('data-creditcardnumber-error') || 'Invalid credit card number'; 3189 3190 if (cardType == "visa" && value.indexOf("4") != 0) 3191 return $el.attr('data-creditcardnumber-error') || 'Invalid Visa card number'; 3192 else if (cardType == "mastercard" && (value.indexOf("5") != 0 && value.indexOf("2") != 0)) 3193 return $el.attr('data-creditcardnumber-error') || 'Invalid Mastercard number'; 3194 else if (cardType == "amex" && value.indexOf("3") != 0) 3195 return $el.attr('data-creditcardnumber-error') || 'Invalid American Express number'; 3196 else if (cardType == "discover" && value.indexOf("6") != 0) 3197 return $el.attr('data-creditcardnumber-error') || 'Invalid Discover card number'; 3198 } 3199 3200 return null; 3201 }, 3202 acceptedcardtypes: function($el) { 3203 if ($el.is(":hidden")) return null; 3204 if (($el.attr('required') || $el.attr('required') !== undefined) && $el.val().length == 0) return null; 3205 3206 var data = $el.attr('data-creditcardnumber').split(';'); 3207 if (data.length < 2) return null; 3208 3209 var cardTypeFieldID = data[0]; 3210 var $cardTypeField = $(cardTypeFieldID); 3211 if (!($cardTypeField instanceof jQuery)) 3212 $cardTypeField = $("#"+cardTypeFieldID); 3213 if (!$cardTypeField.length) return null; 3214 3215 var acceptedCardTypes = $el.attr('data-acceptedcardtypes'); 3216 if (!acceptedCardTypes.length) return null; 3217 3218 var cardType = getCardType($el, $cardTypeField); 3219 if (isEmpty(cardType)) return null; 3220 3221 if (acceptedCardTypes.indexOf(cardType) < 0) { 3222 var cardTypeMap = JSON.parse($el.attr('data-cardtypemap')); 3223 var cardTypeMapError = $el.attr('data-acceptedcardtypeserror-text'); 3224 3225 if (!isEmpty(cardTypeMap) && !isEmpty(cardTypeMapError) && !isEmpty(cardTypeMap[cardType])) 3226 return cardTypeMapError.replace('{0}', cardTypeMap[cardType]); 3227 else 3228 return $el.attr('data-acceptedcardtypes-error') || 'Card type not accepted'; 3229 } 3230 3231 return null; 3232 }, 3233 paymentmethods_expirationdatemonth: function($el) { 3234 if ($el.is(":hidden")) return null; 3235 3236 var expYearFieldID = $el.attr('data-paymentmethods_expirationdatemonth'); 3237 var $expYearField = $(expYearFieldID); 3238 if (!($expYearField instanceof jQuery)) 3239 $expYearField = $("#"+expYearFieldID); 3240 if (!$expYearField.length) return null; 3241 3242 var selectedMonth = $el.val(); 3243 var selectedYear = $expYearField.val(); 3244 if (selectedMonth.length == 0 || selectedYear.length == 0) return null; 3245 3246 if (isNumber(selectedMonth) && isNumber(selectedYear)) { 3247 var date = new Date(); 3248 return (selectedYear < date.getFullYear() || (selectedYear == date.getFullYear() && selectedMonth < date.getMonth())) ? $el.attr('data-paymentmethods_expirationdatemonth-error') || 'Expiration date has passed' : null; 3249 } 3250 3251 return null; 3252 }, 3253 paymentmethods_expirationdateyear: function($el) { 3254 if ($el.is(":hidden")) return null; 3255 3256 var expMonthFieldID = $el.attr('data-paymentmethods_expirationdateyear'); 3257 var $expMonthField = $(expMonthFieldID); 3258 if (!($expMonthField instanceof jQuery)) 3259 $expMonthField = $("#"+expMonthFieldID); 3260 if (!$expMonthField.length) return null; 3261 3262 var selectedYear = $el.val(); 3263 var selectedMonth = $expMonthField.val(); 3264 if (selectedYear.length == 0 || selectedMonth.length == 0) return null; 3265 3266 if (isNumber(selectedYear) && isNumber(selectedMonth)) { 3267 var date = new Date(); 3268 return (selectedYear < date.getFullYear() || (selectedYear == date.getFullYear() && selectedMonth < date.getMonth())) ? $el.attr('data-paymentmethods_expirationdateyear-error') || 'Expiration date has passed' : null; 3269 } 3270 3271 return null; 3272 }, 3273 editautopaysettings_lowbalance: function($el) {
3274 if ($el.is(":hidden")) return null; 3275 3276 var name = $el.attr('data-editautopaysettings_lowbalance'); 3277 var $fieldToCheck = $("input[name='"+name+"']:checked"); 3278 if (!$fieldToCheck.length || $fieldToCheck.is(":hidden")) return null; 3279 3280 var lowBalanceThreshold = $fieldToCheck.val(); 3281 if (lowBalanceThreshold == "other") { 3282 $fieldToCheck = $("input[name='OTHER_"+name+"']"); 3283 if ($fieldToCheck.val().length == 0 || !isNumber($fieldToCheck.val())) return null; 3284 lowBalanceThreshold = $fieldToCheck.val(); 3285 3286 var maxLowBalanceThreshold = $fieldToCheck.attr("data-lessthanorequalto"); 3287 if (maxLowBalanceThreshold !== undefined && maxLowBalanceThreshold != null && isNumber(maxLowBalanceThreshold) && isNumber(lowBalanceThreshold) && (lowBalanceThreshold * 1 > maxLowBalanceThreshold * 1)) 3288 return null; 3289 } 3290 3291 if ($el.is("[type='radio']") && !$el.is(":checked")) 3292 $el = $("input[name='"+$el.attr("name")+"']:checked"); 3293 if ($el.val() == 0) return null; 3294 3295 var paymentAmount = $el.val(); 3296 if (paymentAmount == "other") { 3297 $el = $("input[name='OTHER_"+$el.attr("name")+"']") 3298 if ($el.val().length == 0 || !isNumber($el.val())) return null; 3299 paymentAmount = $el.val(); 3300 } 3301 3302 if (!isNumber(lowBalanceThreshold) || !isNumber(paymentAmount)) return null; 3303 3304 lowBalanceThreshold = lowBalanceThreshold * 2; 3305 if (paymentAmount < lowBalanceThreshold) { 3306 return $el.attr('data-editautopaysettings_lowbalance-error') || (pageValues.messages.invalidLowBalancePayment + " " + pageValues.currencySymbol + lowBalanceThreshold.toFixed(2)); 3307 } 3308 3309 return null; 3310 }, 3311 editlowbalancesettings_lowbalance: function($el) { 3312 if ($el.is(":hidden")) return null; 3313 3314 var paymentAmount = $el.val(); 3315 if (paymentAmount.length == 0 || !isNumber(paymentAmount)) return null; 3316 3317 var lowBalanceFieldID = $el.attr('data-editlowbalancesettings_lowbalance'); 3318 var $lowBalanceField = $(lowBalanceFieldID); 3319 if (!($lowBalanceField instanceof jQuery)) 3320 $lowBalanceField = $("#"+lowBalanceFieldID); 3321 if (!$lowBalanceField.length || $lowBalanceField.is(":hidden")) return null; 3322 3323 var lowBalanceThreshold = $lowBalanceField.val(); 3324 if (lowBalanceThreshold.length == 0 || !isNumber(lowBalanceThreshold)) return null; 3325 3326 lowBalanceThreshold = lowBalanceThreshold * 2; 3327 if (paymentAmount < lowBalanceThreshold) { 3328 return $el.attr('data-editlowbalancesettings_lowbalance-error') || (pageValues.messages.invalidLowBalancePayment + " " + pageValues.currencySymbol + lowBalanceThreshold.toFixed(2)); 3329 } 3330 3331 return null; 3332 } 3333 }; 3334 3335 function validateElement($el) { 3336 if ($el.is(':hidden')) { 3337 $el.get(0).setCustomValidity(''); 3338 $el.removeClass('is-valid is-invalid'); 3339 return; 3340 } 3341 3342 var isValid = true; 3343 var errorMessage = ''; 3344 3345 $.each($el.get(0).attributes, function(index, attr) { 3346 if (attr.name.startsWith('data-') && customValidators[attr.name.substring(5)]) { 3347 var validatorName = attr.name.substring(5); 3348 var error = customValidators[validatorName]($el); 3349 if (error) { 3350 isValid = false; 3351 errorMessage = error; 3352 return false; // Break the loop 3353 } 3354 } 3355 }); 3356 3357 $el.get(0).setCustomValidity(isValid ? '' : errorMessage); 3358 3359 var validationMessage = errorMessage || $el.get(0).validationMessage; 3360 3361 if ($el.data('bs5-interacted')) { 3362 if ($el.get(0).checkValidity()) { 3363 $el.removeClass('is-invalid').addClass('is-valid'); 3364 showErrorMessage($el, ''); 3365 } else { 3366 $el.removeClass('is-valid').addClass('is-invalid'); 3367 showErrorMessage($el, validationMessage); 3368 } 3369 } 3370 } 3371 3372 function showErrorMessage($el, message) { 3373 var $feedback = $el.siblings('.invalid-feedback').first(); 3374 if (!$feedback.length) { 3375 $feedback = $el.parent().find('.invalid-feedback').first(); 3376 } 3377 if (!$feedback.length) { 3378 $feedback = $el.closest('.form-group, .mb-3, .form-floating, .input-group').find('.invali
3378d-feedback').first(); 3379 } 3380 3381 if (!$feedback.length && $el.parent().hasClass('input-group')) { 3382 $feedback = $el.parent().parent().find('.invalid-feedback').first(); 3383 } 3384 3385 if ($feedback.length) { 3386 if (message) { 3387 $feedback.text(message).show(); 3388 } else { 3389 $feedback.text('').hide(); 3390 } 3391 } else if (message) { 3392 $feedback = $('<div class="invalid-feedback d-block"></div>').text(message); 3393 3394 if ($el.parent().hasClass('input-group')) { 3395 $el.parent().after($feedback); 3396 } else { 3397 $el.after($feedback); 3398 } 3399 } 3400 3401 if (message) { 3402 $el.attr('title', message); 3403 } else { 3404 $el.removeAttr('title'); 3405 } 3406 } 3407 3408 $form.find('input, select, textarea').each(function() { 3409 var $el = $(this); 3410 3411 $el.on('focus.bs5validator', function() { 3412 $(this).data('bs5-interacted', true); 3413 }); 3414 3415 $el.on('input.bs5validator change.bs5validator blur.bs5validator', function() { 3416 if ($(this).data('bs5-interacted')) { 3417 validateElement($(this)); 3418 } 3419 }); 3420 3421 $el.get(0).setCustomValidity(''); 3422 $el.removeClass('is-valid is-invalid'); 3423 }); 3424 3425 $("input.required-check-group").click(function() { 3426 var name = $(this).attr('name'); 3427 if (!name || isEmpty(name)) return; 3428 3429 var $group = $('[name="' + name + '"]'); 3430 var checked = $group.is(':checked'); 3431 var errorMsg = checked ? '' : 'Please select at least one option'; 3432 3433 $group.each(function(){ 3434 var $this = $(this); 3435 $this.data('bs5-interacted', true); 3436 $this.get(0).setCustomValidity(errorMsg); 3437 $this.removeClass('is-valid is-invalid').addClass(checked ? 'is-valid' : 'is-invalid'); 3438 showErrorMessage($this, errorMsg); 3439 }); 3440 }); 3441 3442 $("input.required-check-group").each(function() { 3443 var name = $(this).attr('name'); 3444 if (name && !isEmpty(name)) { 3445 $('[name="' + name + '"]').each(function(){ 3446 this.setCustomValidity(''); 3447 $(this).removeClass('is-valid is-invalid'); 3448 }); 3449 } 3450 }); 3451 3452 // Handle form submission with namespaced event 3453 $form.on('submit.bs5validator', function(e) { 3454 var form = this; 3455 3456 // Re-validate all elements before submission 3457 $form.find('input, select, textarea').each(function() { 3458 validateElement($(this)); 3459 }); 3460 3461 // Check only VISIBLE elements for validation 3462 var hasVisibleInvalidElements = false; 3463 $form.find('input, select, textarea').each(function() { 3464 var $el = $(this); 3465 if (!$el.is(':hidden') && !this.checkValidity()) { 3466 hasVisibleInvalidElements = true; 3467 return false; // Break the loop 3468 } 3469 }); 3470 3471 if (hasVisibleInvalidElements) { 3472 e.preventDefault(); 3473 e.stopPropagation(); 3474 } 3475 3476 form.classList.add('was-validated'); 3477 }); 3478 3479 // Handle multi-checkbox range validation 3480 $('[data-multicheckbox_range]').each(function() { 3481 var $container = $(this); 3482 var range = $container.attr('data-multicheckbox_range').split(':'); 3483 var min = parseInt(range[0]) || 0; 3484 var max = parseInt(range[1]) || 0; 3485 3486 function validateCheckboxRange() { 3487 var numChecked = $container.find('input[type="checkbox"]:checked').length; 3488 var isValid = (min <= 0 || numChecked >= min) && (max <= 0 || numChecked <= max); 3489 3490 var errorMessage = ''; 3491 if (!isValid) { 3492 if (min > 0 && max > 0) { 3493 errorMessage = 'Please select between ' + min + ' and ' + max + ' options'; 3494 } else if (min > 0) { 3495 errorMessage = 'Please select at least ' + min + ' option(s)'; 3496 } else if (max > 0) { 3497 errorMessage = 'Please select no more than ' + max + ' option(s)'; 3498 } 3499 } 3500 3501 // Set validity on the container or first checkbox 3502 var $firstCheckbox = $container.find('input[type="checkbox"]').first(); 3503 if ($firstCheckbox.length) { 3504 $firstCheckbox.get(0).setCustomValidity(isValid ? '' : errorMessage); 3505 if (isValid) { 3506 $firstCheckbox.removeClass('is-invalid').addClass('is-valid'); 3507 } else { 3508 $firstCheckbox.removeClass('is-valid').addClass('is-invalid'); 3509 } 3510 } 3511 } 3512 3513 $container.find('input[type="checkbox"]').on('change', validateCheckboxRange); 3514 validateCheckboxRange(); // Initial validation 3515 }); 3516 } else { 3517 $form.attr('novalidate', true); // Disable automatic native validation 3518 } 3519 } 3520} 3521 3522function updateBootstrap5Validator($form) { 3523 if ($form == null) { 3524 console.error('utils.js:updateBootstrap5Validator - form is null') 3525 return; 3526 } 3527 if (!($form instanceof jQuery)) 3528 $form = $("#" + $form); 3529 3530 if ($form.length) { 3531 if (!shouldDisableBootstrapValidator()) { 3532 // Re-validate all form elements 3533 $form.find('input, select, textarea').each(function() { 3534 $(this).trigger('blur'); 3535 }); 3536 } else { 3537 $form.attr('novalidate', true); // Disable automatic native validation 3538 } 3539 } 3540} 3541 3542function validateBootstrap5Validator($form, $el, focusError) { 3543 if ($form == null) { 3544 console.error('utils.js:validateBootstrap5Validator - form is null') 3545 return false; 3546 } 3547 if (!($form instanceof jQuery)) 3548 $form = $("#" + $form); 3549 3550 if ($form.length) { 3551 if (!shouldDisableBootstrapValidator()) { 3552 if ($el != null && ($el || $el !== undefined)) { 3553 if (!($el instanceof jQuery)) 3554 $el = $("#" + $el); 3555 3556 if ($el.length) { 3557 $el.trigger('blur'); // Trigger validation on specific element 3558 var isValid = $el.get(0).checkValidity(); 3559 if (focusError && !isValid) { 3560 $el.focus(); 3561 } 3562 return isValid; 3563 } else { 3564 // Validate entire form 3565 $form.find('input, select, textarea').each(function() { 3566 $(this).trigger('blur'); 3567 });
3568 var formIsValid = $form.get(0).checkValidity(); 3569 if (focusError && !formIsValid) { 3570 var $firstInvalid = $form.find('.is-invalid').first(); 3571 if ($firstInvalid.length) { 3572 $firstInvalid.focus(); 3573 } 3574 } 3575 return formIsValid; 3576 } 3577 } else { 3578 $form.find('input, select, textarea').each(function() { 3579 var $el = $(this); 3580 if ($el.is(':hidden')) { 3581 this.setCustomValidity(''); 3582 $el.removeClass('is-valid is-invalid'); 3583 } else { 3584 $el.trigger('blur'); 3585 } 3586 }); 3587 3588 var invalidCount = 0; 3589 $form.find('input, select, textarea').each(function() { 3590 var $el = $(this); 3591 if (!$el.is(':hidden') && !this.checkValidity()) { 3592 invalidCount++; 3593 } 3594 }); 3595 3596 var formIsValid = invalidCount === 0; 3597 3598 if (focusError && !formIsValid) { 3599 var $firstInvalid = $form.find('.is-invalid:visible').first(); 3600 if ($firstInvalid.length) { 3601 $firstInvalid.focus(); 3602 } 3603 } 3604 return formIsValid; 3605 } 3606 } else { 3607 $form.attr('novalidate', true); 3608 return true; 3609 } 3610 } 3611 return false; 3612} 3613 3614function destroyBootstrap5Validator($form) { 3615 if ($form == null) { 3616 console.error('utils.js:destroyBootstrap5Validator - form is null'); 3617 return; 3618 } 3619 if (!($form instanceof jQuery)) 3620 $form = $("#" + $form); 3621 3622 if ($form.length) { 3623 if (!shouldDisableBootstrapValidator()) { 3624 // Remove Bootstrap 5 validation classes and event handlers 3625 $form.removeClass('needs-validation was-validated'); 3626 $form.removeAttr('novalidate'); 3627 3628 // Clear validation states on all form elements 3629 $form.find('input, select, textarea').each(function() { 3630 var $el = $(this); 3631 $el.get(0).setCustomValidity(''); 3632 $el.removeClass('is-valid is-invalid'); 3633 $el.off('input.bs5validator change.bs5validator blur.bs5validator'); 3634 }); 3635 3636 // Remove form submission handler 3637 $form.off('submit.bs5validator'); 3638 } else { 3639 $form.attr('novalidate', true); 3640 } 3641 } 3642} 3643 3644// End of Bootstrap 5 validator functions 3645 3646// Common method used for debug logging. 3647function isDebugHost() { 3648 return (window.location.protocol === 'http:') || 3649 (window.location.hostname === 'local.www.myschoolbucks.com') || 3650 (window.location.hostname === 'test.www.myschoolbucks.com') || 3651 (window.location.hostname === 'localhost') || 3652 // [::1] is the IPv6 localhost address. 3653 (window.location.hostname === '[::1]') || 3654 // 127.0.0.1/8 is considered localhost for IPv4. 3655 window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/); 3656}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.