1/* 2The following errors were found when attempting to minify this file: 3- Line 37: Parse error. missing : after property id 4- Line 37: Parse error. illegal character 5- Line 37: Parse error. missing ; before statement 6- Line 37: Parse error. illegal character 7- Line 37: Parse error. syntax error 8- Line 38: Parse error. syntax error 9- Line 39: Parse error. syntax error 10- Line 40: Parse error. syntax error 11- Line 42: Parse error. syntax error 12- Line 43: Parse error. illegal character 13- Line 43: Parse error. missing ) after argument list 14- Line 43: Parse error. illegal character 15- Line 43: Parse error. syntax error 16- Line 44: Parse error. syntax error 17- Line 45: Parse error. syntax error 18- Line 46: Parse error. syntax error 19- Line 47: Parse error. syntax error 20- Line 49: Parse error. syntax error 21- Line 51: Parse error. missing ; before statement 22- Line 59: Parse error. syntax error 23- Line 61: Parse error. syntax error 24- Line 66: Parse error. invalid return 25- Line 67: Parse error. syntax error 26- Line 69: Parse error. missing ; before statement 27- Line 73: Parse error. syntax error 28- Line 74: Parse error. syntax error 29- Line 75: Parse error. syntax error 30- Line 77: Parse error. syntax error 31- Line 79: Parse error. missing ; before statement 32- Line 85: Parse error. illegal character 33- Line 85: Parse error. missing ; before statement 34- Line 85: Parse error. illegal character 35- Line 86: Parse error. syntax error 36- Line 87: Parse error. syntax error 37- Line 90: Parse error. syntax error 38- Line 93: Parse error. illegal character 39- Line 93: Parse error. missing ; before statement 40- Line 93: Parse error. illegal character 41- Line 97: Parse error. invalid return 42- Line 98: Parse error. syntax error 43- Line 100: Parse error. missing ; before statement 44- Line 101: Parse error. syntax error 45- Line 102: Parse error. syntax error 46- Line 103: Parse error. syntax error 47- Line 108: Parse error. invalid return 48- Line 113: Parse error. missing ; after for-loop initializer 49- Line 113: Parse error. missing ; before statement 50- Line 120: Parse error. syntax error 51- Line 124: Parse error. syntax error 52- Line 127: Parse error. syntax error 53- Line 129: Parse error. syntax error 54- Line 131: Parse error. syntax error 55- Line 132: Parse error. syntax error 56- Line 133: Parse error. syntax error 57- Line 398: Parse error. missing ; before statement 58*/ 59// Gets Json object using a given url. 60function GetJson(url) { 61 var result; 62 $.ajaxSetup({ async: false }); 63 $.post(url, function (data) { 64 result = data; 65 }); 66 $.ajaxSetup({ async: true }); 67 return result; 68} 69 70function maxLength(field, maxChars) { 71 if (field.value.length >= maxChars) { 72 event.returnValue = false; 73 return false; 74 } 75} 76 77function maxLengthPaste(field, maxChars) { 78 var copyText = field.value + window.clipboardData.getData("Text"); 79 field.value = copyText.substr(0, maxChars); 80 if ((copyText.length) > maxChars) { 81 return false; 82 } 83 event.returnValue = true; 84} 85 86function appendFilesToInput(appendInput, mainFileInput) { 87 const valid = validateMultiFileUpload(appendInput, true); 88 if (!valid) { 89 alert("The file you are trying to upload is not an allowed file type or has an invalid file name. These are the accepted characters: A-z 0-9 ~ ! ( ) - + = [ ] { } , . _"); 90 appendInput.value = null; 91 return; 92 } 93 const dt = new DataTransfer(); 94 const originalFiles = Array.from(mainFileInput.files); 95 const existingSet = new Set(originalFiles.map(({ name, size, lastModified }) => `${name}-${size}-${lastModified}`)); 96 originalFiles.forEach(file => { 97 dt.items.add(file); 98 }) 99 const addedFiles = Array.from(appendInput.files); 100 addedFiles.forEach(file => { 101 if (!existingSet.has(`${file.name}-${file.size}-${file.lastModified}`)) { 102 dt.items.add(file); 103 } 104 }) 105 mainFileInput.files = dt.files; 106 mainFileInput.dispatchEvent(new Event("change")); 107} 108 109function fileAppender(mainFileInput) { 110 const li = document.createElement("li"); 111 const label = document.createElement("label"); 112 label.innerHTML = '<span class="visuallyhidden">Add Files</span><svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" aria-hidden="true"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>' 113 const appendInput = document.createElement("input"); 114 appendInput.type = "file"; 115 appendInput.multiple = true; 116 appendInput.className = "visuallyhidden"; 117 appendInput.addEventListener("change", e => { 118 appendFilesToInput(appendInput, mainFileInput); 119 }); 120 label.appendChild(appendInput); 121 const placeholder = document.createElement("span"); 122 li.appendChild(placeholder); 123 li.appendChild(label); 124 return li; 125} 126 127function removeFileFromInput(input, idx) { 128 const dt = new DataTransfer(); 129 const files = Array.from(input.files); 130 files.splice(idx, 1);
131 files.forEach(file => { 132 dt.items.add(file); 133 }); 134 input.files = dt.files; 135} 136 137function listItemFile(input, file, idx) { 138 const li = document.createElement("li"); 139 const fileNameSpan = document.createElement("span"); 140 fileNameSpan.textContent = file.name; 141 li.appendChild(fileNameSpan); 142 const removeButton = document.createElement("button"); 143 removeButton.title = `Remove ${file.name}`; 144 removeButton.addEventListener("click", e => { 145 e.preventDefault(); 146 removeFileFromInput(input, idx); 147 input.dispatchEvent(new Event("change")); 148 }); 149 removeButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="darkred" aria-hidden="true"><path d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"></path></svg>'; 150 const removeButtonSpan = document.createElement("span"); 151 removeButtonSpan.textContent = `Remove ${file.name}`; 152 removeButtonSpan.className = "visuallyhidden"; 153 removeButton.appendChild(removeButtonSpan); 154 li.appendChild(removeButton); 155 return li; 156} 157 158function listFileInputFiles(fileInput) { 159 if (fileInput.nextElementSibling?.className === "file-list") { 160 fileInput.nextElementSibling.remove(); 161 } 162 const files = fileInput.files || []; 163 const valid = files.length && validateMultiFileUpload(fileInput, true) 164 if (!valid) { 165 fileInput.value = null; 166 return; 167 } 168 if (files.length > 1) { 169 const ul = document.createElement("ul"); 170 ul.className = "file-list"; 171 for (let i = 0; i < files.length; i++) { 172 const li = listItemFile(fileInput, files[i], i); 173 ul.appendChild(li); 174 } 175 const fileAppenderLi = fileAppender(fileInput); 176 ul.appendChild(fileAppenderLi); 177 fileInput.after(ul); 178 } 179} 180 181function initFileInputs() { 182 if (!((window?.FeatureToggles?.isActive?.("CMS.FormCenter.MultipleFileUploadEnhancement")) || (window?.FeatureToggles?.features?.["CMS.FormCenter.MultipleFileUploadEnhancement"]))) { 183 return; 184 } 185 document.querySelectorAll(".FileUpload input[type='file']").forEach(fileInput => { 186 listFileInputFiles(fileInput); 187 fileInput.addEventListener("change", e => { 188 listFileInputFiles(fileInput) 189 }) 190 }); 191} 192; 193/// <reference path="../../../../Scripts/json2.js"> 194/// <reference path="../../../../Scripts/HtmlExtensionSupport.js"> 195/// <reference path="../../../../Scripts/UrlLibrary.js"> 196/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" /> 197/// <reference path="Conditions.js" /> 198/// <reference path="Validation.js" /> 199/// <reference path="/Assets/Scripts/DynamicForm/PrintSubmission.js" /> 200/// <reference path="../../../../Assets/Scripts/DateTimePicker/cp.datetimepicker.js" /> 201 202//////// ENUMERATIONS /////////// 203var FormCenterHomeJS = (function ($) { 204 var self = this; 205 206 // This value is set on the ItemDetailFE.ascx page, using a value from the 207 // View Model. 208 self.submissionURL = ''; 209 210 return self; 211}(jQuery)); 212 213var FormCenterHomeScriptResources = GetJson("/FormCenter/Localization"); 214 215// Field formats. 216var FieldValidation = { 217 Any: 0, 218 Letters: 1, 219 Numeric: 2, 220 PhoneNumber: 3, 221 PostalCode: 4, 222 EmailAddress: 5, 223 RegEx: 6, 224 Currency: 7, 225 Date: 8, 226 Time: 9, 227 DateTime: 10, 228 DateSpan: 11, 229 TimeSpan: 12, 230 DateTimeSpan: 13 231}; 232 233// Supported field types. 234var FieldTypes = { 235 ShortAnswer: 1, 236 LongAnswer: 2, 237 Checkboxes: 3, 238 RadioButtons: 4, 239 Dropdown: 5, 240 FileUpload: 6, 241 ReplyEmail: 10, 242 Password: 11, 243 DateTime: 18, 244 EPayment: 23 245}; 246 247//////// GLOBALS //////////////// 248 249// Stores instantiated form validator/condition instances for the current form. 250var formValidators = null; 251var formConditions = null; 252 253// Use with form submissions. 254var redirectNewWindow = null; 255 256//////// SUPPORT METHODS ////////f 257 258// Shows extra information for a form detail. 259function moreInfo(id) { 260 changeInfo(id, 'addClass', 'removeClass'); 261} 262 263// Hides extra information for a form detail. 264function lessInfo(id) { 265 changeInfo(id, 'removeClass', 'addClass'); 266} 267 268// Internal. Hides or shows extra information. 269function changeInfo(id, lessFn, moreFn) { 270 $([document.getElementById('spnLess' + id), document.getElementById('lnkMore' + id)])[lessFn]('hidden'); 271 $(document.getElementById('spnMore' + id))[moreFn]('hidden'); 272} 273 274// Assigns bulk action checkbox group to jQuery expression. 275function assignBulkCheckGroup($children, $parent) { 276 // Isolate children from parent in event of overlap ($children containing the $parent). 277 // Vastly simplifies the logic in the event handlers by doing this. 278 var $isolatedChildren = $children.filter( 279 function (index) { 280 return $parent.filter(this).length == 0; 281 } 282 ); 283 284 // Cache current number of checked children. 285 $isolatedChildren.lengthChecked = $isolatedChildren.filter(':checked').length; 286 287 // Hook childen change. 288 $isolatedChildren.change(function (event) { 289 $isolatedChildren.lengthChecked += (this.checked ? 1 : -1); 290 291 var allChildrenChecked = ($isolatedChildren.lengthChecked == $isolatedChildren.length); 292 293 $parent.each(function () { 294 this.checked = allChildrenChecked; 295 }); 296 }); 297 298 // Hook parent change. 299 $parent.change(function (event) { 300 var parentChecked = this.checked; 301 302 $isolatedChildren.lengthChecked = this.checked ? $isolatedChildren.length : 0; 303 304 $isolatedChildren.each(function () { 305 this.checked = parentChecked; 306 }); 307 }); 308} 309 310function initCommon() { 311 // Hack: Internet Explorer 7 mode bizarre display bugs. 312 $('.sidebar .search.noStyles').css('zoom', '1'); 313 314 // Hook search category bulk checks. 315 var $bulkCheck = $('#categoryFilter_0'); 316 var $bulkGroup = $('.categoryList input[type="checkbox"]'); 317 318 assignBulkCheckGroup($bulkGroup, $bulkCheck); 319 320 // Get input jQuery refs.
321 var $inputFCTerm = $('#inputFCTerm'); 322 var $inputFCSearch = $('#inputFCSearch'); 323 324 // Hook search button and textbox to capture enter key. 325 $inputFCTerm.keydown(function (event) { 326 // If user presses enter, submit for search. 327 switch (event.which) { 328 case 10: // LF 329 case 13: // CR 330 event.preventDefault(); 331 $inputFCSearch.click(); 332 break; 333 } 334 }); 335 336 $inputFCSearch.click(function (event) { 337 event.preventDefault(); 338 339 // Build search querystring. 340 var qs = {}; 341 var formID = extractFormIDFromURL(location.href); 342 var categoryID = extractCategoryIDFromURL(location.href); 343 344 var searchTerm = $.trim(isNull($inputFCTerm.val(), '')); 345 346 if (formID) 347 qs.formID = formID; 348 349 if (categoryID) 350 qs.categoryID = categoryID; 351 352 if (searchTerm != '') 353 qs.term = searchTerm; 354 355 if (!$bulkCheck[0].checked) { 356 var values = []; 357 358 $bulkGroup.filter(':checked').each(function () { 359 values.push(this.value); 360 }); 361 362 if (values.length > 0) 363 qs.categoryFilter = values.join(','); 364 } 365 366 // Set search URL and go. 367 window.location = '/FormCenter/Search' + (new QueryStringBuilder(qs)).toString(); 368 }); 369 370 // Hook search category dropdown. 371 var $catList = $('#categoryList'); 372 var $catListToggle = $('a.mega'); 373 374 $catListToggle.click(function (e) { 375 e.preventDefault(); 376 377 if ($catList.hasClass('open')) 378 $catList.slideUp(300); 379 else 380 $catList.slideDown(300); 381 382 $catListToggle.toggleClass('active'); 383 $catList.toggleClass('open'); 384 }); 385 386 // Show print dialog if URL contains standard print query string. 387 if (containsPrint(location.href)) 388 window.print(); 389} 390 391// Initialization for search (Search.aspx view). 392function initSearch() { 393 if (this.wasInit) 394 return; 395 396 this.wasInit = true; 397 398 attachCategoryVisibilityTogglers(); 399 400 initCommon(); 401} 402 403// Initialization for category/form list (Index.aspx view). 404function initCategoryList() { 405 if (this.wasInit) 406 return; 407 408 this.wasInit = true; 409 410 attachCategoryVisibilityTogglers(); 411 412 initCommon(); 413} 414 415// Initialization for confirmation (Confirmation.aspx view). 416function initConfirmation() { 417 if (this.wasInit) 418 return; 419 420 this.wasInit = true; 421 422 initCommon(); 423} 424 425function initPostSubmissionSpam() { 426 initCommon(); 427} 428 429 430// Causes current form to be submitted for printing. 431function printForm(saveData) { 432 if (formValidate()) { //check for valid responses before creating print window 433 var frm = document.aspnetForm; 434 var $form = $(document.aspnetForm); 435 var printPrevType = (getPrintPreviewType() == 1 ? 'Print' : 'Preview'); 436 var formID = extractFormIDFromURL(location.href); 437 438 $form.attr('target', 'PrintWindow'); 439 // Change target/action to print data. 440 frm.action = '/FormCenter/Print?formID=' + formID + '&' + printPrevType + '=YES&Save=' + (saveData ? 'True' : 'False'); 441 442 var savedProgressID = parseInt($('#hdnSavedProgressID').val(), 10); 443 444 if (!isNaN(savedProgressID)) { 445 frm.action = frm.action + '&savedProgressID=' + savedProgressID; 446 } 447 448 submitForm($form, true, saveData, formID); 449 } else { 450 enableDisableSubmit(true); 451 } 452} 453 454function submitFormHandler(e) { 455 try { 456 let json = JSON.parse(this.response); 457 458 if (this.status !== 200 || json.Success !== true) { 459 $('.submissionConfirmationMessage').html("Something went wrong with the submission. Please try again."); 460 $('.submissionConfirmation').show(); 461 enableDisableSubmit(true); 462 return; 463 } 464 465 const responseUrl = this.responseURL && new URL(this.responseURL); 466 const saveSubmission = responseUrl && responseUrl.searchParams.get('save') && responseUrl.searchParams.get('save').toLowerCase() === 'true'; 467 const doPrint = responseUrl && responseUrl.searchParams.get('print') && responseUrl.searchParams.get('print').toLowerCase() === 'true'; 468 const redirectAfterSubmissionSaved = json.Redirect && json.Redirect === true && saveSubmission === true; 469 470 if (redirectNewWindow) { 471 // There are two possible redirects in use: 472 // (1) The regular "submit" redirect for the form vs. category confirmation page. 473 // (2) The "conditional" redirect that is applied when a form condition is met. 474 // redirectNewWindow is the latter; redirectAfterSubmissionSaved below deals with the former. 475 window.open(redirectNewWindow); 476 redirectNewWindow = ''; 477 } 478 479 if (redirectAfterSubmissionSaved) 480 { 481 if (json.IsHttps === false) { 482 json.Message = json.Message.replace("https://", "http://"); 483 } 484 485 location.href = json.Message; 486 } 487 else { 488 if (saveSubmission === true) { 489 $('.submissionConfirmationMessage').html(json.Message); 490 typeof displayStep === "function" && displayStep(1); 491 $('.submitForm').hide(); 492 $('.submissionConfirmation').show(); 493 } 494 495 enableDisableSubmit(true); 496 } 497 498 if (doPrint === true) { 499 window.open("/FormCenter/Print?formId=" + json.FormId.toString() + '&save=' + (saveSubmission ? 'True' : 'False')); 500 } 501 502 if (json.Success === true) { 503 document.aspnetForm.reset(); 504 505 if (typeof (grecaptcha) !== "undefined") 506 { 507 grecaptcha.reset(); 508 } 509 } 510 511 !window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
512 } catch (ex) { 513 console.log(ex); 514 } 515} 516 517function submitForm($form, print, saveSubmission, formID) { 518 var submittedViaWidget = $form.parents(".widgetBody").length > 0; 519 520 serializeSpecialFields(); 521 522 if (submittedViaWidget !== true) { 523 var printWindow; 524 525 if (print === true) 526 { 527 printWindow = window.open("", "PrintWindow"); 528 529 // Deferred object at the window level, this becomes available on the opened window via window.opener 530 // Whichever view gets rendered will resolve it and pass the appropriate status code. 531 window.deferredStatus = $.Deferred(); 532 } 533 534 if (printWindow) { 535 window.deferredStatus.done(function (status) { 536 if (status === CP_DynamicForm_PrintSubmission.StatusCodes.Success) { 537 if (saveSubmission && saveSubmission === true) { 538 window.location = '/FormCenter/Confirmation?formID=' + formID; 539 } 540 } else { 541 printWindow.close(); 542 543 if (saveSubmission) { 544 window.location = '/FormCenter/ErrorSubmittingForm?formID=' + formID; 545 } 546 } 547 }); 548 } 549 550 $form.submit(); 551 } 552 else { 553 var submissionUrl = FormCenterHomeJS.submissionURL + (saveSubmission ? '&save=True' : ""); 554 submissionUrl = submissionUrl + (print ? '&print=True' : ''); 555 556 var formData = new FormData($form[0]); 557 var xhr = new XMLHttpRequest(); 558 xhr.onload = submitFormHandler; 559 xhr.onloadend = ajaxPostBackEnd; 560 xhr.onerror = function() { console.log("A network error occurred with the transaction."); }; 561 xhr.open("POST", submissionUrl); 562 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 563 ajaxPostBackStart('Loading'); 564 xhr.send(formData); 565 } 566} 567 568function serializeSpecialFields() 569{ 570 submitCheckboxHandler(); 571 572 submitRadioHandler(); 573 574 submitDateTimeHandler(); 575} 576 577// Initialization for form item printing (Print.aspx view). 578function initFormPrint() { 579 if (this.wasInit) 580 return; 581 582 this.wasInit = true; 583 584 initCommon(); 585 586 initValidationAndConditions(); 587 588 registerFormPrintEvents(); 589} 590 591function initValidationAndConditions() { 592 // Register validation. 593 if (typeof getFormValidatorData !== 'undefined') { 594 formValidators = FieldValidator.createMulti(getFormValidatorData()); 595 } else { 596 formValidators = []; 597 } 598 599 // Register conditions. 600 if (getFormConditionData) { 601 formConditions = FieldConditions.createMulti(getFormConditionData(), formValidators); 602 } else { 603 formConditions = FieldConditions.createMulti([], formValidators); 604 } 605 606 // Register condition runner and make initial condition check. 607 formConditionsRunner = new FieldConditionsRunner(formConditions, $.noop, true, false); // TODO: Finish up, put debugCondRunnerLogResults for $.noop. 608 formConditionsRunner.run(); 609} 610 611// Initialization for form item (Item.aspx view). 612function initForm() { 613 614 if (this.wasInit) 615 return; 616 617 this.wasInit = true; 618 619 initCommon(); 620 initValidationAndConditions(); 621 initDatePickers(); 622 initTimePickers(); 623 initFileInputs(); 624 registerFormEvents(); // Hook up buttons (submit/cancel/etc). 625} 626 627function initDatePickers() { 628 if ($('.telerikDatePicker').length > 0) { 629 $('.telerikDatePicker').each(function () { 630 if (typeof (cp) !== 'undefined') { 631 var datePickerSelector = this.id ? '#' + this.id : this; 632 cp.datetimepicker && cp.datetimepicker.createDatePicker(datePickerSelector); 633 } 634 }); 635 } 636} 637 638function initTimePickers() { 639 if ($('.formCenterTimePicker').length > 0) { 640 $('.formCenterTimePicker').focusout(function () { 641 var text = $(this).val().toLowerCase(); 642 if (text != '' && (text.indexOf('am') < 0 && text.indexOf('pm') < 0)) { 643 text = $(this).val().trim() + ' AM'; 644 $(this).val(text); 645 } 646 }); 647 } 648 else { 649 $('.telerikTimePicker').each(function () { 650 if (typeof (cp) !== 'undefined') 651 cp.datetimepicker && cp.datetimepicker.createTimePicker(this, 652 { 653 format: 'h:i A' 654 }); 655 }); 656 } 657} 658 659// Registers event handlers for form. 660function registerFormPrintEvents() { 661 $('#btnFormContinue').click(function (event) { 662 event.preventDefault(); 663 664 var frm = document.aspnetForm; 665 var formID = extractFormIDFromURL(location.href); 666 var $form = $(document.aspnetForm); 667 668 // Change target/action to confirmation page.
669 frm.action = '/FormCenter/Confirmation?formID=' + formID; 670 671 $form.trigger("submit"); 672 }); 673} 674 675// Registers event handlers for form. 676function registerFormEvents() { 677 // Remove troublemakers (prevent the jQuery submit handler from ever firing, and not needed for this screen anyways). 678 document.aspnetForm.removeAttribute('onsubmit'); 679 document.aspnetForm.onsubmit = null; 680 681 var $form = $(document.aspnetForm); 682 683 // TODO: Plug this into other validation. 684 var wantCopyAddr, $wantCopyAddrLI; 685 var wantCopy = document.getElementById('wantCopy'); 686 687 if (wantCopy) { 688 wantCopyAddr = document.getElementById('wantCopyAddress'); 689 $wantCopyAddrLI = $(wantCopyAddr.parentNode.parentNode); 690 691 $(wantCopy).change(function (event) { 692 if (this.checked) 693 $wantCopyAddrLI.show(); 694 else 695 $wantCopyAddrLI.hide(); 696 }); 697 } 698 699 /* #2378*/ 700 $(document).keydown(function (event) { 701 if (event.keyCode === 13 && 702 event.target.tagName === 'INPUT' && 703 event.target.id !== 'btnFormSubmit' && 704 event.target.tagName !== 'TEXTAREA' && 705 event.target.tagName !== 'SELECT' && 706 event.target.getAttribute('data-enable-enter-keypress') !== 'true') { 707 event.preventDefault(); 708 return false; 709 } 710 if (event.keyCode === 32 && 711 event.target.tagName === 'A' && 712 event.target.getAttribute('role') === 'button') { 713 $(event.target).trigger('click'); 714 event.preventDefault(); 715 return false; 716 } 717 }); 718 719 $form.submit(function () { 720 // Run validation, stop data submission if necessary. 721 if (!formValidate()) { 722 enableDisableSubmit(true); 723 return false; 724 } 725 726 if (isNull(redirectNewWindow, '').trimEnd() != '') 727 window.open(redirectNewWindow); 728 729 // Field types requiring special serialization code (radio/check/file). 730 // The deserialization of dictionaries from forms in MVC 2 is somewhat sensitive. 731 serializeSpecialFields(); 732 return true; 733 }); 734 735 $('#btnFormSubmit').click(function (event) { 736 event.preventDefault(); 737 if (formValidate()) { 738 processSubmit(handleSubmitClick); 739 } 740 }); 741 742 $('#btnFormPrint').click(function (event) { 743 event.preventDefault(); 744 printForm(false); 745 }); 746 747 $('#btnFormSubmitPrint').click(function (event) { 748 event.preventDefault(); 749 if (formValidate()) { 750 processSubmit(handleSubmitPrintClick); 751 } 752 }); 753 754 $('#btnCalculateTotals').click(function (e) { 755 e.preventDefault(); 756 757 submitCheckboxHandler(); 758 submitRadioHandler(); 759 760 if (formValidate()) { 761 var $form = $(document.aspnetForm); 762 763 $.ajax({ 764 url: '/FormCenter/Home/CalculateTotal?formID=' + extractFormIDFromURL(location.href), 765 type: 'POST', 766 data: $(document.aspnetForm).serializeArray(), 767 success: function (response) { 768 openCpModal({ 769 title: FormCenterHomeScriptResources.CalculateTotals, 770 className: 'modalCalculateTotal', 771 isFrontEnd: window.location.href.indexOf('/Admin/FormCenter') == -1, 772 htmlContent: response 773 }); 774 }, 775 error: function (xhr, textStatus, exception) { 776 alert('Error: ' + xhr.statusText + '\nStatus: ' + xhr.status); 777 } 778 }); 779 } 780 }); 781 782 $('#btnProceedToCheckOut').click(function (e) { 783 proceedToCheckOut(e); 784 }); 785 786 if ($('.submissionConfirmationOk').length > 0) 787 { 788 $('.submissionConfirmationOk').unbind('click').click(function () { 789 $('.submitForm').show(); 790 $('.submissionConfirmation').hide(); 791 !window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights(); 792 }) 793 } 794} 795 796function onRecaptchaLoadCallback() { 797 window.gRecaptchaClientId = grecaptcha.render('inline-recaptcha', { 798 'sitekey': document.querySelector('#inline-recaptcha').dataset.sitekey, 799 'badge': 'inline', 800 'size': 'invisible' 801 }); 802} 803 804function setRecaptchaResponse(token) { 805 if (!token) { 806 return; 807 } 808 809 var responseFields = document.querySelectorAll('[name="g-recaptcha-response"]'); 810 for (var i = 0; i < responseFields.length; i++) { 811 responseFields[i].value = token; 812 } 813} 814 815function processSubmit(callback) { 816 const recaptcha = document.querySelector('.recaptcha'); 817 if (!recaptcha) { 818 callback(); 819 return; 820 } 821 822 grecaptcha.ready(function() { 823 grecaptcha.execute(window.gRecaptchaClientId, { action: 'submit' }).then(function(token) { 824 setRecaptchaResponse(token); 825 callback(); 826 }); 827 }); 828} 829 830function enableDisableSubmit(enable) { 831 if (enable) { 832 if ($('#btnFormSubmitPrint').length > 0) { 833 $('#btnFormSubmitPrint').removeClass('inactive'); 834 $('#btnFormSubmitPrint').click(function (event) { 835 handleSubmitPrintClick(); 836 }); 837 } 838 if ($('#btnFormSubmit').length > 0) { 839 $('#btnFormSubmit').removeClass('inactive'); 840 $('#btnFormSubmit').click(function (event) { 841 event.preventDefault(); 842 if (formValidate()) { 843 closeModalDialog('editItemBehavior'); 844 processSubmit(handleSubmitClick); 845 } 846 }); 847 } 848 } else { 849 if ($('#btnFormSubmitPrint').length > 0) { 850 $('#btnFormSubmitPrint').addClass("inactive"); 851 $('#btnFormSubmitPrint').unbind(); 852 } 853 if ($('#btnFormSubmit').length > 0) { 854 $('#btnFormSubmit').addClass("inactive"); 855 $('#btnFormSubmit').unbind(); 856 } 857 } 858} 859 860function handleSubmitClick() { 861 enableDisableSubmit(false); 862 863 var frm = document.aspnetForm; 864 865 frm.action = FormCenterHomeJS.submissionURL; 866 867 var $form = $(frm); 868 869 var formID = extractFormIDFromURL(location.href); 870 871 submitForm($form, false, true, formID); 872} 873 874function handleSubmitPrintClick() { 875 enableDisableSubmit(false); 876 printForm(true); 877} 878 879function proceedToCheckOut(e) { 880 e.preventDefault(); 881 882 serializeSpecialFields(); 883 884 if (formValidate()) { 885 var savedProgressID = parseInt($('#hdnSavedProgressID').val(), 10); 886 887 var submitUrl = '/FormCenter/Home/ProceedToCheckOut?formID=' + extractFormIDFromURL(location.href); 888 889 if (!isNaN(savedProgressID)) {
890 submitUrl = submitUrl + '&savedProgressID=' + savedProgressID; 891 } 892 893 var $form = $(document.aspnetForm); 894 $form.attr('action', submitUrl); 895 $form.submit(); 896 } 897} 898 899function getModalDialogObjects() { 900 return { 901 iframe: document.getElementById('liveEditDialog'), 902 windowElement: $('.modalContainerCP')[0], 903 behavior: $find('editItemBehavior'), 904 titleElement: $('.modalTitle')[0] 905 }; 906} 907 908function getBooleanValue(value) { 909 switch (value.toLowerCase()) { 910 case "true": case "yes": case "1": return true; 911 case "false": case "no": case "0": case null: return false; 912 default: return Boolean(string); 913 } 914} 915 916// Validates current form. 917function formValidate() { 918 var assignFocus = true; 919 var ERROR_MSG = FormCenterHomeScriptResources.InvalidEmailAddressFormat2; 920 var wantCopy = document.getElementById('wantCopy'); 921 922 if (wantCopy) { 923 wantCopyAddr = document.getElementById('wantCopyAddress'); 924 $wantCopyAddrLI = $(wantCopyAddr.parentNode.parentNode); 925 926 if ($wantCopyAddrLI.is(":visible") && wantCopyAddr.value != "") { 927 if (!emailValidate(wantCopyAddr.value)) { 928 errorShow(ERROR_MSG); 929 return false; 930 } 931 } 932 if (wantCopy.checked == false)//if the checkbox is not checked then empty the emailaddress textbox 933 document.getElementById('wantCopyAddress').value = ""; 934 } 935 else { 936 //if the checkbox is not checked then empty the emailaddress textbox 937 var wantCopyAddress = document.getElementById('wantCopyAddress'); 938 if (wantCopyAddress) 939 document.getElementById('wantCopyAddress').value = ""; 940 } 941 942 if (typeof formValidators.keys !== 'undefined') { 943 var liCurrent = $('li.current'); 944 var step = "1"; 945 if (liCurrent.length > 0) { 946 step = liCurrent.attr('id').replace('liStep', ''); 947 } 948 949 var wizardStep = $('#wizard' + step); 950 951 if (wizardStep.length > 0) { 952 wizardStep = wizardStep[0]; 953 } 954 955 var checkForInappropriateWords = new Array(); 956 957 for (var i = 0, len = formValidators.keys.length; i < len; i++) { 958 var key = formValidators.keys[i]; 959 960 if (wizardStep == null || wizardStep.length == 0 || (wizardStep != null && wizardStep.contains(formValidators[key].elem
960Container))) { 961 if (!formValidators[key].validate(assignFocus, checkForInappropriateWords)) 962 assignFocus = false; 963 } 964 } 965 966 if (validateInappropriateWords(checkForInappropriateWords)) { 967 assignFocus = false; 968 } 969 } 970 971 return assignFocus; 972} 973 974function validateInappropriateWords(checkForInappropriateWords) { 975 var data = {}; 976 var j = 0; 977 var inappropriateWordsFound = false; 978 979 for (j = 0; j < checkForInappropriateWords.length; j++) { 980 data[checkForInappropriateWords[j].id.toString()] = checkForInappropriateWords[j].elemInput.value.trim(); 981 } 982 983 var results = HasInappropriateWordsMultiple(data); 984 985 for (j = 0; j < checkForInappropriateWords.length; j++) { 986 if (results[checkForInappropriateWords[j].id.toString()] == true) { 987 checkForInappropriateWords[j].handleInappropriateWords(inappropriateWordsFound == false); 988 989 inappropriateWordsFound = true; 990 } 991 } 992 993 return inappropriateWordsFound; 994} 995 996// Displays error message if email validation failed 997function errorShow(message) { 998 var elemContainer = $('.anonEmail'); 999 elemContainer.addClass('error'); 1000 1001 if (elemContainer.find('.explanation').length == 0) { 1002 var errorElemMsg = document.createElement('p'); 1003 errorElemMsg.className = 'explanation'; 1004 errorElemMsg.innerHTML = message; 1005 elemContainer.append(errorElemMsg); 1006 } 1007} 1008 1009// Determines if URL contains print querystring. 1010function containsPrint(url) { 1011 return url.search(/(&|\?)print\=yes(&.*|)$/i) > -1; 1012} 1013 1014 1015// Extracts CategoryID identifier from URL specified. 1016function extractCategoryIDFromURL(url) { 1017 1018 url = url.toLowerCase(); 1019 var indexOfForm = url.lastIndexOf("/formcenter/"); 1020 if (indexOfForm > -1) { 1021 //extract the categoryurl 1022 var formURL = url.substr(indexOfForm + 12); 1023 1024 1025 //check whether u have formid appended 1026 1027 if (formURL.indexOf("/") > -1) 1028 formURL = formURL.substr(0, formURL.indexOf("/")); 1029 //check wheter the url has categoryName 1030 var indexOfHypen = formURL.lastIndexOf("-"); 1031 var categoryID = 0; 1032 if (indexOfHypen > -1) 1033 categoryID = formURL.substr(indexOfHypen + 1); 1034 else 1035 categoryID = formURL; 1036 return categoryID; 1037 } 1038 else 1039 return null; 1040} 1041 1042 1043// Extracts form identifier from URL specified. 1044function extractFormIDFromURL(url) { 1045 return parseInt($('#hdnFormID').val(), 10); 1046 1047 //there is some query string appended if so remove those 1048 if (url.indexOf("/?") > -1) { 1049 url = url.substr(0, url.indexOf("/?")); 1050 } 1051 1052 url = url.toLowerCase(); 1053 var indexOfForm = url.lastIndexOf("/formcenter/"); 1054 if (indexOfForm > -1) { 1055 //extract the url 1056 var formURL = url.substr(indexOfForm + 12); 1057 1058 if (formURL.indexOf("/") > -1) { 1059 formURL = formURL.substr(formURL.indexOf("/") + 1); 1060 //check wheter the url has categoryName 1061 var indexOfHypen = formURL.lastIndexOf("-"); 1062 var formID = 0; 1063 if (indexOfHypen > -1) 1064 formID = formURL.substr(indexOfHypen + 1); 1065 else if (formURL == "") 1066 return null; 1067 else 1068 formID = formURL; 1069 1070 return formID; 1071 } 1072 else 1073 return null; 1074 } 1075 else 1076 return null; 1077} 1078 1079// Attaches expand/collapse handlers to section headers. 1080function attachCategoryVisibilityTogglers() {
1081 $('#FormCenterContent .contentMain').click(function (event) { 1082 var target = event.target; 1083 1084 if (/^lnkLess[0-9]*$/.test(target.id)) { 1085 // Form details should shrink. 1086 lessInfo(parseInt(target.id.substr(7), 10)); 1087 event.preventDefault(); 1088 } 1089 else if (/^lnkMore[0-9]*$/.test(target.id)) { 1090 // Form details should expand. 1091 moreInfo(parseInt(target.id.substr(7), 10)); 1092 event.preventDefault(); 1093 } 1094 }); 1095} 1096 1097// Called by form submittal to handle radio buttons for serialization. 1098function submitRadioHandler() { 1099 var names = []; 1100 var values = {}; 1101 $checkBoxSelector = null; 1102 $webCheckBox = $('div.formWrap ol.cpForm input[type=radio]'); 1103 $mobileCheckBox = $('.mobileGuts input[type=radio]'); 1104 1105 if ($webCheckBox.length != 0 && $mobileCheckBox.length == 0) 1106 $checkBoxSelector = $webCheckBox; 1107 1108 if ($webCheckBox.length == 0 && $mobileCheckBox.length != 0) 1109 $checkBoxSelector = $mobileCheckBox; 1110 if ($checkBoxSelector != null) { 1111 // Get list of radio inputs and their values. 1112 $checkBoxSelector.each(function () { 1113 if (!names.contains(this.name)) { 1114 names.push(this.name); 1115 values[this.name] = ''; 1116 } 1117 1118 if (this.checked) 1119 values[this.name] = this.value; 1120 }) 1121 } 1122 1123 // Map collected data to hidden inputs submitted to server. 1124 for (var i = 0, len = names.length; i < len; i++) { 1125 var inputName = names[i]; 1126 var id = (inputName.match(/_/g) || []).length == 2 ? inputName.substring(inputName.indexOf('_') + 1) : inputName; 1127 document.getElementById(id).value = values[inputName]; 1128 } 1129} 1130 1131// Called by form submittal to handle checkboxes for serialization. 1132function submitCheckboxHandler() { 1133 var names = []; 1134 var values = {}; 1135 $checkBoxSelector = null; 1136 $webCheckBox = $('div.formWrap ol.cpForm input[type=checkbox]'); 1137 $mobileCheckBox = $('.mobileGuts input[type=checkbox]'); 1138 1139 if ($webCheckBox.length != 0 && $mobileCheckBox.length == 0) 1140 $checkBoxSelector = $webCheckBox; 1141 1142 if ($webCheckBox.length == 0 && $mobileCheckBox.length != 0) 1143 $checkBoxSelector = $mobileCheckBox; 1144 1145 // Get list of checkbox inputs and their values. 1146 if ($checkBoxSelector != null) { 1147 $checkBoxSelector.each(function () { 1148 if (!names.contains(this.name)) { 1149 names.push(this.name); 1150 values[this.name] = []; 1151 } 1152 1153 if (this.checked) { 1154 var val = this.value.replace(/target="__blank"/g, 'target="_blank"'); 1155 val = val.replace(/\,/g, '_comma_'); 1156 if (val.trim() == '') 1157 val = '_empty_value_'; 1158 values[this.name].push(val); 1159 1160 } 1161 }); 1162 } 1163 1164 // Map collected data to hidden inputs submitted to server. 1165 for (var i = 0, len = names.length; i < len; i++) 1166 document.getElementById(names[i]).value = values[names[i]].join(','); 1167} 1168 1169function submitDateTimeHandler() { 1170 var names = []; 1171 var values = []; 1172 1173 var $datePickers, $timePickers; 1174 if ($('.formCenterDatePicker').length > 0 || $('.formCenterTimePicker').length > 0) { 1175 $datePickers = $('.formCenterDatePicker'); 1176 $timePickers = $('.formCenterTimePicker'); 1177 } 1178 else { 1179 $datePickers = $('.telerikDatePicker'); 1180 $timePickers = $('.telerikTimePicker'); 1181 } 1182 1183 $datePickers.each(function () { 1184 if (!names.contains(this.name)) { 1185 names.push(this.name); 1186 values[this.name] = [this.value]; 1187 } 1188 else { 1189 values[this.name].push(this.value); 1190 } 1191 }); 1192 1193 $timePickers.each(function () { 1194 if (!names.contains(this.name)) { 1195 names.push(this.name); 1196 values[this.name] = ['', '', this.value]; 1197 } 1198 else { 1199 if (values[this.name].length == 1) { 1200 values[this.name].push(''); 1201 } 1202 values[this.name].push(this.value); 1203 } 1204 }); 1205 1206 for (var i = 0, len = names.length; i < len; i++) 1207 document.getElementById(names[i]).value = values[names[i]].join(); 1208} 1209 1210$(function () { 1211 $('#aspnetForm,form[name=aspnetForm]').attr('enctype', "multipart/form-data"); 1212 $('#aspnetForm,form[name=aspnetForm]').addClass('submitForm'); 1213}); 1214; 1215 1216/// <reference path="../../../../Scripts/json2.js" /> 1217/// <reference path="../../../../Scripts/HtmlExtensionSupport.js" /> 1218/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" /> 1219/// <reference path="../../../../Assets/Scripts/InappropriateWords.js" /> 1220/// <reference path="Home.js" /> 1221 1222// Overridden on a per-form basis. Retrieves validator information for submission validation. 1223//function getFormValidatorData() { 1224// return []; 1225//} 1226 1227var FormCenterHomeScriptResources = GetJson("/FormCenter/Localization"); 1228var originalTextColorRadio; 1229var originalTextColorCheckBox; 1230 1231$.getScript("/Assets/Scripts/InappropriateWords.js"); 1232 1233// Used to properly localize strings. 1234String.prototype.format = function () { 1235 var args = arguments; 1236 return this.replace(/{(\d+)}/g, function (match, number) { 1237 return typeof args[number] != 'undefined' 1238 ? args[number] 1239 : match 1240 ; 1241 }); 1242}; 1243 1244 1245// Performs validation for a form field. 1246function FieldValidator(elemInput, id, fieldType, isInternal, isRequired, formatValidation, formatValidationCustom, minA
1246nswer, maxAnswer, maxLength) { 1247 var self = this; 1248 this.elemInputIsArray = $.isArray(elemInput); 1249 1250 if (this.elemInputIsArray) { 1251 for (var i = 0, len = elemInput.length; i < len; i++) { 1252 if (typeof (elemInput[i]) == "string") 1253 elemInput[i] = document.getElementById(elemInput[i]); 1254 } 1255 1256 if (elemInput.length > 0 && elemInput[0]) { 1257 this.elemContainer = elemInput[0].parentNode.parentNode.parentNode.parentNode; 1258 this.elemInput = elemInput; 1259 } 1260 } 1261 else { 1262 if (typeof (elemInput) == "string") 1263 elemInput = document.getElementById(elemInput); 1264 1265 if (elemInput) { 1266 if (fieldType === FieldTypes.DatePicker) 1267 this.elemContainer = elemInput.parentNode.parentNode.parentNode.parentNode; 1268 else 1269 this.elemContainer = elemInput.parentNode.parentNode; 1270 this.elemInput = elemInput; 1271 } 1272 } 1273 1274 this.enabled = true; 1275 this.$elemContainer = $(this.elemContainer); 1276 this.fieldType = fieldType; 1277 if (this.fieldType === FieldTypes.FileUpload) { 1278 //When validating <input type="file" />, we need to update the elemInput reference since it is created a new one when failing inside validateFileUpload method - LC 1279 var validateInputFileUpload = function () { 1280 var $this = $(this); 1281 if (!validateMultiFileUpload($this[0], true)) { 1282 alert("The file you are trying to upload is not an allowed file type or has an invalid file name. These are the accepted characters: A-z 0-9 ~ ! ( ) - + = [ ] { } , . _"); 1283 self.elemInput.value = null; 1284 self.elemInput.focus(); 1285 } 1286 }; 1287 $(this.elemInput).attr("change", "").removeAttr("onChange").unbind("change").change(validateInputFileUpload); 1288 } 1289 this.id = id; 1290 this.isInternal = isInternal; 1291 this.isRequired = isRequired; 1292 this.formatValidation = formatValidation; 1293 this.formatValidationCustom = formatValidationCustom; 1294 this.elemErrorNote = null; 1295 this.minAnswer = minAnswer; 1296 this.maxAnswer = maxAnswer; 1297 this.maxLength = maxLength; 1298 this.focusElementIndex = 0; 1299 this.alreadySetFocus = false; 1300 var isRequiredInfoLableDisplayed = false; 1301 if (this.isRequired && !isRequiredInfoLableDisplayed) { 1302 $('#liRequiredFieldInfo').show(); 1303 isRequiredInfoLableDisplayed = true; 1304 } 1305} 1306 1307// Static. Creates a single validation object from an options object. 1308FieldValidator.createSingle = function (options) { 1309 return new FieldValidator( 1310 options.elemInput, 1311 options.id, 1312 options.fieldType, 1313 options.isInternal, 1314 options.isRequired, 1315 options.formatValidation, 1316 options.formatValidationCustom, 1317 options.minAnswer, 1318 options.maxAnswer, 1319 options.maxLength); 1320}; 1321 1322// Static. Creates array of validation objects from array of option objects. 1323FieldValidator.createMulti = function (optionsArray) { 1324 var key = null; 1325 1326 var ret = { 1327 keys: [], 1328 add: function (validator) { 1329 var key = validator.id; 1330 this[key] = validator; 1331 this.keys.push(key); 1332 } 1333 }; 1334 1335 for (var i = 0, len = optionsArray.length; i < len; i++) 1336 ret.add(FieldValidator.createSingle(optionsArray[i])); 1337 1338 return ret; 1339}; 1340 1341// Internal. Used by validation() to ensure the input does not exceed the max length. 1342FieldValidator.prototype.validateMaxLength = function (errorMsg) { 1343 var ERROR_MSG = FormCenterHomeScriptResources.MaxLengthErrorMessage.format(this.maxLength); 1344 if (this.maxLength == null) 1345 return true; 1346 var cleanValue = this.elemInput.value.trim(); 1347 if (cleanValue != null && cleanValue.length > this.maxLength) { 1348 errorMsg.push(ERROR_MSG); 1349 return false; 1350 } 1351 return true; 1352}; 1353 1354// Internal. Used by validate() to ensure user-enterable fields are phone numbers (of NANP format). 1355FieldValidator.prototype.validatePhone = function (errorMsg) { 1356 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidPhoneNumberFormat); 1357 var origLen = errorMsg.length; 1358 1359 switch (this.fieldType) { 1360 case FieldTypes.ShortAnswer: 1361 case FieldTypes.LongAnswer: 1362 case FieldTypes.Password: 1363 var cleanValue = this.elemInput.value.trim(); 1364 if (cleanValue != '' && !phoneValidate(cleanValue, intCountryCode)) 1365 errorMsg.push(ERROR_MSG); 1366 break; 1367 } 1368
1369 // Return false if validation check failed (number of errors changed). 1370 return origLen == errorMsg.length; 1371} 1372 1373// Internal. Used by validate() to ensure user-enterable fields are postal codes. 1374FieldValidator.prototype.validatePostal = function (errorMsg) { 1375 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidPostalCode); 1376 var origLen = errorMsg.length; 1377 1378 switch (this.fieldType) { 1379 case FieldTypes.ShortAnswer: 1380 case FieldTypes.LongAnswer: 1381 case FieldTypes.Password: 1382 var cleanValue = this.elemInput.value.trim(); 1383 1384 if (cleanValue != '' && !isZipCode(cleanValue, intCountryCode)) 1385 errorMsg.push(ERROR_MSG); 1386 1387 break; 1388 } 1389 1390 // Return false if validation check failed (number of errors changed). 1391 return origLen == errorMsg.length; 1392} 1393 1394// Internal. Used by validate() to ensure user-enterable fields are email addresses. 1395FieldValidator.prototype.validateEmail = function (errorMsg) { 1396 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidEmailAddressFormat2); 1397 var origLen = errorMsg.length; 1398 1399 switch (this.fieldType) { 1400 case FieldTypes.ShortAnswer: 1401 case FieldTypes.LongAnswer: 1402 case FieldTypes.Password: 1403 case FieldTypes.ReplyEmail: 1404 var cleanValue = this.elemInput.value.trim(); 1405 1406 if (cleanValue != '' && !emailValidate(cleanValue)) 1407 errorMsg.push(ERROR_MSG); 1408 1409 break; 1410 } 1411 1412 // Return false if validation check failed (number of errors changed). 1413 return origLen == errorMsg.length; 1414} 1415function getNumericThousandsSeparator() { 1416 var dummy = new Number(11111111); 1417 var thousandSep = dummy.toLocaleString().replace(/1/g, ''); 1418 return (thousandSep.length > 0 ? thousandSep.charAt(0) : ','); 1419} 1420// Internal. Used by validate() to ensure user-enterable fields match custom expression. 1421FieldValidator.prototype.validateCustom = function (expr, errorMsg) { 1422 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidGenericField); 1423 var origLen = errorMsg.length; 1424 1425 var customRegExp = new RegExp(expr); 1426 1427 switch (this.fieldType) { 1428 case FieldTypes.ShortAnswer: 1429 case FieldTypes.LongAnswer: 1430 case FieldTypes.Password: 1431 var cleanValue = this.elemInput.value.trim(); 1432 1433 if (cleanValue != '' && (!customRegExp.test(cleanValue))) 1434 errorMsg.push(ERROR_MSG); 1435 1436 break; 1437 } 1438 1439 // Return false if validation check failed (number of errors changed). 1440 return origLen == errorMsg.length; 1441} 1442 1443// Internal. Used by validate() to ensure user-enterable fields are letters only. 1444FieldValidator.prototype.validateLetters = function (errorMsg) { 1445 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.LettersOnlyField); 1446 var origLen = errorMsg.length; 1447 1448 var letterRegExp = /^[a-zA-Z ]+$/; 1449 1450 switch (this.fieldType) { 1451 case FieldTypes.ShortAnswer: 1452 case FieldTypes.LongAnswer: 1453 case FieldTypes.Password: 1454 var cleanValue = this.elemInput.value.trim(); 1455 1456 if (cleanValue != '' && (!letterRegExp.test(cleanValue))) 1457 errorMsg.push(ERROR_MSG); 1458 1459 break; 1460 } 1461 1462 // Return false if validation check failed (number of errors changed). 1463 return origLen == errorMsg.length; 1464} 1465 1466// Internal. Used by validate() to ensure user-enterable fields are numeric (numbers, decimal separators, and group separators allowed). 1467FieldValidator.prototype.validateNumeric = function (errorMsg) { 1468 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.NumericOnlyField); 1469 var origLen = errorMsg.length; 1470 1471 var numericRegExp = new RegExp("^(([0-9]+?[0-9%G]+?[0-9](|%D[0-9]+))|([0-9]*(|%D[0-9]+)))$" 1472 .replace(/%G/g, RegExp.metaEscape(getNumericThousandsSeparator())) 1473 .replace(/%D/g, RegExp.metaEscape(getNumericDecimalSeparator()))); 1474 1475 switch (this.fieldType) { 1476 case FieldTypes.ShortAnswer: 1477 case FieldTypes.LongAnswer: 1478 case FieldTypes.Password: 1479 var cleanValue = this.elemInput.value.trim(); 1480 1481 if (cleanValue != '' && (!numericRegExp.test(cleanValue))) 1482 errorMsg.push(ERROR_MSG); 1483 1484 break; 1485 } 1486 1487 // Return false if validation check failed (number of errors changed). 1488 return origLen == errorMsg.length; 1489} 1490 1491FieldValidator.prototype.validateCurrency = function (errorMsg) { 1492 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.CurrencyOnlyField); 1493 var origLen = errorMsg.length; 1494 1495 var value = this.elemInput.value.trim(); 1496 1497 if (!this.isValidMoney(value)) { 1498 errorMsg.push(ERROR_MSG);; 1499 } 1500
1501 // Return false if validation check failed (nubmer of errors changed). 1502 return origLen == errorMsg.length; 1503} 1504 1505FieldValidator.prototype.isValidMoney = function (value) { 1506 if (value && value != '') 1507 return /^\$?\s*\d+(,\d{3})*(\.\d{1,2})?$/.test(value); 1508 else 1509 return true; 1510}; 1511 1512//#region Date Validation 1513 1514FieldValidator.prototype.validateDate = function (errorMsg) { 1515 var origLen = errorMsg.length; 1516 1517 var date = $(this.elemInput[0]).val(); 1518 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidDate); 1519 1520 if (!this.isValidDate(date, this.isRequired)) { 1521 errorMsg.push(ERROR_MSG); 1522 } 1523 1524 // Return false if validation check failed (nubmer of errors changed). 1525 return origLen == errorMsg.length; 1526} 1527 1528FieldValidator.prototype.validateTime = function (errorMsg) { 1529 var origLen = errorMsg.length; 1530 1531 var time = $(this.elemInput[0]).val(); 1532 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidTime); 1533 1534 if (!this.isValidTime(time, this.isRequired)) { 1535 errorMsg.push(ERROR_MSG); 1536 } 1537 1538 // Return false if validation check failed (nubmer of errors changed). 1539 return origLen == errorMsg.length; 1540} 1541 1542FieldValidator.prototype.validateDateTime = function (errorMsg) { 1543 var origLen = errorMsg.length; 1544 1545 var date = $(this.elemInput[0]).val(); 1546 1547 var time = $(this.elemInput[1]).val(); 1548 1549 var validDate = this.isValidDate(date, this.isRequired); 1550 1551 var validTime = this.isValidTime(time, this.isRequired); 1552 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidDateTime); 1553 1554 if (!validDate && !validTime) { 1555 if (!this.alreadySetFocus) { 1556 this.focusElementIndex = 0; 1557 this.alreadySetFocus = true; 1558 } 1559 errorMsg.push(ERROR_MSG); 1560 } 1561 else if (!validDate) { 1562 if (!this.alreadySetFocus) { 1563 this.focusElementIndex = 0; 1564 this.alreadySetFocus = true; 1565 } 1566 errorMsg.push(ERROR_MSG); 1567 } 1568 else if (!validTime) { 1569 if (!this.alreadySetFocus) { 1570 this.focusElementIndex = 1; 1571 this.alreadySetFocus = true; 1572 } 1573 errorMsg.push(ERROR_MSG); 1574 } 1575 1576 // Return false if validation check failed (nubmer of errors changed). 1577 return origLen == errorMsg.length; 1578} 1579 1580FieldValidator.prototype.validateDateSpan = function (errorMsg) { 1581 var origLen = errorMsg.length; 1582 1583 var date = $(this.elemInput[0]).val(); 1584 1585 var dateSpan = $(this.elemInput[1]).val(); 1586 1587 if (!this.isValidDate(date, this.isRequired)) { 1588 if (!this.alreadySetFocus) { 1589 this.focusElementIndex = 0; 1590 this.alreadySetFocus = true; 1591 } 1592 errorMsg.push(FormCenterHomeScriptResources.InvalidStartDate); 1593 } 1594 if (!this.isValidDate(dateSpan, this.isRequired)) { 1595 if (!this.alreadySetFocus) { 1596 this.focusElementIndex = 1; 1597 this.alreadySetFocus = true; 1598 } 1599 errorMsg.push(FormCenterHomeScriptResources.InvalidEndDate); 1600 } 1601 if (!this.isValidDateRange(date, dateSpan, this.isRequired, this.isRequired)) { 1602 if (!this.alreadySetFocus) { 1603 this.focusElementIndex = 0; 1604 this.alreadySetFocus = true; 1605 } 1606 errorMsg.push(FormCenterHomeScriptResources.InvalidDateSpan); 1607 } 1608 1609 // Return false if validation check failed (nubmer of errors changed). 1610 return origLen == errorMsg.length; 1611} 1612 1613FieldValidator.prototype.validateTimeSpan = function (errorMsg) { 1614 var origLen = errorMsg.length; 1615 1616 var time = $(this.elemInput[0]).val(); 1617 1618 var timeSpan = $(this.elemInput[1]).val(); 1619 1620 if (!this.isValidTime(time, this.isRequired)) { 1621 if (!this.alreadySetFocus) { 1622 this.focusElementIndex = 0; 1623 this.alreadySetFocus = true; 1624 } 1625 errorMsg.push(FormCenterHomeScriptResources.InvalidStartTime); 1626 } 1627 1628 if (!this.isValidTime(timeSpan, this.isRequired)) { 1629 if (!this.alreadySetFocus) { 1630 this.focusElementIndex = 1; 1631 this.alreadySetFocus = true; 1632 } 1633 errorMsg.push(FormCenterHomeScriptResources.InvalidEndTime); 1634 } 1635 1636 if (!this.isValidTimeRange(time, timeSpan, this.isRequired, this.isRequired)) { 1637 if (!this.alreadySetFocus) { 1638 this.focusElementIndex = 0; 1639 this.alreadySetFocus = true; 1640 } 1641 errorMsg.push(FormCenterHomeScriptResources.InvalidTimeSpan); 1642 } 1643
1644 // Return false if validation check failed (nubmer of errors changed). 1645 return origLen == errorMsg.length; 1646} 1647 1648FieldValidator.prototype.validateDateTimeSpan = function (errorMsg) { 1649 var origLen = errorMsg.length; 1650 1651 var date = $(this.elemInput[0]).val(); 1652 1653 var dateSpan = $(this.elemInput[1]).val(); 1654 1655 var time = $(this.elemInput[2]).val(); 1656 1657 var timeSpan = $(this.elemInput[3]).val(); 1658 1659 var validDate = this.isValidDate(date, this.isRequired); 1660 1661 var validTime = this.isValidTime(time, this.isRequired); 1662 1663 if (!validDate && !validTime) { 1664 if (!this.alreadySetFocus) { 1665 this.focusElementIndex = 0; 1666 this.alreadySetFocus = true; 1667 } 1668 errorMsg.push(FormCenterHomeScriptResources.InvalidStartDateTime); 1669 } 1670 else if (!validDate) { 1671 if (!this.alreadySetFocus) { 1672 this.focusElementIndex = 0; 1673 this.alreadySetFocus = true; 1674 } 1675 errorMsg.push(FormCenterHomeScriptResources.InvalidStartDate); 1676 } 1677 else if (!validTime) { 1678 if (!this.alreadySetFocus) { 1679 this.focusElementIndex = 2; 1680 this.alreadySetFocus = true; 1681 } 1682 errorMsg.push(FormCenterHomeScriptResources.InvalidStartTime); 1683 } 1684 1685 var validSpanDate = this.isValidDate(dateSpan, this.isRequired); 1686 1687 var validSpanTime = this.isValidTime(timeSpan, this.isRequired); 1688 1689 if (!validSpanDate && !validSpanTime) { 1690 if (!this.alreadySetFocus) { 1691 this.focusElementIndex = 1; 1692 this.alreadySetFocus = true; 1693 } 1694 errorMsg.push(FormCenterHomeScriptResources.InvalidEndDateTime); 1695 } 1696 else if (!validSpanDate) { 1697 if (!this.alreadySetFocus) { 1698 this.focusElementIndex = 1; 1699 this.alreadySetFocus = true; 1700 } 1701 errorMsg.push(FormCenterHomeScriptResources.InvalidEndDate); 1702 } 1703 else if (!validSpanTime) { 1704 if (!this.alreadySetFocus) { 1705 this.focusElementIndex = 3; 1706 this.alreadySetFocus = true; 1707 } 1708 errorMsg.push(FormCenterHomeScriptResources.InvalidEndTime); 1709 } 1710 var isDayBeforeMonth = getDateFormat().toLocaleLowerCase() == "dd/mm/yyyy" ? true : false; 1711 if (!this.isValidDateTimeRange(getDateFromInput(this.elemInput[0], isDayBeforeMonth), getDateFromInput(this.elemInput[1], isDayBeforeMonth), this.isRequired, this.isRequired, time, timeSpan, this.isRequired, this.isRequired)) { 1712 if (!this.alreadySetFocus) { 1713 this.focusElementIndex = 1; 1714 this.alreadySetFocus = true; 1715 } 1716 errorMsg.push(FormCenterHomeScriptResources.InvalidDateTimeSpan); 1717 } 1718 1719 // Return false if validation check failed (nubmer of errors changed). 1720 return origLen == errorMsg.length; 1721} 1722 1723FieldValidator.prototype.isValidDate = function (date, dateRequired) { 1724 var objDate = new dateValidator(); 1725 1726 return objDate.dateValidateNew(date, dateRequired, ''); 1727} 1728 1729FieldValidator.prototype.isValidTime = function (time, timeRequired) { 1730 var objDate = new dateValidator(); 1731 1732 var amIndex = time.toLowerCase().indexOf('am'); 1733 var pmIndex = time.toLowerCase().indexOf('pm'); 1734 1735 if (amIndex != -1) { 1736 time = time.substring(0, amIndex).replace(" ", ""); 1737 1738 if (time.trim() === "") { 1739 return false; 1740 } 1741 } 1742 else if (pmIndex != -1) { 1743 time = time.substring(0, pmIndex).replace(" ", ""); 1744 1745 if (time.trim() === "") { 1746 return false; 1747 } 1748 } 1749 1750 return objDate.timeValidate(time, timeRequired, ''); 1751} 1752 1753FieldValidator.prototype.isValidTimeRange = function (startTime, endTime, startTimeRequired, endTimeRequired) { 1754 var objDate = new dateValidator(); 1755 1756 objDate.ysnAllowTimeOnly = true; 1757 objDate.dtiStartTime = this.stripAMPM(startTime); 1758 objDate.ysnStartTimeRequired = startTimeRequired; 1759 objDate.dtiEndTime = this.stripAMPM(endTime); 1760 objDate.ysnEndTimeRequired = endTimeRequired; 1761 objDate.ysnDatesAreEqual = true; 1762 objDate.strStartAMPM = this.getAMPMString(startTime); 1763 objDate.strEndAMPM = this.getAMPMString(endTime); 1764 objDate.timesAlreadyValidated = true; 1765 1766 return objDate.timeOrderValidate(); 1767 1768} 1769 1770FieldValidator.prototype.isValidDateRange = function (startDate, endDate, startDateRequired, endDateRequired) { 1771 var objDate = new dateValidator(); 1772 objDate.setStartDate(checkDateFormatReturnUSString(startDate, false)); 1773 objDate.setEndDate(checkDateFormatReturnUSString(endDate, false)); 1774 objDate.setStartDateRequired(startDateRequired); 1775 objDate.setEndDateRequired(endDateRequired); 1776 objDate.datesAlreadyValidated = true; 1777 1778 return objDate.dateOrderValidateNew(); 1779} 1780 1781FieldValidator.prototype.isValidDateTimeRange = function (startDate, endDate, startDateRequired, endDateRequired, startTime, endTime, startTimeRequired, endTimeRequired) { 1782 var objDate = new dateValidator(); 1783 1784 1785 objDate.setStartDate(checkDateFormatReturnUSString(startDate, false)); 1786 objDate.setEndDate(checkDateFormatReturnUSString(endDate, false)); 1787 objDate.setStartDateRequired(startDateRequired); 1788 objDate.setEndDateRequired(endDateRequired); 1789 objDate.ysnAllowEqualDates = true; 1790 objDate.datesAlreadyValidated = true; 1791 1792 var validDateOrder = objDate.dateOrderValidateNew(); 1793 1794 objDate.strStartAMPM = this.getAMPMString(startTime); 1795 objDate.strEndAMPM = this.getAMPMString(endTime);
1796 objDate.ysnAllowTimeOnly = true; 1797 objDate.ysnAllowEqualTimes = !objDate.ysnDatesAreEqual; 1798 objDate.dtiStartTime = this.stripAMPM(startTime); 1799 objDate.ysnStartTimeRequired = startTimeRequired; 1800 objDate.dtiEndTime = this.stripAMPM(endTime); 1801 objDate.ysnEndTimeRequired = endTimeRequired; 1802 objDate.timesAlreadyValidated = true; 1803 1804 var validTimeOrder = objDate.timeOrderValidate(); 1805 1806 return validDateOrder && validTimeOrder; 1807} 1808 1809FieldValidator.prototype.stripAMPM = function (time) { 1810 var amIndex = time.toLowerCase().indexOf('am'); 1811 var pmIndex = time.toLowerCase().indexOf('pm'); 1812 1813 if (amIndex != -1) { 1814 time = time.substring(0, amIndex).replace(" ", ""); 1815 } 1816 else if (pmIndex != -1) { 1817 time = time.substring(0, pmIndex).replace(" ", ""); 1818 } 1819 1820 return time; 1821} 1822 1823FieldValidator.prototype.getAMPMString = function (time) { 1824 var amIndex = time.toLowerCase().indexOf('am'); 1825 var pmIndex = time.toLowerCase().indexOf('pm'); 1826 1827 var AMPM = ''; 1828 1829 if (amIndex != -1) { 1830 AMPM = time.substring(amIndex).replace(" ", ""); 1831 } 1832 else if (pmIndex != -1) { 1833 AMPM = time.substring(pmIndex).replace(" ", ""); 1834 } 1835 return AMPM; 1836} 1837 1838//#endregion Date Validation 1839 1840//checkbox validation done for min and max answer selcted 1841FieldValidator.prototype.checkBoxValidation = function (errorMsg) { 1842 var origLen = errorMsg.length; 1843 if (this.fieldType == FieldTypes.Checkboxes && ((this.minAnswer != "" && this.minAnswer != undefined) || (this.maxAnswer != "" && this.maxAnswer != undefined))) { 1844 var i = 0, len = this.elemInput.length; 1845 var checkedCount = 0; 1846 for (; i < len; i++) { 1847 if (this.elemInput[i].checked) 1848 checkedCount++; 1849 } 1850 //validation for checkbox min max fields 1851 if (this.minAnswer != "" && this.minAnswer != undefined) { 1852 if (checkedCount < parseInt(this.minAnswer, 10)) // the message is not localized on purpose as this has text and dynamic value 1853 errorMsg.push("This field must have at least " + parseInt(this.minAnswer, 10) + " options selected"); 1854 } 1855 1856 if (origLen == errorMsg.length && this.maxAnswer != "" && this.maxAnswer != undefined) { 1857 if (checkedCount > parseInt(this.maxAnswer, 10)) // the message is not localized on purpose as this has text and dynamic value 1858 errorMsg.push("This field can have at most " + parseInt(this.maxAnswer, 10) + " options selected"); 1859 } 1860 } 1861 1862 // Return false if validation check failed (number of errors changed). 1863 return origLen == errorMsg.length; 1864}; 1865 1866// Internal. Used by validate() to ensure required fields are set. 1867FieldValidator.prototype.validateRequired = function (errorMsg) { 1868 var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.RequiredField);//FormCenterHomeScriptResources.RequiredField; 1869 var origLen = errorMsg.length; 1870 switch (this.fieldType) { 1871 case FieldTypes.ShortAnswer: 1872 case FieldTypes.LongAnswer: 1873 case FieldTypes.ReplyEmail: 1874 case FieldTypes.Password: 1875 case FieldTypes.FileUpload: 1876 case FieldTypes.CodeSnippet: 1877 case FieldTypes.InputLink: 1878 case FieldTypes.DatePicker: 1879 case FieldTypes.InputImage: 1880 if (this.elemInput != 'undefined' && this.elemInput.value != undefined) { 1881 if (this.elemInput.value.trim() == '') 1882 errorMsg.push(ERROR_MSG); 1883 } 1884 break; 1885 case FieldTypes.Checkboxes: 1886 case FieldTypes.RadioButtons: 1887 var i = 0, len = this.elemInput.length; 1888 1889 for (; i < len; i++) { 1890 if (this.elemInput[i].checked) 1891 break; 1892 } 1893 1894 if (i == len) 1895 errorMsg.push(ERROR_MSG); 1896 1897 break; 1898 case FieldTypes.Dropdown: 1899 case FieldTypes.FormLink: 1900 if (this.elemInput.selectedIndex <= 0) 1901 errorMsg.push(ERROR_MSG); 1902 1903 break; 1904 case FieldTypes.DateTime: 1905 var i = 0, len = this.elemInput.length; 1906 1907 for (; i < len; i++) { 1908 if (this.elemInput[i].value.trim() == '') { 1909 errorMsg.push(ERROR_MSG); 1910 this.focusElementIndex = i; 1911 break; 1912 } 1913 } 1914 } 1915 // Return false if validation check failed (number of errors changed). 1916 return origLen == errorMsg.length; 1917}; 1918 1919// Performs validation for field and updates format if necessary. 1920FieldValidator.prototype.validate = function (assignFocusOnFailure, checkForInappropriateWords) { 1921 var errorMsg = []; 1922 1923 // Element not present OR disabled, pass (skipped). 1924 if (this.elemInput == null || !this.enabled) 1925 return true; 1926 1927 if (!this.isRequired || (this.isRequired && this.validateRequired(errorMsg))) { 1928 //checkbox validation done for min and max answer selected 1929 if (this.checkBoxValidation(errorMsg)) { 1930 switch (this.formatValidation) { 1931 case FieldValidation.Letters: 1932 this.validateLetters(errorMsg); 1933 break; 1934 case FieldValidation.Numeric: 1935 this.validateNumeric(errorMsg); 1936 break; 1937 case FieldValidation.Currency: 1938 this.validateCurrency(errorMsg); 1939 break; 1940 case FieldValidation.PhoneNumber: 1941 this.validatePhone(errorMsg); 1942 break; 1943 case FieldValidation.PostalCode: 1944 this.validatePostal(errorMsg); 1945 break; 1946 case FieldValidation.EmailAddress: 1947 this.validateEmail(errorMsg); 1948 break; 1949 case FieldValidation.RegEx: 1950 this.validateCustom(this.formatValidationCustom, errorMsg); 1951 break; 1952 case FieldValidation.Date: 1953 this.validateDate(errorMsg); 1954 break; 1955 case FieldValidation.Time: 1956 this.validateTime(errorMsg); 1957 break; 1958 case FieldValidation.DateTime: 1959 this.validateDateTime(errorMsg); 1960 break; 1961 case FieldValidation.DateSpan: 1962 this.validateDateSpan(errorMsg); 1963 break; 1964 case FieldValidation.TimeSpan: 1965 this.validateTimeSpan(errorMsg); 1966 break; 1967 case FieldValidation.DateTimeSpan: 1968 this.validateDateTimeSpan(errorMsg); 1969 break; 1970 } 1971 } 1972 } 1973 if (this.fieldType === FieldTypes.DatePicker && this.elemInput.value.trim() != '') { 1974 //validate date picker value (based on telerik mvc datePicker) 1975 var datepicker = $(this.elemInput).data("tDatePicker"); 1976 if ($(this.elemInput).hasClass('t-state-error') || datepicker.parse(this.elemInput.value.trim()) == null) 1977 errorMsg.push("Invalid date format"); 1978 } 1979 if (this.fieldType === FieldTypes.ShortAnswer || this.fieldType === FieldTypes.LongAnswer) {
1980 var validator = this; 1981 1982 if (checkForInappropriateWords != null && typeof (checkForInappropriateWords.push) == 'function') { 1983 checkForInappropriateWords.push(validator); 1984 } else { 1985 if (HasInappropriateWords(this.elemInput.value.trim())) { 1986 $(this.elemInput).addClass('t-state-error'); 1987 errorMsg.push(FormCenterHomeScriptResources.InappropriateWords); 1988 } 1989 } 1990 } 1991 1992 if (FeatureToggles.isActive("CMS.FormCenter.EnableServerSideValidationsForSubmissions") 1993 && this.fieldType === FieldTypes.FileUpload 1994 && this.elemInput.value.trim() != '') { 1995 if (Math.round((this.elemInput.files[0].size / 1024)) > 20480) 1996 errorMsg.push("Files cannot be larger than 20 MB."); 1997 } 1998 1999 this.validateMaxLength(errorMsg); 2000 2001 if (errorMsg.length > 0 && $(this.elemInput).is(":visible")) { 2002 this.errorShow(errorMsg); 2003 2004 if (assignFocusOnFailure) { 2005 this.focusErrorElement(); 2006 } 2007 2008 return false; 2009 } else { 2010 this.errorClear(); 2011 return true; 2012 } 2013 2014}; 2015 2016// Performs validation for admin edit internal only fields and updates format if necessary. 2017FieldValidator.prototype.validateInternal = function (assignFocusOnFailure, checkForInappropriateWords) { 2018 if (this.isInternal) { 2019 var errorMsg = []; 2020 2021 // Element not present OR disabled, pass (skipped). 2022 if (this.elemInput == null || !this.enabled) 2023 return true; 2024 2025 if (!this.isRequired || (this.isRequired && this.validateRequired(errorMsg))) { 2026 //checkbox validation done for min and max answer selected 2027 if (this.checkBoxValidation(errorMsg)) { 2028 switch (this.formatValidation) { 2029 case FieldValidation.Letters: 2030 this.validateLetters(errorMsg); 2031 break; 2032 case FieldValidation.Numeric: 2033 this.validateNumeric(errorMsg); 2034 break; 2035 case FieldValidation.Currency: 2036 this.validateCurrency(errorMsg); 2037 break; 2038 case FieldValidation.PhoneNumber: 2039 this.validatePhone(errorMsg); 2040 break; 2041 case FieldValidation.PostalCode: 2042 this.validatePostal(errorMsg); 2043 break; 2044 case FieldValidation.EmailAddress: 2045 this.validateEmail(errorMsg); 2046 break; 2047 case FieldValidation.RegEx: 2048 this.validateCustom(this.formatValidationCustom, errorMsg); 2049 break; 2050 case FieldValidation.Date: 2051 this.validateDate(errorMsg); 2052 break; 2053 case FieldValidation.Time: 2054 this.validateTime(errorMsg); 2055 break; 2056 case FieldValidation.DateTime: 2057 this.validateDateTime(errorMsg); 2058 break; 2059 case FieldValidation.DateSpan: 2060 this.validateDateSpan(errorMsg); 2061 break; 2062 case FieldValidation.TimeSpan: 2063 this.validateTimeSpan(errorMsg); 2064 break; 2065 case FieldValidation.DateTimeSpan: 2066 this.validateDateTimeSpan(errorMsg); 2067 break; 2068 } 2069 } 2070 } 2071 if (this.fieldType === FieldTypes.DatePicker && this.elemInput.value.trim() != '') { 2072 //validate date picker value (based on telerik mvc datePicker) 2073 var datepicker = $(this.elemInput).data("tDatePicker"); 2074 if ($(this.elemInput).hasClass('t-state-error') || datepicker.parse(this.elemInput.value.trim()) == null) 2075 errorMsg.push("Invalid date format"); 2076 } 2077 if (this.fieldType === FieldTypes.ShortAnswer || this.fieldType === FieldTypes.LongAnswer) { 2078 var validator = this; 2079 2080 if (checkForInappropriateWords != null && typeof (checkForInappropriateWords.push) == 'function') { 2081 checkForInappropriateWords.push(validator); 2082 } else { 2083 if (HasInappropriateWords(this.elemInput.value.trim())) { 2084 $(this.elemInput).addClass('t-state-error'); 2085 errorMsg.push(FormCenterHomeScriptResources.InappropriateWords); 2086 } 2087 } 2088 } 2089 2090 this.validateMaxLength(errorMsg); 2091 2092 if (errorMsg.length > 0 && $(this.elemInput).is(":visible")) { 2093 this.errorShow(errorMsg); 2094 2095 if (assignFocusOnFailure) { 2096 this.focusErrorElement(); 2097 } 2098 2099 return false; 2100 } else { 2101 this.errorClear(); 2102 return true; 2103 } 2104 } 2105 else { 2106 return true; 2107 } 2108}; 2109 2110FieldValidator.prototype.handleInappropriateWords = function (focus) { 2111 var errorMsg = []; 2112 $(this.elemInput).addClass('t-state-error'); 2113 errorMsg.push(FormCenterHomeScriptResources.InappropriateWords); 2114 this.errorShow(errorMsg); 2115 if (focus) { 2116 this.focusErrorElement(); 2117 } 2118}; 2119
2120FieldValidator.prototype.focusErrorElement = function () { 2121 var scrollIntoView = function (element) { 2122 var offset = $(element).offset(); 2123 var elementTop = offset.top; 2124 // Add a 50px padding to the top of the element, just for aesthetics 2125 $(document).scrollTop(elementTop - 150); 2126 }; 2127 2128 if (this.elemInputIsArray) { 2129 if ($(this.elemInput).is(":visible")) { 2130 if (this.elemInput.length > this.focusElementIndex) { 2131 this.elemInput[this.focusElementIndex].focus(); 2132 } 2133 else { 2134 this.elemInput[0].focus(); 2135 } 2136 scrollIntoView(this.elemInput[0]); 2137 } 2138 this.alreadySetFocus = false; 2139 this.focusElementIndex = 0; 2140 } 2141 else { 2142 if ($(this.elemInput).is(":visible")) { 2143 this.elemInput.focus(); 2144 scrollIntoView(this.elemInput); 2145 } 2146 } 2147} 2148 2149// Shows error message for field. Called by validate() if field value fails validation. 2150FieldValidator.prototype.errorShow = function (messages) { 2151 if (this.elemErrorNote) 2152 this.errorClear(); 2153 2154 // Element not defined, cannot perform operation. 2155 if (this.elemInput == null) 2156 return; 2157 2158 this.$elemContainer.addClass('error'); 2159 this.elemErrorNote = []; 2160 2161 $('ol li.error div').addClass('selfClear'); 2162 2163 if (this.elemInput != undefined) { 2164 if (this.elemInput["0"] != undefined) { 2165 //Check current element is radio 2166 if (this.elemInput["0"].type == "radio" & !(this.$elemContainer.closest('li').hasClass('error'))) { 2167 this.$elemContainer.closest('li').addClass('error'); 2168 originalTextColorRadio = this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color; 2169 this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = "#800000"; 2170 } 2171 else //Check current element is checkbox 2172 if (this.elemInput["0"].type == "checkbox" & !(this.$elemContainer.closest('li').hasClass('error'))) { 2173 this.$elemContainer.closest('li').addClass('error'); 2174 originalTextColorCheckBox = this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color; 2175 this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = "#800000"; 2176 } 2177 } 2178 } 2179 2180 for (var i = 0, len = messages.length; i < len; i++) { 2181 var errorElemMsg = document.createElement('p'); 2182 2183 errorElemMsg.className = 'explanation'; 2184 2185 var multiSelectable = this.elemInput[0] !== undefined && this.elemInput.tagName !== "SELECT"; 2186 if (multiSelectable) { 2187 errorElemMsg.id = 'errorMsg' + this.elemInput[0].id; 2188 } 2189 else { 2190 errorElemMsg.id = 'errorMsg' + this.elemInput.id; 2191 } 2192 2193 errorElemMsg.innerHTML = messages[i]; 2194 2195 if (this.elemInput != undefined) { 2196 if (this.elemInput["0"] != undefined) { 2197 if (this.elemInput["0"].type == "radio" || this.elemInput["0"].type == "checkbox") { 2198 errorElemMsg.style.backgroundColor = "#940E0D" 2199 errorElemMsg.style.color = "#FFFFFF"; 2200 errorElemMsg.style.fontWeight = "bold" 2201 errorElemMsg.style.lineHeight = "1.2" 2202 errorElemMsg.style.width = "94%" 2203 errorElemMsg.style.margin = ".5em 0 0" 2204 errorElemMsg.style.padding = ".5em" 2205 } 2206 } 2207 } 2208 2209 this.elemContainer.appendChild(errorElemMsg); 2210 2211 this.elemErrorNote.push(errorElemMsg); 2212 } 2213 !window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights(); 2214}; 2215 2216// Clears error message for field. Called by validate() if field value passes validation. 2217FieldValidator.prototype.errorClear = function () { 2218 if (this.elemInput != undefined) { 2219 if (this.elemInput["0"] != undefined) { 2220 var i = 0, len = this.elemInput.length; 2221 for (; i < len; i++) { 2222 if (this.elemInput[i].checked) 2223 break; 2224 } 2225 2226 if (i != len) { 2227 if (this.elemInput["0"].type == "radio") { 2228 this.$elemContainer.closest('li').removeClass('error'); 2229 this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = originalTextColorRadio; 2230 } 2231 else if (this.elemInput["0"].type == "checkbox") { 2232 this.$elemContainer.closest('li').removeClass('error'); 2233 this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = originalTextColorCheckBox; 2234 } 2235 } 2236 } 2237 } 2238 if (this.elemErrorNote == null) 2239 return; 2240 else if (this.elemInput == null) 2241 return; 2242 2243 if (this.elemErrorNote.length > 0) { 2244 var errorParent = this.elemErrorNote[0].parentNode; 2245 2246 for (var i = 0, len = this.elemErrorNote.length; i < len; i++) 2247 errorParent.removeChild(this.elemErrorNote[i]); 2248 2249 this.elemErrorNote = null; 2250 } 2251 2252 this.$elemContainer.removeClass('error'); 2253 !window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
2254}; 2255 2256FieldValidator.prototype.getErrorMessage = function (errorTemplateMsg) { 2257 var $fieldElemContainer = $(this.elemContainer); 2258 2259 if (!$fieldElemContainer.hasClass('formFieldContainer')) 2260 { 2261 $fieldElemContainer = $fieldElemContainer.parents('.formFieldContainer'); 2262 } 2263 2264 var $fieldLabelElem = $fieldElemContainer.find('.labelForFieldElement'); 2265 var fieldLabel = ''; 2266 2267 if ($fieldLabelElem.length > 0) 2268 { 2269 fieldLabel = $fieldLabelElem.text().replace(/\*/g, ''); 2270 } 2271 else 2272 { 2273 $fieldLabelElem = $fieldElemContainer.find('legend'); 2274 if ($fieldLabelElem.length > 0) 2275 { 2276 fieldLabel = $fieldLabelElem.text().replace(/\*/g, ''); 2277 } 2278 } 2279 2280 var errorMsg = errorTemplateMsg.format(fieldLabel); 2281 2282 return errorMsg; 2283} 2284; 2285/// <reference path="../../../../Scripts/json2.js"/> 2286/// <reference path="../../../../Scripts/HtmlExtensionSupport.js"/> 2287/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" /> 2288/// <reference path="Home.js" /> 2289 2290// Configurable in debug console. Controls behavior of FieldConditionsRunner.log(). 2291var condRunnerHideLogDupes = true; 2292 2293// Missing methods for IE 2294if (!Element.prototype.matches) { 2295 Element.prototype.matches = 2296 Element.prototype.msMatchesSelector || 2297 Element.prototype.webkitMatchesSelector; 2298} 2299 2300if (!Element.prototype.closest) { 2301 Element.prototype.closest = function (s) { 2302 var el = this; 2303 2304 do { 2305 if (Element.prototype.matches.call(el, s)) return el; 2306 el = el.parentElement || el.parentNode; 2307 } while (el !== null && el.nodeType === 1); 2308 return null; 2309 }; 2310} 2311 2312////////// Helper Functions /////////////// 2313 2314$("*").focus(function () { 2315 var inputs = $("input[type=text][cp5ph=true], textarea[cp5ph=true]"); 2316 if (inputs != null) { 2317 for (var i = 0; i < inputs.length; i++) { 2318 var elementName = inputs[i].getAttribute('id'); 2319 if (elementName != null) { 2320 var top = $('#' + elementName).offset().top; 2321 var left = $('#' + elementName).offset().left; 2322 if (top != -2 && left != -2) 2323 $('#ph_' + elementName).offset({ left: (left + 5), top: (top + 5) }); 2324 } 2325 } 2326 } 2327}); 2328 2329// Helper that handles replacement of special characters. 2330// * value - variable to handle characters of. 2331function HandleCharacters(value) { 2332 return value.replace(/,/g, '_d'); 2333} 2334 2335if (Array.prototype.any == null && !$.isFunction(Array.prototype.any)) { 2336 // Returns true if callback condition returns true for any items in array. 2337 // * callback - Callback function with single argument for current index. The 'this' reference points to the array. 2338 Array.prototype.any = function (callback) { 2339 if (this == null) 2340 return false; 2341 2342 for (var i = 0, len = this.length; i < len; i++) { 2343 if (callback.call(this, i)) 2344 return true; 2345 } 2346 2347 return false; 2348 } 2349} 2350 2351if (Array.prototype.all == null && !$.isFunction(Array.prototype.all)) { 2352 // Returns true if callback condition returns true for all items in array. 2353 // * callback - Callback function with single argument for current index. The 'this' reference points to the array. 2354 Array.prototype.all = function (callback) { 2355 if (this == null) 2356 return false; 2357 2358 for (var i = 0, len = this.length; i < len; i++) { 2359 if (!callback.call(this, i)) 2360 return false; 2361 } 2362 2363 return true; 2364 }; 2365} 2366 2367if (Error.argNull == null || !$.isFunction(Error.argNull)) { 2368 // Static helper that creates error when null argument that should not be null is encountered. 2369 // * argName - Name of problematic null argument. 2370 Error.argNull = function (argName) { 2371 return new ReferenceError(argName + " cannot be null"); 2372 } 2373} 2374 2375// Overridden on a per-form basis. Retrieves validator information for submission validation. 2376// * validators - Validator dictionary. 2377function getFormConditionData() { 2378 return []; 2379} 2380 2381// Prints useful debugging information for FieldConditionsRunner.run() results. Use as done callback. 2382// Note: When minifiying the JavaScript, this function can/should be removed. 2383// * logs - Log generated by run(). 2384function debugCondRunnerLogResults(logs) { 2385 if (logs == null) 2386 throw new Error.argNull("logs"); 2387 2388 console.log("## run() started at " + (new Date()) + " ##"); 2389 2390 for (var i = 0, iLen = logs.length; i < iLen; i++) { 2391 var log = logs[i]; 2392 var resultEx = log.isMet ? " (met)" : " (not met)"; 2393 2394 console.log( 2395 "field: " + log.id + 2396 ", condIndex: " + log.condIndex + 2397 ", action: " + ConditionActions.getName(log.actType) + 2398 ", result: " + ConditionRunnerActResult.getName(log.result) + resultEx); 2399 2400 if (log.needRestart) 2401 console.log("-- restart --"); 2402 } 2403 2404 console.log("## run() ended ##"); 2405 console.log("\t"); 2406} 2407 2408// Helper that repositions line siblings by re-assigning left/middle/right classes. 2409// * liSiblingList - Array of LI elements for a form line. 2410function repositionLineSiblings(liSiblingList) { 2411 if (liSiblingList == null) 2412 throw new Error.argNull("liSiblingList"); 2413 2414 var sibling, $sibling; 2415 var len = liSiblingList.length; 2416 2417 if (len > 0) { 2418 var operableNodes = []; 2419 2420 for (var i = 0; i < len; i++) { 2421 sibling = liSiblingList[i]; 2422 $sibling = $(sibling); 2423 2424 if (!$sibling.hasClass('hidden')) 2425 operableNodes.push($sibling); 2426 } 2427 2428 len = operableNodes.length; 2429 2430 if (len > 0) { 2431 operableNodes[0].removeClass('middle right').addClass('left'); 2432 2433 var j = 1; 2434 2435 for (; j < len - 1; j++) 2436 operableNodes[j].removeClass('left right').addClass('middle'); 2437 2438 if (j < len) 2439 operableNodes[j].removeClass('left middle').addClass('right'); 2440 } 2441 } 2442} 2443 2444// Helper that gets sibling field LI elements for a line field LI element. 2445// * liElem - The LI element for a form field to get line siblings LIs for. 2446function getLineSiblings(liElem) { 2447 2448 if (liElem == null && (window.location.href).toString().toLowerCase().indexOf("print") != -1) 2449 return ""; 2450 2451 if (liElem == null) 2452 throw new Error.argNull("liElem"); 2453 2454 var nodeList = []; 2455 var startNode = liElem; 2456 2457 // Get start of line. 2458 while (startNode && !$(startNode).hasClass('lineStart')) 2459 startNode = previousElementSibling(startNode); 2460 2461 // Get end of line. 2462 if (startNode) { 2463 var endNode = startNode; 2464 var $endNode = null; 2465 2466 while (endNode != null) { 2467 $endNode = $(endNode); 2468 2469 if ($endNode.hasClass('lineEnd')) 2470 break; 2471 2472 endNode = nextElementSibling(endNode); 2473 } 2474 } 2475 2476 if (startNode && endNode) { 2477 var nodeEnum = startNode; 2478 2479 while (true) { 2480 nodeList.push(nodeEnum); 2481 2482 if (nodeEnum == endNode) 2483 break; 2484 2485 nodeEnum = nextElementSibling(nodeEnum); 2486 } 2487 } 2488 2489 return nodeList; 2490} 2491 2492////////// Enumerations /////////////////// 2493 2494// Condition actions. 2495var ConditionActions = { 2496 None: 0, 2497 Show: 1, 2498 Hide: 2, 2499 Require: 3, 2500 RedirectTo: 4, 2501 getName: function (value) { 2502 for (var k in ConditionActions) { 2503 if (ConditionActions[k] == value) 2504 return k; 2505 } 2506 2507 return null; 2508 } 2509}; 2510 2511// Condition comparison types. 2512var ConditionComparison = { 2513 None: 0, 2514 GreaterThan: 1, 2515 LessThan: 2, 2516 EqualTo: 3, 2517 NotEqualTo: 4, 2518 Contains: 5, 2519 Available: 6, 2520 Unavailable: 7, 2521 Between: 8, 2522 getName: function (value) { 2523 for (var k in ConditionComparison) { 2524 if (ConditionComparison[k] == value) 2525 return k; 2526 } 2527 2528 return null; 2529 } 2530}; 2531 2532// Condition runner action results. 2533var ConditionRunnerActResult = { 2534 None: 0, 2535 SuccessImplicit: 1, 2536 SuccessExplicit: 2, 2537 SkipDuplicate: 3, 2538 SkipContradiction: 4, 2539 getName: function (value) { 2540 for (var k in ConditionRunnerActResult) { 2541 if (ConditionRunnerActResult[k] == value) 2542 return k; 2543 } 2544 2545 return null; 2546 } 2547}; 2548
2549////////// FieldCondition Class /////////// 2550 2551// Internal. Creates instance of FieldCondition class. 2552// * parent - Parent FieldConditions instance. 2553// * condInfo - Object containing properties {compType, compValue, actElemInput, actID, actType, actFieldType, actURL, actURLNewWindow}. 2554function FieldCondition(parent, condInfo) { 2555 if (parent == null) 2556 throw new Error.argNull("parent"); 2557 else if (condInfo == null) 2558 throw new Error.argNull("condInfo"); 2559 2560 $.extend(this, condInfo); 2561 2562 // Set instance parent. 2563 this.parent = parent; 2564 2565 // Process actElemInput argument. 2566 var actElemInput = this.actElemInput; 2567 this.actElemInputIsArray = $.isArray(actElemInput); 2568 2569 if (this.actElemInputIsArray) { 2570 for (var i = 0, len = actElemInput.length; i < len; i++) { 2571 if (typeof (actElemInput[i]) == "string") 2572 actElemInput[i] = document.getElementById(actElemInput[i]); 2573 } 2574 2575 if (actElemInput.length > 0 && actElemInput[0]) { 2576 this.actElemContainer = actElemInput[0].closest('.formFieldContainer'); 2577 this.actElemInput = actElemInput; 2578 } 2579 } 2580 else { 2581 if (typeof (actElemInput) == "string") 2582 actElemInput = document.getElementById(actElemInput); 2583 2584 if (actElemInput) { 2585 this.actElemContainer = actElemInput.closest('.formFieldContainer'); 2586 this.actElemInput = actElemInput; 2587 } 2588 } 2589 2590 // Get shared container. 2591 this.$actElemContainer = $(this.actElemContainer); 2592} 2593 2594// Static. Creates multiple instances from list of condition information. 2595FieldCondition.createMulti = function (parent, condList) { 2596 if (parent == null) 2597 throw new Error.argNull("parent"); 2598 else if (condList == null) 2599 throw new Error.argNull("condList"); 2600 2601 var ret = []; 2602 2603 for (var i = 0, len = condList.length; i < len; i++) 2604 ret.push(new FieldCondition(parent, condList[i])); 2605 2606 return ret; 2607} 2608 2609// Checks if condition is met and performs any necessary action. 2610// * updateUI - Determines if UI is updated after check. Treated as true if null/unspecified. 2611FieldCondition.prototype.check = function (updateUI) { 2612 // TODO: Handle checkboxes/radios/dropdowns when Akila gets that done on back-end. 2613 var thisValue = []; 2614 var compValue = this.compValue; 2615 var isMet = false; 2616 2617 switch (this.actFieldType) { 2618 case FieldTypes.Checkboxes: 2619 for (var i = 0, len = this.actElemInput.length; i < len; i++) { 2620 if (this.actElemInput[i].checked) 2621 thisValue.push(this.actElemInput[i].value.replace(/\,/g, '_comma_')); 2622 } 2623 break; 2624 case FieldTypes.RadioButtons: 2625 for (var i = 0, len = this.actElemInput.length; i < len; i++) { 2626 if (this.actElemInput[i].checked) { 2627 thisValue = [this.actElemInput[i].value]; 2628 break; 2629 } 2630 } 2631 break; 2632 case FieldTypes.DateTime: 2633 var tempArray = new Array(); 2634 for (var i = 0, len = this.actElemInput.length; i < len; i++) { 2635 tempArray.push([this.actElemInput[i].value]) 2636 } 2637 thisValue = [tempArray.join(',')]; 2638 break; 2639 default: 2640 thisValue = [this.actElemInput.value]; 2641 } 2642 2643 var extractMoneyValue = function (v) { 2644 var stringBeginsWith = function (val, beginString) { 2645 return val.substring(0, beginString.length) === beginString 2646 } 2647 if (v && v != '') { 2648 if (stringBeginsWith(v, '$')) { 2649 return v.substring(1).replace(',', ''); 2650 } 2651 } 2652 return v.replace(',', ''); 2653 }; 2654 2655 2656 var isNum = function (val) { 2657 var extracted = extractMoneyValue(val); 2658 return parseFloat(extracted) === +extracted; 2659 }; 2660 2661 var checkAgainst = function (compValue) { 2662 switch (this.compType) { 2663 case ConditionComparison.GreaterThan: 2664 return thisValue.any(function (index) { 2665 if (isNum(this[index]) && isNum(HandleCharacters(compValue))) { 2666 return parseFloat(extractMoneyValue(this[index])) > parseFloat(extractMoneyValue(HandleCharacters(compValue))); 2667 } 2668 return false; 2669 }); 2670 case ConditionComparison.LessThan: 2671 return thisValue.any(function (index) { 2672 if (isNum(this[index]) && isNum(HandleCharacters(compValue))) { 2673 return parseFloat(extractMoneyValue(this[index])) < parseFloat(extractMoneyValue(HandleCharacters(compValue))); 2674 } 2675 return false; 2676 }); 2677 case ConditionComparison.EqualTo: 2678 if (this.actFieldType == FieldTypes.DateTime) { 2679 if (this.actIsDateTimeSpan) { 2680 return this.checkDateEqualSpan(thisValue[0], compValue); 2681 } 2682 else { 2683 return this.checkDateEqualTo(thisValue[0], compValue); 2684 } 2685 } 2686 else { 2687 return thisValue.any(function (index) { 2688 return (isNull(HandleCharacters(this[index]), '') == isNull(HandleCharacters(compValue), '')); 2689 });
2690 } 2691 case ConditionComparison.NotEqualTo: 2692 return thisValue.any(function (index) { return (isNull(this[index], '').toUpperCase() != isNull(HandleCharacters(compValue), '').toUpperCase()); }); 2693 break; 2694 case ConditionComparison.Contains: 2695 var containsExp = new RegExp(RegExp.metaEscape(HandleCharacters(compValue)), 'i'); 2696 2697 return thisValue.any(function (index) { 2698 return containsExp.test(this[index]); 2699 }); 2700 case ConditionComparison.Unavailable: 2701 return this.$actElemContainer.hasClass('unavailable'); 2702 case ConditionComparison.Available: 2703 return !this.$actElemContainer.hasClass('unavailable'); 2704 case ConditionComparison.Between: 2705 return this.checkDateBetween(thisValue[0], compValue); 2706 return; 2707 case ConditionComparison.None: 2708 default: 2709 return true; 2710 } 2711 }; 2712 2713 switch (this.actFieldType) { 2714 case FieldTypes.Checkboxes: 2715 var compArr = compValue.split(','); 2716 2717 isMet = true; 2718 2719 for (var i = 0, len = compArr.length; i < len; i++) { 2720 2721 if (!checkAgainst.call(this, compArr[i])) { 2722 isMet = false; 2723 break; 2724 } 2725 } 2726 break; 2727 default: 2728 isMet = checkAgainst.call(this, compValue); 2729 } 2730 2731 if (updateUI) 2732 this.doAction(isMet); 2733 2734 return isMet; 2735}; 2736 2737// Performs changes to applying field based on condition action and comparison result. 2738FieldCondition.prototype.doAction = function (isMet) { 2739 var validator = (this.parent.validators ? this.parent.validators[this.parent.id] : null); 2740 2741 switch (this.actType) { 2742 case ConditionActions.Show: 2743 this.doActionShow(isMet, validator); 2744 break; 2745 case ConditionActions.Hide: 2746 this.doActionHide(isMet, validator); 2747 break; 2748 case ConditionActions.Require: 2749 this.doActionRequire(isMet, validator); 2750 break; 2751 case ConditionActions.RedirectTo: 2752 this.doActionRedirect(isMet, validator); 2753 break; 2754 } 2755}; 2756 2757FieldCondition.prototype.doActionShow = function (isMet, validator) { 2758 if (isMet) { 2759 this.parent.$elemContainer.removeClass('hidden'); 2760 this.parent.$elemContainer.removeClass('unavailable'); 2761 2762 // Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed). 2763 repositionLineSiblings(getLineSiblings(this.parent.elemContainer)); 2764 2765 if (this.parent.$elemContainer.find('label > span') != null && this.parent.$elemContainer.find('label > span').length > 0) 2766 $('#liRequiredFieldInfo').show(); 2767 2768 if (validator) 2769 validator.enabled = true; 2770 } 2771 else { 2772 this.parent.$elemContainer.addClass('hidden'); 2773 this.parent.$elemContainer.addClass('unavailable'); 2774 2775 // Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed). 2776 repositionLineSiblings(getLineSiblings(this.parent.elemContainer)); 2777 2778 if (validator) 2779 validator.enabled = false; 2780 } 2781}; 2782 2783FieldCondition.prototype.doActionHide = function (isMet, validator) { 2784 if (isMet) { 2785 this.parent.$elemContainer.addClass('hidden'); 2786 this.parent.$elemContainer.addClass('unavailable'); 2787 2788 // Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed). 2789 repositionLineSiblings(getLineSiblings(this.parent.elemContainer)); 2790 2791 if (validator) 2792 validator.enabled = false; 2793 } 2794 else { 2795 this.parent.$elemContainer.removeClass('hidden'); 2796 this.parent.$elemContainer.removeClass('unavailable'); 2797 2798 // Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed). 2799 repositionLineSiblings(getLineSiblings(this.parent.elemContainer)); 2800 2801 if (validator) 2802 validator.enabled = true; 2803 } 2804}; 2805 2806FieldCondition.prototype.doActionRequire = function (isMet, validator) { 2807 if (isMet) { 2808 this.parent.$elemReq.removeClass('hidden'); 2809 2810 if (validator == null) { 2811 validator = new FieldValidator(this.parent.elemInput, this.parent.id, this.parent.fieldType, this.parent.isInternal, true, FieldValidation.Any); 2812 this.parent.validators.add(validator); 2813 } 2814 2815 this.parent.$elemContainer.find('label.labelForFieldElement > span').show(); 2816 $('#liRequiredFieldInfo').show(); 2817 if (validator) 2818 validator.isRequired = true; 2819 } 2820 else { 2821 this.parent.$elemReq.addClass('hidden'); 2822 this.parent.$elemContainer.find('label.labelForFieldElement > span').hide(); 2823 2824 if (validator) 2825 validator.isRequired = false; 2826 } 2827}; 2828 2829FieldCondition.prototype.doActionRedirect = function (isMet, validator) { 2830 // TODO: Some point alter this to pass in references for submitRedirect/redirectNewWindow. Not ideal at moment to ref. global vars. 2831 var submitRedirect = document.getElementById('submitRedirect'); 2832 2833 redirectNewWindow = null; 2834 submitRedirect.value = ''; 2835 2836 if (isMet) { 2837 if (this.actURLNewWindow) { 2838 redirectNewWindow = this.actURL; 2839 submitRedirect.value = ''; 2840 } 2841 else 2842 submitRedirect.value = this.actURL; 2843 } 2844}; 2845 2846// Attachs event handler to field to monitor changes. 2847// * condRunner - FieldConditionsRunner instance to attach. 2848// Note: Calling more than once will have no effect. 2849FieldCondition.prototype.attachRunner = function (condRunner) { 2850 if (condRunner == null) 2851 throw new Error.argNull("condRunner"); 2852 2853 // Do not try to bind this if it is an internal field condition and we are not running internal field 2854 // conditions. 2855 if (this.parent.isInternal && !condRunner.runInternal) 2856 return; 2857 2858 if ($.data(this.actElemInput, "runner_chghook") == null) { 2859 var changeHook = function (event) { 2860 condRunner.run(true); 2861 !window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
2862 }; 2863 $.data(this.actElemInput, "runner_chghook", changeHook); 2864 $(this.actElemInput).change(changeHook); 2865 } 2866}; 2867 2868// Removes event handler that monitors changes. 2869// Note: Calling more than once will have no effect. 2870FieldCondition.prototype.detachRunner = function () { 2871 var changeHook = $.data(this.actElemInput, "runner_chghook"); 2872 2873 if (changeHook) { 2874 $(this.actElemInput).unbind('change', changeHook); 2875 $.removeData(this.actElemInput, "runner_chghook"); 2876 } 2877}; 2878 2879FieldCondition.prototype.checkDateCommon = function (thisValue, compValue) { 2880 var defaultDate = '1/1/70'; 2881 var defualtDateObj = new Date(defaultDate); 2882 2883 var compArr = compValue.split(','); 2884 var valueArr = thisValue.split(','); 2885 2886 var hasTimeOfDayStart = false; 2887 var hasTimeOfDayEnd = false; 2888 2889 var compareStartDate; 2890 var compareEndDate; 2891 2892 if (compArr[0] == '') { 2893 compareStartDate = new Date(defaultDate + ' ' + compArr[2]); 2894 } 2895 else { 2896 compareStartDate = new Date(compArr[0] + ' ' + compArr[2]); 2897 } 2898 2899 if (compArr[1] == '') { 2900 compareEndDate = new Date(defaultDate + ' ' + compArr[3]); 2901 } 2902 else { 2903 compareEndDate = new Date(compArr[1] + ' ' + compArr[3]); 2904 } 2905 2906 var validStartDate = new Date(defaultDate); 2907 var validEndDate = new Date(defaultDate); 2908 var timeOfDayStart = ''; 2909 var timeOfDayEnd = ''; 2910 2911 if (valueArr.length == 4) { 2912 if (valueArr[0] == '') { 2913 validStartDate = new Date(defaultDate + ' ' + valueArr[2]); 2914 } 2915 else { 2916 validStartDate = new Date(valueArr[0] + ' ' + valueArr[2]); 2917 } 2918 2919 timeOfDayStart = this.getAMPMString(valueArr[2]); 2920 2921 if (timeOfDayStart != '') 2922 hasTimeOfDayStart = true; 2923 2924 if (valueArr[1] == '') { 2925 validEndDate = new Date(defaultDate + ' ' + valueArr[3]); 2926 } 2927 else { 2928 validEndDate = new Date(valueArr[1] + ' ' + valueArr[3]); 2929 } 2930 2931 timeOfDayEnd = this.getAMPMString(valueArr[3]); 2932 2933 if (timeOfDayEnd != '') 2934 hasTimeOfDayEnd = true; 2935 } 2936 else if (valueArr.length == 2) { 2937 timeOfDayStart = this.getAMPMString(valueArr[0]); 2938 2939 if (timeOfDayStart == '') { 2940 if (valueArr[0] != '') { 2941 validStartDate = new Date(valueArr[0]); 2942 hasTimeOfDayStart = true; 2943 } 2944 } 2945 else { 2946 validStartDate = new Date(defaultDate + ' ' + valueArr[0]); 2947 hasTimeOfDayStart = true; 2948 } 2949 2950 timeOfDayEnd = this.getAMPMString(valueArr[1]); 2951 2952 if (timeOfDayEnd == '') { 2953 if (valueArr[1] != '') { 2954 validEndDate = new Date(valueArr[1]); 2955 hasTimeOfDayEnd = true; 2956 } 2957 } 2958 else { 2959 validEndDate = new Date(defaultDate + ' ' + valueArr[1]); 2960 hasTimeOfDayEnd = true; 2961 } 2962 } 2963 2964 return { 2965 validStartDate: validStartDate, 2966 compareStartDate: compareStartDate, 2967 compareEndDate: compareEndDate, 2968 hasTimeOfDayStart: hasTimeOfDayStart, 2969 validEndDate: validEndDate, 2970 hasTimeOfDayEnd: hasTimeOfDayEnd 2971 }; 2972}; 2973 2974FieldCondition.prototype.checkDateBetween = function (thisValue, compValue) { 2975 var checkDateCommonData = this.checkDateCommon(thisValue, compValue); 2976 2977 return (checkDateCommonData.validStartDate.getTime() >= checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.validStartDate.getTime() <= checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.hasTimeOfDayStart) 2978 && (checkDateCommonData.validEndDate.getTime() <= checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.validEndDate.getTime() >= checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.hasTimeOfDayEnd); 2979}; 2980 2981FieldCondition.prototype.checkDateEqualSpan = function (thisValue, compValue) { 2982 var checkDateCommonData = this.checkDateCommon(thisValue, compValue); 2983 2984 return (checkDateCommonData.validStartDate.getTime() == checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.hasTimeOfDayStart) 2985 && (checkDateCommonData.validEndDate.getTime() == checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.hasTimeOfDayEnd); 2986}; 2987 2988FieldCondition.prototype.checkDateEqualTo = function (thisValue, compValue) { 2989 var defaultDate = '1/1/70'; 2990 var defualtDateObj = new Date(defaultDate); 2991 2992 var compArr = compValue.split(','); 2993 var valueArr = thisValue.split(','); 2994 2995 var compareDate; 2996 2997 var hasTimeOfDay = false; 2998 2999 if (compArr[0] == '') { 3000 compareDate = new Date(defaultDate + ' ' + compArr[2]); 3001 } 3002 else { 3003 compareDate = new Date(compArr[0] + ' ' + compArr[2]); 3004 } 3005 3006 var validDate = new Date(defaultDate); 3007 var timeOfDay = ''; 3008 3009 if (valueArr.length == 1) { 3010 timeOfDay = this.getAMPMString(valueArr[0]); 3011 3012 if (timeOfDay == '') { 3013 if (valueArr[0] != '') { 3014 validDate = new Date(valueArr[0]); 3015 hasTimeOfDay = true; 3016 } 3017 } 3018 else { 3019 hasTimeOfDay = true; 3020 validDate = new Date(valueArr[0]); 3021 } 3022 } 3023 else if (valueArr.length == 2) { 3024 timeOfDay = this.getAMPMString(valueArr[1]); 3025 3026 if (timeOfDay != '') 3027 hasTimeOfDay = true; 3028 3029 if (valueArr[0] != '') { 3030 validDate = new Date((valueArr[0] + ' ' + valueArr[1] + ' ').replace(/\-/g, '/')); 3031 } 3032 else { 3033 validDate = new Date((defaultDate + ' ' + valueArr[0] + ' ').replace(/\-/g, '/')); 3034 } 3035 } 3036 3037 return validDate.getTime() == compareDate.getTime() && hasTimeOfDay; 3038}; 3039 3040FieldCondition.prototype.getAMPMString = function (time) { 3041 var amIndex = time.indexOf('AM'); 3042 var pmIndex = time.indexOf('PM'); 3043 3044 var timeOfDay = ''; 3045 3046 // First see if the time is military time... if so we need to convert it to AM/PM so the rest 3047 // of this function will return the proper value. 3048 3049 if (amIndex === -1 && pmIndex === -1) { 3050 time = militaryTimeToStandard(time); 3051 amIndex = time.indexOf('AM'); 3052 pmIndex = time.indexOf('PM'); 3053 } 3054 3055 if (amIndex != -1) { 3056 timeOfDay = time.substring(amIndex).replace(" ", ""); 3057 } 3058 else if (pmIndex != -1) { 3059 timeOfDay = time.substring(pmIndex).replace(" ", ""); 3060 } 3061 3062 return timeOfDay; 3063}; 3064 3065function militaryTimeToStandard(militaryTime) { 3066 var colonIndex = militaryTime.indexOf(':'); 3067 var hours = ''; 3068 var min = ''; 3069 3070 if (colonIndex === -1) { 3071 hours = militaryTime.substring(0, 2); 3072 min = militaryTime.substring(2, 4); 3073 } else { 3074 var tokens = militaryTime.split(':'); 3075 hours = tokens[0]; 3076 min = tokens[1]; 3077 } 3078 3079 if (hours < 12) { 3080 ampm = 'AM'; 3081 } else { 3082 hours = hours - 12; 3083 ampm = 'PM'; 3084 } 3085 3086 return parseInt(hours) + ':' + ('00' + min).slice('-2') + ' ' + ampm; ß 3087} 3088 3089 3090////////// FieldConditions Class ////////// 3091 3092// Creates instance of FieldConditions class. 3093// * elemInput - Reference to DOM element of field with condition. May be a DOM element or string containing ID. May also be array of DOM elements or IDs. Multiple elements must share same parent. 3094// * id - The ID of the field with the condition. 3095// * fieldType - The field type of the field with the condition. 3096// * isInternal - Whether or not this condition is for internal-only fields. 3097// * condList - List (array) of zero or more condition clauses for the field. 3098// * validators - Validator dictionary. 3099function FieldConditions(options, validators) { 3100 if (options.elemInput == null) 3101 throw new Error.argNull("options.elemInput"); 3102 else if (options.id == null) 3103 throw new Error.argNull("id"); 3104 else if (options.fieldType == null) 3105 throw new Error.argNull("options.fieldType"); 3106 else if (options.condList == null) 3107 throw new Error.argNull("options.condList"); 3108 else if (validators == null) 3109 throw new Error.argNull("validators"); 3110 3111 this.elemInputIsArray = $.isArray(options.elemInput); 3112 3113 // Process elemInput argument. 3114 if (this.elemInputIsArray) { 3115 for (var i = 0, len = options.elemInput.length; i < len; i++) { 3116 if (typeof (options.elemInput[i]) == "string") 3117 options.elemInput[i] = document.getElementById(options.elemInput[i]); 3118 } 3119 3120 if (options.elemInput.length > 0 && options.elemInput[0]) { 3121 this.elemContainer = options.elemInput[0].closest('.formFieldContainer'); 3122 this.elemInput = options.elemInput; 3123 } 3124 } 3125 else { 3126 if (typeof (options.elemInput) == "string") 3127 options.elemInput = document.getElementById(options.elemInput); 3128 3129 if (options.elemInput) { 3130 this.elemContainer = options.elemInput.closest('.formFieldContainer'); 3131 this.elemInput = options.elemInput; 3132 } 3133 } 3134 3135 this.validators = validators; 3136 this.elemReq = document.getElementById('r_' + options.id); 3137 this.$elemContainer = $(this.elemContainer); 3138 this.$elemReq = $(this.elemReq); 3139 this.fieldType = options.fieldType; 3140 this.id = options.id; 3141 this.isInternal = options.isInternal; 3142 this.conditions = FieldCondition.createMulti(this, options.condList); 3143} 3144 3145// Static. Creates a single conditions object from an options object. 3146FieldConditions.createSingle = function (options, validators) { 3147 if (options == null) 3148 throw new Error.argNull("options"); 3149 else if (validators == null) 3150 throw new Error.argNull("validators"); 3151 3152 return new FieldConditions( 3153 options, 3154 validators); 3155}; 3156 3157// Static. Creates dictionary of conditions objects from array of option objects. 3158FieldConditions.createMulti = function (optionsArray, validators) { 3159 if (optionsArray == null) 3160 throw new Error.argNull("optionsArray"); 3161 else if (validators == null) 3162 throw new Error.argNull("validators"); 3163 3164 var ret = { keys: [] }; 3165 var key = null; 3166 3167 for (var i = 0, len = optionsArray.length; i < len; i++) { 3168 if (optionsArray[i].id != null) { 3169 key = optionsArray[i].id; 3170 ret[key] = FieldConditions.createSingle(optionsArray[i], validators); 3171 ret.keys.push(key); 3172 } 3173 } 3174 3175 return ret; 3176}; 3177 3178// Attachs event handler to field to monitor changes. 3179// * condRunner - FieldConditionsRunner instance to attach. 3180// Note: Calling more than once will have no effect. 3181FieldConditions.prototype.attachRunner = function (condRunner) { 3182 if (condRunner == null) 3183 throw new Error.argNull("condRunner"); 3184 3185 for (var i = 0, len = this.conditions.length; i < len; i++) 3186 this.conditions[i].attachRunner(condRunner); 3187};
3188 3189// Removes event handler that monitors changes. 3190// Note: Calling more than once will have no effect. 3191FieldConditions.prototype.detachRunner = function () { 3192 for (var i = 0, len = this.conditions.length; i < len; i++) 3193 this.conditions[i].detachRunner(); 3194}; 3195 3196////////// FieldConditionsRunner Class //// 3197 3198// Creates new instance of conditions runners for the field conditions objects provided. 3199// * allFieldConditions - Array of FieldConditions objects to execute checks for. 3200// * doneCallback - Method executed when run() completes. Recieves log information as argument. 3201// * autoAttach - Determines if allFieldConditions are automatically attached to this instance by the constructor. If null/unspecified, treated as true. 3202// * runInternal - Set to true to execute internal-only fields. 3203function FieldConditionsRunner(allFieldConditions, doneCallback, autoAttach, runInternal) { 3204 if (allFieldConditions == null) 3205 throw new Error.argNull("allFieldConditions"); 3206 else if (doneCallback == null) 3207 throw new Error.argNull("doneCallback"); 3208 3209 this.fieldConditions = allFieldConditions; 3210 this.doneCallback = doneCallback; 3211 this.runInternal = runInternal; 3212 3213 if (autoAttach == null || autoAttach) 3214 this.attachConditions(); 3215} 3216 3217// Static. Internal. Creates static information object for state tracking during execution. 3218FieldConditionsRunner.createFieldStateInfo = function () { 3219 var ret = {}; 3220 3221 ret[ConditionActions.None] = ConditionRunnerActResult.None; 3222 ret[ConditionActions.Show] = ConditionRunnerActResult.None; 3223 ret[ConditionActions.Hide] = ConditionRunnerActResult.None; 3224 ret[ConditionActions.Require] = ConditionRunnerActResult.None; 3225 ret[ConditionActions.RedirectTo] = ConditionRunnerActResult.None; 3226 3227 return ret; 3228}; 3229 3230// Attaches to FieldConditions/FieldCondition objects that runner will use. 3231FieldConditionsRunner.prototype.attachConditions = function () { 3232 for (var i = 0, iLen = this.fieldConditions.keys.length; i < iLen; i++) 3233 this.fieldConditions[this.fieldConditions.keys[i]].attachRunner(this); 3234}; 3235 3236// Detaches to FieldConditions/FieldCondition objects that runner may have used. 3237FieldConditionsRunner.prototype.detachConditions = function () { 3238 for (var i = 0, iLen = this.fieldConditions.keys.length; i < iLen; i++) 3239 this.fieldConditions[this.fieldConditions.keys[i]].detachRunner(this); 3240}; 3241 3242// Internal. Cleans up runner for use again. 3243FieldConditionsRunner.prototype.cleanup = function () { 3244 if (this.fieldConditionResults != null) { 3245 delete this.fieldConditionResults; 3246 delete this.fieldConditionLog; 3247 } 3248}; 3249 3250// Internal. Called by run() to log result for each condition. 3251FieldConditionsRunner.prototype.log = function (id, condIndex, actType, result, isMet, needRestart) { 3252 if (condRunnerHideLogDupes && result == ConditionRunnerActResult.SkipDuplicate) 3253 return; 3254 3255 this.fieldConditionLog.push({ 3256 id: id, 3257 condIndex: condIndex, 3258 actType: actType, 3259 result: result, 3260 isMet: isMet, 3261 needRestart: needRestart 3262 }); 3263}; 3264 3265// Internal. Sets whether or not the condition engine is executing. 3266FieldConditionsRunner.prototype.setRunning = function (value) { 3267 if (arguments.length == 0) 3268 this.isRunning = true; 3269 else 3270 this.isRunning = value; 3271}; 3272 3273// Gets whether or not the condition engine is executing. 3274FieldConditionsRunner.prototype.getRunning = function () { 3275 return this.isRunning; 3276}; 3277 3278// Internal. Tells condition execution task to reset cycle (done whenever something changes). 3279FieldConditionsRunner.prototype.setRestart = function (value) { 3280 if (arguments == null || arguments.length == 0) 3281 this.needRestart = true; 3282 else 3283 this.needRestart = value; 3284}; 3285 3286// Internal. Gets whether or not the runner needs to restart. 3287FieldConditionsRunner.prototype.getRestart = function () { 3288 return this.needRestart; 3289}; 3290 3291// Launches execution of conditions. 3292// * fromEvent - Should be set to true when triggerd from DOM events. 3293// NOTE: fromEvent may be a race condition but will be left for now while debugging (to simplify logs). 3294FieldConditionsRunner.prototype.run = function (fromEvent) { 3295 if (this.getRunning() && !fromEvent) 3296 this.setRestart(); 3297 else { 3298 this.setRunning(); 3299 3300 this.cleanup(); 3301 3302 this.runTask(); 3303 3304 this.doneCallback(this.fieldConditionLog); 3305 3306 this.setRunning(false); 3307 } 3308}; 3309 3310// Internal. Executes conditions. Should not be called while already running, so run() has sync mechanism for this.
3311// * runInternal - If true, conditions for internal conditions will be ran. 3312FieldConditionsRunner.prototype.runTask = function (runInternal) { 3313 var needRestart = true; 3314 3315 // Prepare objects for execution. 3316 this.fieldConditionLog = []; 3317 this.fieldConditionResults = []; 3318 this.setRedirectUrl = false; 3319 3320 // Populate state information. 3321 var condCondList = this.fieldConditions; 3322 var condListInfo = null; 3323 var condList = null; 3324 3325 for (var i = 0, iLen = condCondList.keys.length; i < iLen; i++) 3326 this.fieldConditionResults[condCondList[condCondList.keys[i]].id] = FieldConditionsRunner.createFieldStateInfo(); 3327 3328 // Number of restart actions (tracked to eventually kill infinite loops that should not happen). 3329 var numRestarts = 0; 3330 3331 // Start/restart actions. 3332 while (needRestart && (++numRestarts < 65535)) { 3333 needRestart = false; 3334 this.setRestart(needRestart); 3335 3336 for (var i = 0, iLen = condCondList.keys.length; i < iLen && !needRestart; i++) { 3337 condListInfo = condCondList[condCondList.keys[i]]; 3338 3339 // If we are on the front-end, we don't want to have anything to do with back-end (internal only) 3340 // conditions, they will cause javascript errors. 3341 if (!this.runInternal && condListInfo.isInternal == true) 3342 continue; 3343 3344 if (condListInfo.isInternal == false && document.URL.indexOf("Admin") > -1 && document.URL.indexOf("Submissions") > -1) 3345 continue; 3346 3347 condList = condListInfo.conditions; 3348 3349 for (var j = 0, jLen = condList.length; j < jLen && !needRestart; j++) { 3350 var cond = condList[j]; 3351 var results = this.fieldConditionResults[condListInfo.id]; 3352 var logState = ConditionRunnerActResult.None; 3353 3354 // Determine whether or not condition is met. 3355 var isMet = cond.check(false); 3356 3357 // This is because the effective action for a failed explicit Show is an implicit Hide (and vice versa). 3358 var effectiveActType = cond.actType; 3359 3360 // Determine if action is contradictory or duplicate and skip UI changes if so. 3361 switch (effectiveActType) { 3362 case ConditionActions.Show: 3363 // Show can collide with hide. 3364 var resultsHide = results[ConditionActions.Hide]; 3365 3366 if (resultsHide == ConditionRunnerActResult.SuccessExplicit || (resultsHide == ConditionRunnerActResult.SuccessImplicit && !isMet)) 3367 logState = ConditionRunnerActResult.SkipContradiction; 3368 3369 break; 3370 case ConditionActions.Hide: 3371 // Hide can collide with show. 3372 var resultsShow = results[ConditionActions.Show]; 3373 3374 if (resultsShow == ConditionRunnerActResult.SuccessExplicit || (resultsShow == ConditionRunnerActResult.SuccessImplicit && !isMet)) 3375 logState = ConditionRunnerActResult.SkipContradiction; 3376 3377 break; 3378 case ConditionActions.RedirectTo: 3379 // Redirects work differently since they span all fields. Extra contradiction detection needed. 3380 if (this.setRedirectUrl) 3381 logState = ConditionRunnerActResult.SkipContradiction; 3382 else if (isMet) 3383 this.setRedirectUrl = true; 3384 break; 3385 } 3386 3387 if (isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessExplicit) 3388 logState = ConditionRunnerActResult.SkipDuplicate; 3389 else if (!isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessExplicit) 3390 logState = ConditionRunnerActResult.SkipContradiction; 3391 else if (!isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessImplicit) 3392 logState = ConditionRunnerActResult.SkipDuplicate; 3393 3394 if (logState == ConditionRunnerActResult.None) { 3395 // Determine log result. 3396 logState = isMet ? ConditionRunnerActResult.SuccessExplicit : ConditionRunnerActResult.SuccessImplicit; 3397 3398 // Update running state info. 3399 results[effectiveActType] = logState; 3400 3401 // Update form UI. 3402 cond.doAction(isMet); 3403 3404 // Restart task. 3405 this.setRestart(); 3406 needRestart = true; 3407 } 3408 3409 // Log result. 3410 this.log(condListInfo.id, j, cond.actType, logState, isMet, needRestart); 3411 3412 // Some other action could cause this to get set. 3413 if (this.getRestart()) 3414 needRestart = true; 3415 } 3416 } 3417 } 3418 3419 if (numRestarts >= 65535)
3420 console[console.warn ? "warn" : "log"]("Infinite Loop (heuristic-detected) in FieldConditionsRunner.runTask()"); 3421}; 3422; 3423// This file contains javascript functions for the "External Integration" field type. 3424// REQUIREMENTS: 3425// FieldSetDesigner.js 3426 3427// These values must match the values provided in CivicPlus.Entities.Modules.FormCenter.Enums.ExternalIntegrationCommandType 3428var ExternalIntegrationCommandType = { 3429 XMLCommand: 1, 3430 EchoCommand: 2 3431}; 3432 3433// Closure containing the client-side functionality for the External Integration field type. 3434// * externalIntegrationClass - The name of the CSS class that is used to identify external integration buttons. 3435function ExternalIntegration(externalIntegrationClassname, fieldSetDesigner) { 3436 this.cssClassname = externalIntegrationClassname; 3437 this.fieldSetDesigner = fieldSetDesigner; 3438 3439 this.hookEvents = function () { 3440 var my = this; 3441 if (my.cssClassname == null) 3442 throw 'No external integration button CSS class name has been specified.' 3443 3444 $('.' + this.cssClassname).click(function (event) { 3445 var fieldID = null; 3446 fieldID = event.target.id; 3447 my.sendRequest(fieldID); 3448 }); 3449 }; 3450 3451 this.sendRequest = function (fieldID) { 3452 var params = this.getFormData(); 3453 var formID = $('#Item_FormID').val(); 3454 var intFieldID = fieldID.replace('e_', ''); 3455 3456 var my = this; 3457 var sendData = { 3458 id: formID, 3459 fieldID: intFieldID, 3460 parameters: JSON.stringify(params) 3461 }; 3462 3463 $.ajax({ 3464 type: 'post', 3465 url: '/FormCenter/ExecuteExternalIntegrationCommand', 3466 data: sendData, 3467 success: function (data) { my.updateFormData(data, my); }, 3468 error: function (xhr, status, error) { alert('An error occurred while sending the request.'); }, 3469 beforeSend: function () { 3470 ajaxPostBackStart('Loading'); 3471 }, 3472 complete: function () { 3473 ajaxPostBackEnd(); 3474 } 3475 }); 3476 }; 3477 3478 // This function takes the values from the JSON result and sets the appropriate 3479 // form field values. 3480 this.updateFormData = function (responseFromController, externalIntegrationObject) { 3481 3482 if (responseFromController.error) { 3483 alert('An error occurred: ' + responseFromController.errorMessage); 3484 return; 3485 } 3486 3487 for (var i = 0, len = responseFromController.length; i < len; i++) { 3488 var param = responseFromController[i]; 3489 switch (responseFromController[i].Type) { 3490 case FieldTypes.ShortAnswer: 3491 externalIntegrationObject.updateShortAnswerField(param); 3492 break; 3493 case FieldTypes.LongAnswer: 3494 externalIntegrationObject.updateLongAnswerField(param); 3495 break; 3496 case FieldTypes.Checkboxes: 3497 externalIntegrationObject.updateCheckboxField(param); 3498 break; 3499 case FieldTypes.Dropdown: 3500 externalIntegrationObject.updateDropdownField(param); 3501 break; 3502 case FieldTypes.Password: 3503 externalIntegrationObject.updatePasswordField(param); 3504 break; 3505 case FieldTypes.RadioButtons: 3506 externalIntegrationObject.updateRadioField(param); 3507 break; 3508 case FieldTypes.DateTime: 3509 externalIntegrationObject.updateDateField(param); 3510 break; 3511 } 3512 } 3513 } 3514 3515 this.getFormData = function () { 3516 var params = []; 3517 3518 this.appendShortAnswerFields(params); 3519 this.appendCheckboxFields(params); 3520 this.appendRadioFields(params); 3521 this.appendDropdowns(params); 3522 this.appendTextAreaFields(params); 3523 this.appendPasswordFields(params); 3524 3525 return params; 3526 }; 3527 3528 this.createFieldValue = function (fieldID, type, value, externalName) { 3529 return { ID: fieldID, Type: type, Value: value, ExternalName: externalName}; 3530 }; 3531 3532 this.appendShortAnswerFields = function (params) { 3533 var my = this; 3534 $('input[id^="e_"][type="text"]').each(function (i) { 3535 var element = $(this); 3536 params.push(my.createFieldValue(element.attr('id'), FieldTypes.ShortAnswer, element.val(), my.externalSubmissionName(element))); 3537 }); 3538 }; 3539 3540 this.externalSubmissionName = function($element) { 3541 return $element.parents('*[data-external-submission-name]').attr('data-external-submission-name'); 3542 }; 3543 3544 this.updateShortAnswerField = function (param) { 3545 var id = param.ID; 3546 $('#' + param.ID).val(param.Value); 3547 } 3548 3549 this.appendCheckboxFields = function (params) { 3550 var my = this; 3551 $('input[id^="e_"][type="checkbox"]').each(function (index) { 3552 var item = $(this); 3553 var value = item.attr('value'); 3554 var checked = false; 3555 if (item.is(':checked')) { 3556 checked = true; 3557 } 3558 var fieldValue = my.createFieldValue(item.attr('id'), FieldTypes.Checkboxes, value, my.externalSubmissionName(item)); 3559 fieldValue.Checked = checked; 3560 params.push(fieldValue); 3561 }); 3562 }; 3563 3564 this.updateCheckboxField = function (param) { 3565 var id = param.ID; 3566 if (param.Checked == true) 3567 $('#' + param.ID).prop('checked', true); 3568 else 3569 $('#' + param.ID).prop('checked', false); 3570 }; 3571 3572 this.appendRadioFields = function (params) { 3573 var my = this; 3574 $('input[id^="e_"][type="radio"]').each(function (index) { 3575 var item = $(this); 3576 var value = item.attr('value'); 3577 var checked = false; 3578 if (item.is(':checked')) { 3579 checked = true; 3580 } 3581 var fieldValue = my.createFieldValue(item.attr('id'), FieldTypes.RadioButtons, value, my.externalSubmissionName(item)); 3582 fieldValue.Checked = checked; 3583 3584 params.push(fieldValue); 3585 }); 3586 }; 3587 3588 this.updateRadioField = function (param) { 3589 var id = param.ID; 3590 var value = param.Value; 3591 var button = $('input[id^="' + id + '"][value="' + value + '"]'); 3592 button.prop('checked', true); 3593 }; 3594 3595 this.appendDropdowns = function (params) { 3596 var my = this; 3597 $('select[id^="e_"]').each(function (index) { 3598 var item = $(this); 3599 params.push(my.createFieldValue(item.attr('id'), FieldTypes.Dropdown, item.val(), my.externalSubmissionName(item))); 3600 }); 3601 }; 3602 3603 this.updateDropdownField = function (param) { 3604 $('#' + param.ID).val(param.Value); 3605 } 3606 3607 this.appendTextAreaFields = function (params) { 3608 var my = this; 3609 $('textarea[id^="e_"]').each(function (index) { 3610 var element = $(this); 3611 3612 if (element.closest('li.LongAnswer').length > 0) { 3613 params.push(my.createFieldValue(element.attr('id'), FieldTypes.LongAnswer, element.text(), my.externalSubmissionName(element))); 3614 } 3615 }); 3616 }; 3617 3618 this.updateLongAnswerField = function (param) { 3619 $('#' + param.ID).text(param.Value); 3620 } 3621 3622 this.appendPasswordFields = function (params) { 3623 var my = this; 3624 $('input[id^="e_"][type="password"').each(function (i) { 3625 var element = $(this); 3626 params.push(my.createFieldValue(element.attr('id'), FieldTypes.Password, element.val(), my.externalSubmissionName(element))); 3627 }); 3628 } 3629 3630 this.updatePasswordField = function (param) { 3631 // We don't want to update password information with values returned from the remote service. 3632 return; 3633 } 3634 3635 this.updateDateField = function (param) { 3636 var my = this; 3637 var isSpan = (param.Value.indexOf(',') >= 0); 3638 3639 var dateID = param.ID + '_date'; 3640 var timeID = param.ID + '_time'; 3641 var endDateID = param.ID + '_dateSpan'; 3642 var endTimeID = param.ID + '_timeSpan'; 3643 3644 var updateDate = function (selector, val) { 3645 var datePicker = $('#' + selector); 3646 if (datePicker.length == 0) 3647 return; 3648 3649 datePicker.data('tDatePicker').value(val); 3650 }; 3651 3652 var updateTime = function (selector, val) { 3653 var timePicker = $('#' + selector); 3654 if (timePicker.length == 0) 3655 return; 3656 timePicker.data('tTimePicker').value(val); 3657 }; 3658 3659 if (isSpan) { 3660 var dateArray = param.Value.split(','); 3661 var startDate = new Date(dateArray[0]); 3662 var endDate = new Date(dateArray[1]); 3663 3664 updateDate(dateID, startDate); 3665 updateTime(timeID, startDate); 3666 3667 updateDate(endDateID, endDate); 3668 updateTime(endTimeID, endDate); 3669 } else { 3670 var startDate = new Date(param.Value); 3671 updateDate(dateID, startDate); 3672 updateTime(timeID, startDate); 3673 } 3674 } 3675}; 3676
3677// Calls subroutines used to initialize the ExternalIntegration javascript object. 3678ExternalIntegration.prototype.initialize = function () { 3679 this.hookEvents(); 3680}; 3681 3682var CP_DynamicForm_ExternalSubmissionFE = function($) { 3683 3684 var self = this; 3685 3686 3687 self.getExternalSubmissionData = function() { 3688 var $fieldContainers = $('.formFieldContainer'); 3689 var data = {}; 3690 $fieldContainers.each(function (index) { 3691 var $container = $(this); 3692 var externalSubmissionName = self.getExternalSubmissionName($container); 3693 var singleFieldData = self.getSingleFieldData($container); 3694 if (singleFieldData != null) { 3695 data[externalSubmissionName] = self.getSingleFieldData($container); 3696 } 3697 }); 3698 return data; 3699 }; 3700 3701 self.getExternalSubmissionName = function($container) { 3702 return $container.attr('data-external-submission-name'); 3703 }; 3704 3705 self.getSingleFieldData = function($container) { 3706 var self = this; 3707 var result = null; 3708 if ($container.hasClass('ShortAnswer')) { 3709 result = self.submissionDataShortAnswer($container); 3710 } else if ($container.hasClass('LongAnswer')) { 3711 result = self.submissionDataLongAnswer($container); 3712 } else if ($container.hasClass('Checkboxes')) { 3713 result = self.submissionDataCheckboxes($container); 3714 } else if ($container.hasClass('Dropdown')) { 3715 result = self.submissionDataDropdown($container); 3716 } else if ($container.hasClass('RadioButtons')) { 3717 result = self.submissionDataRadioButtons($container); 3718 } else if ($container.hasClass('ReplyEmail')) { 3719 result = self.submissionDataReplyEmail($container); 3720 } else if ($container.hasClass('DateTime')) { 3721 result = self.submissionDataDateTime($container); 3722 } else if ($container.hasClass('DateTimeSpan')) { 3723 result = self.submissionDataDateTimeSpan($container); 3724 } else if ($container.hasClass('Password')) { 3725 result = self.submissionDataPassword($container); 3726 } 3727 3728 return result; 3729 }; 3730 3731 // 3732 // There needs to be one function for each type of field that will be passed to 3733 // an external submission service. 3734 // 3735 3736 self.submissionDataShortAnswer = function($container) { 3737 return $('input[id^=e_]', $container).val(); 3738 }; 3739 3740 self.submissionDataLongAnswer = function($container) { 3741 return $('textarea', $container).html(); 3742 }; 3743 3744 self.submissionDataPassword = function($container) { 3745 return $('input[id^=e_]', $container).val(); 3746 }; 3747 3748 self.submissionDataCheckboxes = function($container) { 3749 var result = []; 3750 var $checkedItems = $('input[type=checkbox]:checked', $container); 3751 $checkedItems.each(function (index) { 3752 result.push($(this).attr('Value')); 3753 }); 3754 return result.join(); 3755 }; 3756 3757 self.submissionDataDropdown = function($container) { 3758 return $('select option:selected').val(); 3759 }; 3760 3761 self.submissionDataRadioButtons = function($container) { 3762 return $('input:radio:checked', $container).val(); 3763 }; 3764 3765 self.submissionDataReplyEmail = function($container) { 3766 return $('input[id^=e_]', $container).val(); 3767 }; 3768 3769 self.isDateTimeValueEmpty = function(val) { 3770 // The telerik controls sometimes have just "0" if the user does not 3771 // enter a value, so this function checks for both an empty value and for "0" 3772 if (val == null || val == '' || val == '0') 3773 return true; 3774 else 3775 return false; 3776 }; 3777 3778 self.submissionDataDateTime = function($container) { 3779 var $dateDiv = $('div.date', $container).not('.time')[0]; 3780 var $timeDiv = $('div.time', $container)[0]; 3781 3782 var date = $('input', $dateDiv).val(); 3783 var time = $('input', $timeDiv).val(); 3784 3785 return formatDateTime(date, time); 3786 }; 3787 3788 self.formatDateTime = function(date, time) { 3789 var dateEmpty = isDateTimeValueEmpty(date); 3790 var timeEmpty = isDateTimeValueEmpty(time); 3791 3792 if (dateEmpty && timeEmpty) 3793 return ''; 3794 else if (!dateEmpty && timeEmpty) 3795 return date; 3796 else if (dateEmpty && !timeEmpty) 3797 return time; 3798 else 3799 return date + ' ' + time; 3800 } 3801 3802 self.submissionDataDateTimeSpan = function($container) { 3803 var $startDateDiv = $('div.date', $container).not('.time')[0]; 3804 var $startTimeDiv = $('div.date.time', $container)[0]; 3805 var startDate = $('input', $startDateDiv).val(); 3806 var startTime = $('input', $startTimeDiv).val(); 3807 3808 var $endDateDiv = $('div.date', $container).not('.time')[1]; 3809 var $endTimeDiv = $('div.date.time', $container)[1]; 3810 var endDate = $('input', $endDateDiv).val(); 3811 var endTime = $('input', $endTimeDiv).val(); 3812 3813 var startDateTime = formatDateTime(startDate, startTime); 3814 var endDateTime = formatDateTime(endDate, endTime); 3815 3816 if (startDateTime == '' && endDateTime == '') 3817 return ''; 3818 3819 return startDateTime + ',' + endDateTime; 3820 }; 3821 3822 3823 return self; 3824}(jQuery); 3825 3826 3827var CP_DynamicForm_PrintSubmission = (function () { 3828 self.StatusCodes = { 3829 Loading: 1, 3830 Success: 2, 3831 Spam: 3, 3832 Timeout: 4 3833 }; 3834 3835 return self; 3836})(); 3837/* 3838 * ----------------------------- JSTORAGE ------------------------------------- 3839 * Simple local storage wrapper to save data on the browser side, supporting 3840 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+ 3841 * 3842 * Copyright (c) 2010 - 2012 Andris Reinman, [email protected]
3843 * Project homepage: www.jstorage.info 3844 * 3845 * Licensed under MIT-style license: 3846 * 3847 * Permission is hereby granted, free of charge, to any person obtaining a copy 3848 * of this software and associated documentation files (the "Software"), to deal 3849 * in the Software without restriction, including without limitation the rights 3850 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 3851 * copies of the Software, and to permit persons to whom the Software is 3852 * furnished to do so, subject to the following conditions: 3853 * 3854 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3855 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 3856 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 3857 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 3858 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 3859 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3860 * SOFTWARE. 3861 */ 3862 3863(function () { 3864 var 3865 /* jStorage version */ 3866 JSTORAGE_VERSION = "0.4.3", 3867 3868 /* detect a dollar object or create one if not found */ 3869 $ = window.jQuery || window.$ || (window.$ = {}), 3870 3871 /* check for a JSON handling support */ 3872 JSON = { 3873 parse: 3874 window.JSON && (window.JSON.parse || window.JSON.decode) || 3875 String.prototype.evalJSON && function (str) { return String(str).evalJSON(); } || 3876 $.parseJSON || 3877 $.evalJSON, 3878 stringify: 3879 Object.toJSON || 3880 window.JSON && (window.JSON.stringify || window.JSON.encode) || 3881 $.toJSON 3882 }; 3883 3884 // Break if no JSON support was found 3885 if (!JSON.parse || !JSON.stringify) { 3886 throw new Error("No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page"); 3887 } 3888 3889 var 3890 /* This is the object, that holds the cached values */ 3891 _storage = { __jstorage_meta: { CRC32: {} } }, 3892 3893 /* Actual browser storage (localStorage or globalStorage['domain']) */ 3894 _storage_service = { jStorage: "{}" }, 3895 3896 /* DOM element for older IE versions, holds userData behavior */ 3897 _storage_elm = null, 3898 3899 /* How much space does the storage take */ 3900 _storage_size = 0, 3901 3902 /* which backend is currently used */ 3903 _backend = false, 3904 3905 /* onchange observers */ 3906 _observers = {}, 3907 3908 /* timeout to wait after onchange event */ 3909 _observer_timeout = false, 3910 3911 /* last update time */ 3912 _observer_update = 0, 3913 3914 /* pubsub observers */ 3915 _pubsub_observers = {}, 3916 3917 /* skip published items older than current timestamp */ 3918 _pubsub_last = +new Date(), 3919 3920 /* Next check for TTL */ 3921 _ttl_timeout, 3922 3923 /** 3924 * XML encoding and decoding as XML nodes can't be JSON'ized 3925 * XML nodes are encoded and decoded if the node is the value to be saved 3926 * but not if it's as a property of another object 3927 * Eg. - 3928 * $.jStorage.set("key", xmlNode); // IS OK 3929 * $.jStorage.set("key", {xml: xmlNode}); // NOT OK 3930 */ 3931 _XMLService = { 3932 3933 /** 3934 * Validates a XML node to be XML 3935 * based on jQuery.isXML function 3936 */ 3937 isXML: function (elm) { 3938 var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement; 3939 return documentElement ? documentElement.nodeName !== "HTML" : false; 3940 }, 3941 3942 /** 3943 * Encodes a XML node to string 3944 * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/ 3945 */ 3946 encode: function (xmlNode) { 3947 if (!this.isXML(xmlNode)) { 3948 return false; 3949 } 3950 try { // Mozilla, Webkit, Opera 3951 return new XMLSerializer().serializeToString(xmlNode); 3952 } catch (E1) { 3953 try { // IE 3954 return xmlNode.xml; 3955 } catch (E2) { } 3956 } 3957 return false; 3958 }, 3959 3960 /** 3961 * Decodes a XML node from string 3962 * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/ 3963 */ 3964 decode: function (xmlString) { 3965 var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) || 3966 (window.ActiveXObject && function (_xmlString) { 3967 var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
vendor: 26,289 bytes, lines 3968-4773
3968 xml_doc.async = 'false'; 3969 xml_doc.loadXML(_xmlString); 3970 return xml_doc; 3971 }), 3972 resultXML; 3973 if (!dom_parser) { 3974 return false; 3975 } 3976 resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml'); 3977 return this.isXML(resultXML) ? resultXML : false; 3978 } 3979 }; 3980 3981 3982 ////////////////////////// PRIVATE METHODS //////////////////////// 3983 3984 /** 3985 * Initialization function. Detects if the browser supports DOM Storage 3986 * or userData behavior and behaves accordingly. 3987 */ 3988 function _init() { 3989 /* Check if browser supports localStorage */ 3990 var localStorageReallyWorks = false; 3991 if ("localStorage" in window) { 3992 try { 3993 window.localStorage.setItem('_tmptest', 'tmpval'); 3994 localStorageReallyWorks = true; 3995 window.localStorage.removeItem('_tmptest'); 3996 } catch (BogusQuotaExceededErrorOnIos5) { 3997 // Thanks be to iOS5 Private Browsing mode which throws 3998 // QUOTA_EXCEEDED_ERRROR DOM Exception 22. 3999 } 4000 } 4001 4002 if (localStorageReallyWorks) { 4003 try { 4004 if (window.localStorage) { 4005 _storage_service = window.localStorage; 4006 _backend = "localStorage"; 4007 _observer_update = _storage_service.jStorage_update; 4008 } 4009 } catch (E3) {/* Firefox fails when touching localStorage and cookies are disabled */ } 4010 } 4011 /* Check if browser supports globalStorage */ 4012 else if ("globalStorage" in window) { 4013 try { 4014 if (window.globalStorage) { 4015 _storage_service = window.globalStorage[window.location.hostname]; 4016 _backend = "globalStorage"; 4017 _observer_update = _storage_service.jStorage_update; 4018 } 4019 } catch (E4) {/* Firefox fails when touching localStorage and cookies are disabled */ } 4020 } 4021 /* Check if browser supports userData behavior */ 4022 else { 4023 _storage_elm = document.createElement('link'); 4024 if (_storage_elm.addBehavior) { 4025 4026 /* Use a DOM element to act as userData storage */ 4027 _storage_elm.style.behavior = 'url(#default#userData)'; 4028 4029 /* userData element needs to be inserted into the DOM! */ 4030 document.getElementsByTagName('head')[0].appendChild(_storage_elm); 4031 4032 try { 4033 _storage_elm.load("jStorage"); 4034 } catch (E) { 4035 // try to reset cache 4036 _storage_elm.setAttribute("jStorage", "{}"); 4037 _storage_elm.save("jStorage"); 4038 _storage_elm.load("jStorage"); 4039 } 4040 4041 var data = "{}"; 4042 try { 4043 data = _storage_elm.getAttribute("jStorage"); 4044 } catch (E5) { } 4045 4046 try { 4047 _observer_update = _storage_elm.getAttribute("jStorage_update"); 4048 } catch (E6) { } 4049 4050 _storage_service.jStorage = data; 4051 _backend = "userDataBehavior"; 4052 } else { 4053 _storage_elm = null; 4054 return; 4055 } 4056 } 4057 4058 // Load data from storage 4059 _load_storage(); 4060 4061 // remove dead keys 4062 _handleTTL(); 4063 4064 // start listening for changes 4065 _setupObserver(); 4066 4067 // initialize publish-subscribe service 4068 _handlePubSub(); 4069 4070 // handle cached navigation 4071 if ("addEventListener" in window) { 4072 window.addEventListener("pageshow", function (event) { 4073 if (event.persisted) { 4074 _storageObserver(); 4075 } 4076 }, false); 4077 } 4078 } 4079 4080 /** 4081 * Reload data from storage when needed 4082 */ 4083 function _reloadData() { 4084 var data = "{}"; 4085 4086 if (_backend == "userDataBehavior") { 4087 _storage_elm.load("jStorage"); 4088 4089 try { 4090 data = _storage_elm.getAttribute("jStorage"); 4091 } catch (E5) { } 4092 4093 try { 4094 _observer_update = _storage_elm.getAttribute("jStorage_update"); 4095 } catch (E6) { } 4096 4097 _storage_service.jStorage = data; 4098 } 4099 4100 _load_storage(); 4101 4102 // remove dead keys 4103 _handleTTL(); 4104 4105 _handlePubSub(); 4106 } 4107 4108 /** 4109 * Sets up a storage change observer 4110 */ 4111 function _setupObserver() { 4112 if (_backend == "localStorage" || _backend == "globalStorage") { 4113 if ("addEventListener" in window) { 4114 window.addEventListener("storage", _storageObserver, false); 4115 } else { 4116 document.attachEvent("onstorage", _storageObserver); 4117 } 4118 } else if (_backend == "userDataBehavior") { 4119 setInterval(_storageObserver, 1000); 4120 } 4121 } 4122 4123 /** 4124 * Fired on any kind of data change, needs to check if anything has 4125 * really been changed 4126 */ 4127 function _storageObserver() { 4128 var updateTime; 4129 // cumulate change notifications with timeout 4130 clearTimeout(_observer_timeout); 4131 _observer_timeout = setTimeout(function () { 4132 4133 if (_backend == "localStorage" || _backend == "globalStorage") { 4134 updateTime = _storage_service.jStorage_update; 4135 } else if (_backend == "userDataBehavior") { 4136 _storage_elm.load("jStorage"); 4137 try { 4138 updateTime = _storage_elm.getAttribute("jStorage_update"); 4139 } catch (E5) { } 4140 } 4141 4142 if (updateTime && updateTime != _observer_update) { 4143 _observer_update = updateTime; 4144 _checkUpdatedKeys(); 4145 } 4146 4147 }, 25); 4148 } 4149 4150 /** 4151 * Reloads the data and checks if any keys are changed 4152 */ 4153 function _checkUpdatedKeys() { 4154 var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)), 4155 newCrc32List; 4156 4157 _reloadData(); 4158 newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)); 4159 4160 var key, 4161 updated = [], 4162 removed = []; 4163 4164 for (key in oldCrc32List) { 4165 if (oldCrc32List.hasOwnProperty(key)) { 4166 if (!newCrc32List[key]) { 4167 removed.push(key); 4168 continue; 4169 } 4170 if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == "2.") { 4171 updated.push(key); 4172 } 4173 } 4174 } 4175 4176 for (key in newCrc32List) { 4177 if (newCrc32List.hasOwnProperty(key)) { 4178 if (!oldCrc32List[key]) { 4179 updated.push(key); 4180 } 4181 } 4182 } 4183 4184 _fireObservers(updated, "updated"); 4185 _fireObservers(removed, "deleted"); 4186 } 4187 4188 /** 4189 * Fires observers for updated keys 4190 * 4191 * @param {Array|String} keys Array of key names or a key 4192 * @param {String} action What happened with the value (updated, deleted, flushed) 4193 */ 4194 function _fireObservers(keys, action) { 4195 keys = [].concat(keys || []); 4196 if (action == "flushed") { 4197 keys = []; 4198 for (var key in _observers) { 4199 if (_observers.hasOwnProperty(key)) { 4200 keys.push(key); 4201 } 4202 } 4203 action = "deleted"; 4204 } 4205 for (var i = 0, len = keys.length; i < len; i++) { 4206 if (_observers[keys[i]]) { 4207 for (var j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) { 4208 _observers[keys[i]][j](keys[i], action); 4209 } 4210 } 4211 if (_observers["*"]) { 4212 for (var j = 0, jlen = _observers["*"].length; j < jlen; j++) { 4213 _observers["*"][j](keys[i], action); 4214 } 4215 } 4216 } 4217 } 4218 4219 /** 4220 * Publishes key change to listeners 4221 */ 4222 function _publishChange() { 4223 var updateTime = (+new Date()).toString(); 4224 4225 if (_backend == "localStorage" || _backend == "globalStorage") { 4226 _storage_service.jStorage_update = updateTime; 4227 } else if (_backend == "userDataBehavior") { 4228 _storage_elm.setAttribute("jStorage_update", updateTime); 4229 _storage_elm.save("jStorage"); 4230 } 4231 4232 _storageObserver(); 4233 } 4234 4235 /** 4236 * Loads the data from the storage based on the supported mechanism 4237 */ 4238 function _load_storage() { 4239 /* if jStorage string is retrieved, then decode it */ 4240 if (_storage_service.jStorage) { 4241 try { 4242 _storage = JSON.parse(String(_storage_service.jStorage)); 4243 } catch (E6) { _storage_service.jStorage = "{}"; } 4244 } else { 4245 _storage_service.jStorage = "{}"; 4246 } 4247 _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; 4248 4249 if (!_storage.__jstorage_meta) { 4250 _storage.__jstorage_meta = {}; 4251 } 4252 if (!_storage.__jstorage_meta.CRC32) { 4253 _storage.__jstorage_meta.CRC32 = {}; 4254 } 4255 } 4256 4257 /** 4258 * This functions provides the "save" mechanism to store the jStorage object 4259 */ 4260 function _save() { 4261 _dropOldEvents(); // remove expired events 4262 try { 4263 _storage_service.jStorage = JSON.stringify(_storage); 4264 // If userData is used as the storage engine, additional 4265 if (_storage_elm) { 4266 _storage_elm.setAttribute("jStorage", _storage_service.jStorage); 4267 _storage_elm.save("jStorage"); 4268 } 4269 _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; 4270 } catch (E7) {/* probably cache is full, nothing is saved this way*/ } 4271 } 4272 4273 /** 4274 * Function checks if a key is set and is string or numberic 4275 * 4276 * @param {String} key Key name 4277 */ 4278 function _checkKey(key) { 4279 if (!key || (typeof key != "string" && typeof key != "number")) { 4280 throw new TypeError('Key name must be string or numeric'); 4281 } 4282 if (key == "__jstorage_meta") { 4283 throw new TypeError('Reserved key name'); 4284 } 4285 return true; 4286 } 4287 4288 /** 4289 * Removes expired keys 4290 */ 4291 function _handleTTL() { 4292 var curtime, i, TTL, CRC32, nextExpire = Infinity, changed = false, deleted = []; 4293 4294 clearTimeout(_ttl_timeout); 4295 4296 if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object") { 4297 // nothing to do here 4298 return; 4299 } 4300 4301 curtime = +new Date(); 4302 TTL = _storage.__jstorage_meta.TTL; 4303 4304 CRC32 = _storage.__jstorage_meta.CRC32; 4305 for (i in TTL) { 4306 if (TTL.hasOwnProperty(i)) { 4307 if (TTL[i] <= curtime) { 4308 delete TTL[i]; 4309 delete CRC32[i]; 4310 delete _storage[i]; 4311 changed = true; 4312 deleted.push(i); 4313 } else if (TTL[i] < nextExpire) { 4314 nextExpire = TTL[i]; 4315 } 4316 } 4317 } 4318 4319 // set next check 4320 if (nextExpire != Infinity) { 4321 _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime); 4322 } 4323 4324 // save changes 4325 if (changed) { 4326 _save(); 4327 _publishChange(); 4328 _fireObservers(deleted, "deleted"); 4329 } 4330 } 4331 4332 /** 4333 * Checks if there's any events on hold to be fired to listeners 4334 */ 4335 function _handlePubSub() { 4336 var i, len; 4337 if (!_storage.__jstorage_meta.PubSub) { 4338 return; 4339 } 4340 var pubelm, 4341 _pubsubCurrent = _pubsub_last; 4342 4343 for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) { 4344 pubelm = _storage.__jstorage_meta.PubSub[i]; 4345 if (pubelm[0] > _pubsub_last) { 4346 _pubsubCurrent = pubelm[0]; 4347 _fireSubscribers(pubelm[1], pubelm[2]); 4348 } 4349 } 4350 4351 _pubsub_last = _pubsubCurrent; 4352 } 4353 4354 /** 4355 * Fires all subscriber listeners for a pubsub channel 4356 * 4357 * @param {String} channel Channel name 4358 * @param {Mixed} payload Payload data to deliver 4359 */ 4360 function _fireSubscribers(channel, payload) { 4361 if (_pubsub_observers[channel]) { 4362 for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) { 4363 // send immutable data that can't be modified by listeners 4364 _pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload))); 4365 } 4366 } 4367 } 4368 4369 /** 4370 * Remove old events from the publish stream (at least 2sec old) 4371 */ 4372 function _dropOldEvents() { 4373 if (!_storage.__jstorage_meta.PubSub) { 4374 return; 4375 } 4376 4377 var retire = +new Date() - 2000; 4378 4379 for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) { 4380 if (_storage.__jstorage_meta.PubSub[i][0] <= retire) { 4381 // deleteCount is needed for IE6 4382 _storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i); 4383 break; 4384 } 4385 } 4386 4387 if (!_storage.__jstorage_meta.PubSub.length) { 4388 delete _storage.__jstorage_meta.PubSub; 4389 } 4390 4391 } 4392 4393 /** 4394 * Publish payload to a channel 4395 * 4396 * @param {String} channel Channel name 4397 * @param {Mixed} payload Payload to send to the subscribers 4398 */ 4399 function _publish(channel, payload) { 4400 if (!_storage.__jstorage_meta) { 4401 _storage.__jstorage_meta = {}; 4402 } 4403 if (!_storage.__jstorage_meta.PubSub) { 4404 _storage.__jstorage_meta.PubSub = []; 4405 } 4406 4407 _storage.__jstorage_meta.PubSub.unshift([+new Date, channel, payload]); 4408 4409 _save(); 4410 _publishChange(); 4411 } 4412 4413 4414 /** 4415 * JS Implementation of MurmurHash2 4416 * 4417 * SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed) 4418 * 4419 * @author <a href="mailto:[email protected]">Gary Court</a> 4420 * @see http://github.com/garycourt/murmurhash-js 4421 * @author <a href="mailto:[email protected]">Austin Appleby</a> 4422 * @see http://sites.google.com/site/murmurhash/ 4423 * 4424 * @param {string} str ASCII only 4425 * @param {number} seed Positive integer only 4426 * @return {number} 32-bit positive integer hash 4427 */ 4428 4429 function murmurhash2_32_gc(str, seed) { 4430 var 4431 l = str.length, 4432 h = seed ^ l, 4433 i = 0, 4434 k; 4435 4436 while (l >= 4) { 4437 k = 4438 ((str.charCodeAt(i) & 0xff)) | 4439 ((str.charCodeAt(++i) & 0xff) << 8) | 4440 ((str.charCodeAt(++i) & 0xff) << 16) | 4441 ((str.charCodeAt(++i) & 0xff) << 24); 4442 4443 k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16)); 4444 k ^= k >>> 24; 4445 k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16)); 4446 4447 h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k; 4448 4449 l -= 4; 4450 ++i; 4451 } 4452 4453 switch (l) { 4454 case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16; 4455 case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8; 4456 case 1: h ^= (str.charCodeAt(i) & 0xff); 4457 h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)); 4458 } 4459 4460 h ^= h >>> 13; 4461 h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)); 4462 h ^= h >>> 15; 4463 4464 return h >>> 0; 4465 } 4466 4467 ////////////////////////// PUBLIC INTERFACE ///////////////////////// 4468 4469 $.jStorage = { 4470 /* Version number */ 4471 version: JSTORAGE_VERSION, 4472 4473 /** 4474 * Sets a key's value. 4475 * 4476 * @param {String} key Key to set. If this value is not set or not 4477 * a string an exception is raised. 4478 * @param {Mixed} value Value to set. This can be any value that is JSON 4479 * compatible (Numbers, Strings, Objects etc.). 4480 * @param {Object} [options] - possible options to use 4481 * @param {Number} [options.TTL] - optional TTL value 4482 * @return {Mixed} the used value 4483 */ 4484 set: function (key, value, options) { 4485 _checkKey(key); 4486 4487 options = options || {}; 4488 4489 // undefined values are deleted automatically 4490 if (typeof value == "undefined") { 4491 this.deleteKey(key); 4492 return value; 4493 } 4494 4495 if (_XMLService.isXML(value)) { 4496 value = { _is_xml: true, xml: _XMLService.encode(value) }; 4497 } else if (typeof value == "function") { 4498 return undefined; // functions can't be saved! 4499 } else if (value && typeof value == "object") { 4500 // clone the object before saving to _storage tree 4501 value = JSON.parse(JSON.stringify(value)); 4502 } 4503 4504 _storage[key] = value; 4505 4506 _storage.__jstorage_meta.CRC32[key] = "2." + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c); 4507 4508 this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange 4509 4510 _fireObservers(key, "updated"); 4511 return value; 4512 }, 4513 4514 /** 4515 * Looks up a key in cache 4516 * 4517 * @param {String} key - Key to look up. 4518 * @param {mixed} def - Default value to return, if key didn't exist. 4519 * @return {Mixed} the key value, default value or null 4520 */ 4521 get: function (key, def) { 4522 _checkKey(key); 4523 if (key in _storage) { 4524 if (_storage[key] && typeof _storage[key] == "object" && _storage[key]._is_xml) { 4525 return _XMLService.decode(_storage[key].xml); 4526 } else { 4527 return _storage[key]; 4528 } 4529 } 4530 return typeof (def) == 'undefined' ? null : def; 4531 }, 4532 4533 /** 4534 * Deletes a key from cache. 4535 * 4536 * @param {String} key - Key to delete. 4537 * @return {Boolean} true if key existed or false if it didn't 4538 */ 4539 deleteKey: function (key) { 4540 _checkKey(key); 4541 if (key in _storage) { 4542 delete _storage[key]; 4543 // remove from TTL list 4544 if (typeof _storage.__jstorage_meta.TTL == "object" && 4545 key in _storage.__jstorage_meta.TTL) { 4546 delete _storage.__jstorage_meta.TTL[key]; 4547 } 4548 4549 delete _storage.__jstorage_meta.CRC32[key]; 4550 4551 _save(); 4552 _publishChange(); 4553 _fireObservers(key, "deleted"); 4554 return true; 4555 } 4556 return false; 4557 }, 4558 4559 /** 4560 * Sets a TTL for a key, or remove it if ttl value is 0 or below 4561 * 4562 * @param {String} key - key to set the TTL for 4563 * @param {Number} ttl - TTL timeout in milliseconds 4564 * @return {Boolean} true if key existed or false if it didn't 4565 */ 4566 setTTL: function (key, ttl) { 4567 var curtime = +new Date(); 4568 _checkKey(key); 4569 ttl = Number(ttl) || 0; 4570 if (key in _storage) { 4571 4572 if (!_storage.__jstorage_meta.TTL) { 4573 _storage.__jstorage_meta.TTL = {}; 4574 } 4575 4576 // Set TTL value for the key 4577 if (ttl > 0) { 4578 _storage.__jstorage_meta.TTL[key] = curtime + ttl; 4579 } else { 4580 delete _storage.__jstorage_meta.TTL[key]; 4581 } 4582 4583 _save(); 4584 4585 _handleTTL(); 4586 4587 _publishChange(); 4588 return true; 4589 } 4590 return false; 4591 }, 4592 4593 /** 4594 * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set 4595 * 4596 * @param {String} key Key to check 4597 * @return {Number} Remaining TTL in milliseconds 4598 */ 4599 getTTL: function (key) { 4600 var curtime = +new Date(), ttl; 4601 _checkKey(key); 4602 if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) { 4603 ttl = _storage.__jstorage_meta.TTL[key] - curtime; 4604 return ttl || 0; 4605 } 4606 return 0; 4607 }, 4608 4609 /** 4610 * Deletes everything in cache. 4611 * 4612 * @return {Boolean} Always true 4613 */ 4614 flush: function () { 4615 _storage = { __jstorage_meta: { CRC32: {} } }; 4616 _save(); 4617 _publishChange(); 4618 _fireObservers(null, "flushed"); 4619 return true; 4620 }, 4621 4622 /** 4623 * Returns a read-only copy of _storage 4624 * 4625 * @return {Object} Read-only copy of _storage 4626 */ 4627 storageObj: function () { 4628 function F() { } 4629 F.prototype = _storage; 4630 return new F(); 4631 }, 4632 4633 /** 4634 * Returns an index of all used keys as an array 4635 * ['key1', 'key2',..'keyN'] 4636 * 4637 * @return {Array} Used keys 4638 */ 4639 index: function () { 4640 var index = [], i; 4641 for (i in _storage) { 4642 if (_storage.hasOwnProperty(i) && i != "__jstorage_meta") { 4643 index.push(i); 4644 } 4645 } 4646 return index; 4647 }, 4648 4649 /** 4650 * How much space in bytes does the storage take? 4651 * 4652 * @return {Number} Storage size in chars (not the same as in bytes, 4653 * since some chars may take several bytes) 4654 */ 4655 storageSize: function () { 4656 return _storage_size; 4657 }, 4658 4659 /** 4660 * Which backend is currently in use? 4661 * 4662 * @return {String} Backend name 4663 */ 4664 currentBackend: function () { 4665 return _backend; 4666 }, 4667 4668 /** 4669 * Test if storage is available 4670 * 4671 * @return {Boolean} True if storage can be used 4672 */ 4673 storageAvailable: function () { 4674 return !!_backend; 4675 }, 4676 4677 /** 4678 * Register change listeners 4679 * 4680 * @param {String} key Key name 4681 * @param {Function} callback Function to run when the key changes 4682 */ 4683 listenKeyChange: function (key, callback) { 4684 _checkKey(key); 4685 if (!_observers[key]) { 4686 _observers[key] = []; 4687 } 4688 _observers[key].push(callback); 4689 }, 4690 4691 /** 4692 * Remove change listeners 4693 * 4694 * @param {String} key Key name to unregister listeners against 4695 * @param {Function} [callback] If set, unregister the callback, if not - unregister all 4696 */ 4697 stopListening: function (key, callback) { 4698 _checkKey(key); 4699 4700 if (!_observers[key]) { 4701 return; 4702 } 4703 4704 if (!callback) { 4705 delete _observers[key]; 4706 return; 4707 } 4708 4709 for (var i = _observers[key].length - 1; i >= 0; i--) { 4710 if (_observers[key][i] == callback) { 4711 _observers[key].splice(i, 1); 4712 } 4713 } 4714 }, 4715 4716 /** 4717 * Subscribe to a Publish/Subscribe event stream 4718 * 4719 * @param {String} channel Channel name 4720 * @param {Function} callback Function to run when the something is published to the channel 4721 */ 4722 subscribe: function (channel, callback) { 4723 channel = (channel || "").toString(); 4724 if (!channel) { 4725 throw new TypeError('Channel not defined'); 4726 } 4727 if (!_pubsub_observers[channel]) { 4728 _pubsub_observers[channel] = []; 4729 } 4730 _pubsub_observers[channel].push(callback); 4731 }, 4732 4733 /** 4734 * Publish data to an event stream 4735 * 4736 * @param {String} channel Channel name 4737 * @param {Mixed} payload Payload to deliver 4738 */ 4739 publish: function (channel, payload) { 4740 channel = (channel || "").toString(); 4741 if (!channel) { 4742 throw new TypeError('Channel not defined'); 4743 } 4744 4745 _publish(channel, payload); 4746 }, 4747 4748 /** 4749 * Reloads the data from browser storage 4750 */ 4751 reInit: function () { 4752 _reloadData(); 4753 } 4754 }; 4755 4756 // Initialize jStorage 4757 _init(); 4758 4759})(); 4760/*! 4761 * jQuery Form Plugin 4762 * version: 4.3.0 4763 * Requires jQuery v1.7.2 or later 4764 * Project repository: https://github.com/jquery-form/form 4765 4766 * Copyright 2017 Kevin Morris 4767 * Copyright 2006 M. Alsup 4768 4769 * Dual licensed under the LGPL-2.1+ or MIT licenses 4770 * https://github.com/jquery-form/form#license 4771 4772 * This library is free software; you can redistribute it and/or 4773 * modify it under the terms of the GNU Lesser General Public
4774 * License as published by the Free Software Foundation; either 4775 * version 2.1 of the License, or (at your option) any later version. 4776 * This library is distributed in the hope that it will be useful, 4777 * but WITHOUT ANY WARRANTY; without even the implied warranty of 4778 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 4779 * Lesser General Public License for more details. 4780 */ 4781/* global ActiveXObject */ 4782 4783/* eslint-disable */ 4784(function (factory) { 4785 if (typeof define === 'function' && define.amd) { 4786 // AMD. Register as an anonymous module. 4787 define(['jquery'], factory); 4788 } else if (typeof module === 'object' && module.exports) { 4789 // Node/CommonJS 4790 module.exports = function( root, jQuery ) { 4791 if (typeof jQuery === 'undefined') { 4792 // require('jQuery') returns a factory that requires window to build a jQuery instance, we normalize how we use modules 4793 // that require this pattern but the window provided is a noop if it's defined (how jquery works) 4794 if (typeof window !== 'undefined') { 4795 jQuery = require('jquery'); 4796 } 4797 else { 4798 jQuery = require('jquery')(root); 4799 } 4800 } 4801 factory(jQuery); 4802 return jQuery; 4803 }; 4804 } else { 4805 // Browser globals 4806 factory(jQuery); 4807 } 4808 4809}(function ($) { 4810/* eslint-enable */ 4811 'use strict'; 4812 4813 /* 4814 Usage Note: 4815 ----------- 4816 Do not use both ajaxSubmit and ajaxForm on the same form. These 4817 functions are mutually exclusive. Use ajaxSubmit if you want 4818 to bind your own submit handler to the form. For example, 4819 4820 $(document).ready(function() { 4821 $('#myForm').on('submit', function(e) { 4822 e.preventDefault(); // <-- important 4823 $(this).ajaxSubmit({ 4824 target: '#output' 4825 }); 4826 }); 4827 }); 4828 4829 Use ajaxForm when you want the plugin to manage all the event binding 4830 for you. For example, 4831 4832 $(document).ready(function() { 4833 $('#myForm').ajaxForm({ 4834 target: '#output' 4835 }); 4836 }); 4837 4838 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 4839 form does not have to exist when you invoke ajaxForm: 4840 4841 $('#myForm').ajaxForm({ 4842 delegation: true, 4843 target: '#output' 4844 }); 4845 4846 When using ajaxForm, the ajaxSubmit function will be invoked for you 4847 at the appropriate time. 4848 */ 4849 4850 var rCRLF = /\r?\n/g; 4851 4852 /** 4853 * Feature detection 4854 */ 4855 var feature = {}; 4856 4857 feature.fileapi = $('<input type="file">').get(0).files !== undefined; 4858 feature.formdata = (typeof window.FormData !== 'undefined'); 4859 4860 var hasProp = !!$.fn.prop; 4861 4862 // attr2 uses prop when it can but checks the return type for 4863 // an expected string. This accounts for the case where a form 4864 // contains inputs with names like "action" or "method"; in those 4865 // cases "prop" returns the element 4866 $.fn.attr2 = function() { 4867 if (!hasProp) { 4868 return this.attr.apply(this, arguments); 4869 } 4870 4871 var val = this.prop.apply(this, arguments); 4872 4873 if ((val && val.jquery) || typeof val === 'string') { 4874 return val; 4875 } 4876 4877 return this.attr.apply(this, arguments); 4878 }; 4879 4880 /** 4881 * ajaxSubmit() provides a mechanism for immediately submitting 4882 * an HTML form using AJAX. 4883 * 4884 * @param {object|string} options jquery.form.js parameters or custom url for submission 4885 * @param {object} data extraData 4886 * @param {string} dataType ajax dataType 4887 * @param {function} onSuccess ajax success callback function 4888 */ 4889 $.fn.ajaxSubmit = function(options, data, dataType, onSuccess) { 4890 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 4891 if (!this.length) { 4892 log('ajaxSubmit: skipping submit process - no element selected'); 4893 4894 return this; 4895 } 4896 4897 /* eslint consistent-this: ["error", "$form"] */ 4898 var method, action, url, isMsie, iframeSrc, $form = this; 4899 4900 if (typeof options === 'function') { 4901 options = {success: options}; 4902 4903 } else if (typeof options === 'string' || (options === false && arguments.length > 0)) { 4904 options = { 4905 'url' : options, 4906 'data' : data, 4907 'dataType' : dataType 4908 }; 4909 4910 if (typeof onSuccess === 'function') { 4911 options.success = onSuccess; 4912 } 4913 4914 } else if (typeof options === 'undefined') { 4915 options = {}; 4916 } 4917 4918 method = options.method || options.type || this.attr2('method'); 4919 action = options.url || this.attr2('action'); 4920 4921 url = (typeof action === 'string') ? $.trim(action) : ''; 4922 url = url || window.location.href || ''; 4923 if (url) { 4924 // clean url (don't include hash vaue) 4925 url = (url.match(/^([^#]+)/) || [])[1]; 4926 } 4927 // IE requires javascript:false in https, but this breaks chrome >
492783 and goes against spec. 4928 // Instead of using javascript:false always, let's only apply it for IE. 4929 isMsie = /(MSIE|Trident)/.test(navigator.userAgent || ''); 4930 iframeSrc = (isMsie && /^https/i.test(window.location.href || '')) ? 'javascript:false' : 'about:blank'; // eslint-disable-line no-script-url 4931 4932 options = $.extend(true, { 4933 url : url, 4934 success : $.ajaxSettings.success, 4935 type : method || $.ajaxSettings.type, 4936 iframeSrc : iframeSrc 4937 }, options); 4938 4939 // hook for manipulating the form data before it is extracted; 4940 // convenient for use with rich editors like tinyMCE or FCKEditor 4941 var veto = {}; 4942 4943 this.trigger('form-pre-serialize', [this, options, veto]); 4944 4945 if (veto.veto) { 4946 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 4947 4948 return this; 4949 } 4950 4951 // provide opportunity to alter form data before it is serialized 4952 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 4953 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 4954 4955 return this; 4956 } 4957 4958 var traditional = options.traditional; 4959 4960 if (typeof traditional === 'undefined') { 4961 traditional = $.ajaxSettings.traditional; 4962 } 4963 4964 var elements = []; 4965 var qx, a = this.formToArray(options.semantic, elements, options.filtering); 4966 4967 if (options.data) { 4968 var optionsData = $.isFunction(options.data) ? options.data(a) : options.data; 4969 4970 options.extraData = optionsData; 4971 qx = $.param(optionsData, traditional); 4972 } 4973 4974 // give pre-submit callback an opportunity to abort the submit 4975 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 4976 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 4977 4978 return this; 4979 } 4980 4981 // fire vetoable 'validate' event 4982 this.trigger('form-submit-validate', [a, this, options, veto]); 4983 if (veto.veto) { 4984 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 4985 4986 return this; 4987 } 4988 4989 var q = $.param(a, traditional); 4990 4991 if (qx) { 4992 q = (q ? (q + '&' + qx) : qx); 4993 } 4994 4995 if (options.type.toUpperCase() === 'GET') { 4996 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 4997 options.data = null; // data is null for 'get' 4998 } else { 4999 options.data = q; // data is the query string for 'post' 5000 } 5001 5002 var callbacks = []; 5003 5004 if (options.resetForm) { 5005 callbacks.push(function() { 5006 $form.resetForm(); 5007 }); 5008 } 5009 5010 if (options.clearForm) { 5011 callbacks.push(function() { 5012 $form.clearForm(options.includeHidden); 5013 }); 5014 } 5015 5016 // perform a load on the target only if dataType is not provided 5017 if (!options.dataType && options.target) { 5018 var oldSuccess = options.success || function(){}; 5019 5020 callbacks.push(function(data, textStatus, jqXHR) { 5021 var successArguments = arguments, 5022 fn = options.replaceTarget ? 'replaceWith' : 'html'; 5023 5024 $(options.target)[fn](data).each(function(){ 5025 oldSuccess.apply(this, successArguments); 5026 }); 5027 }); 5028 5029 } else if (options.success) { 5030 if ($.isArray(options.success)) { 5031 $.merge(callbacks, options.success); 5032 } else { 5033 callbacks.push(options.success); 5034 } 5035 } 5036 5037 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 5038 var context = options.context || this; // jQuery 1.4+ supports scope context 5039 5040 for (var i = 0, max = callbacks.length; i < max; i++) { 5041 callbacks[i].apply(context, [data, status, xhr || $form, $form]); 5042 } 5043 }; 5044 5045 if (options.error) { 5046 var oldError = options.error; 5047 5048 options.error = function(xhr, status, error) { 5049 var context = options.context || this; 5050 5051 oldError.apply(context, [xhr, status, error, $form]); 5052 }; 5053 } 5054 5055 if (options.complete) { 5056 var oldComplete = options.complete; 5057 5058 options.complete = function(xhr, status) { 5059 var context = options.context || this; 5060 5061 oldComplete.apply(context, [xhr, status, $form]); 5062 }; 5063 } 5064 5065 // are there files to upload? 5066 5067 // [value] (issue #113), also see comment: 5068 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 5069 var fileInputs = $('input[type=file]:enabled', this).filter(function() { 5070 return $(this).val() !== ''; 5071 }); 5072 var hasFileInputs = fileInputs.length > 0; 5073 var mp = 'multipart/form-data'; 5074 var multipart = ($form.attr('enctype') === mp || $form.attr('encoding') === mp); 5075 var fileAPI = feature.fileapi && feature.formdata; 5076 5077 log('fileAPI :' + fileAPI); 5078 5079 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 5080 var jqxhr; 5081 5082 // options.iframe allows user to force iframe mode 5083 // 06-NOV-09: now defaulting to iframe mode if file input is detected 5084 if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 5085 // hack to fix Safari hang (thanks to Tim Molendijk for this) 5086 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 5087 if (options.closeKeepAlive) { 5088 $.get(options.closeKeepAlive, function() { 5089 jqxhr = fileUploadIframe(a); 5090 }); 5091 5092 } else { 5093 jqxhr = fileUploadIframe(a); 5094 } 5095 5096 } else if ((hasFileInputs || multipart) && fileAPI) { 5097 jqxhr = fileUploadXhr(a); 5098 5099 } else { 5100 jqxhr = $.ajax(options); 5101 } 5102 5103 $form.removeData('jqxhr').data('jqxhr', jqxhr); 5104 5105 // clear element array 5106 for (var k = 0; k < elements.length; k++) { 5107 elements[k] = null; 5108 } 5109 5110 // fire 'notify' event 5111 this.trigger('form-submit-notify', [this, options]); 5112 5113 return this; 5114 5115 // utility fn for deep serialization 5116 function deepSerialize(extraData) { 5117 var serialized = $.param(extraData, options.traditional).split('&'); 5118 var len = serialized.length; 5119 var result = []; 5120 var i, part; 5121 5122 for (i = 0; i < len; i++) { 5123 // #252; undo param space replacement 5124 serialized[i] = serialized[i].replace(/\+/g, ' '); 5125 part = serialized[i].split('='); 5126 // #278; use array instead of object storage, favoring array serializations 5127 result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); 5128 } 5129 5130 return result; 5131 } 5132 5133 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 5134 function fileUploadXhr(a) { 5135 var formdata = new FormData(); 5136 5137 for (var i = 0; i < a.length; i++) { 5138 formdata.append(a[i].name, a[i].value); 5139 } 5140 5141 if (options.extraData) { 5142 var serializedData = deepSerialize(options.extraData); 5143 5144 for (i = 0; i < serializedData.length; i++) { 5145 if (serializedData[i]) { 5146 formdata.append(serializedData[i][0], serializedData[i][1]); 5147 } 5148 } 5149 } 5150 5151 options.data = null; 5152 5153 var s = $.extend(true, {}, $.ajaxSettings, options, { 5154 contentType : false, 5155 processData : false, 5156 cache : false, 5157 type : method || 'POST' 5158 }); 5159 5160 if (options.uploadProgress) { 5161 // workaround because jqXHR does not expose upload property 5162 s.xhr = function() { 5163 var xhr = $.ajaxSettings.xhr(); 5164 5165 if (xhr.upload) { 5166 xhr.upload.addEventListener('progress', function(event) { 5167 var percent = 0; 5168 var position = event.loaded || event.position; /* event.position is deprecated */ 5169 var total = event.total; 5170 5171 if (event.lengthComputable) {
5172 percent = Math.ceil(position / total * 100); 5173 } 5174 5175 options.uploadProgress(event, position, total, percent); 5176 }, false); 5177 } 5178 5179 return xhr; 5180 }; 5181 } 5182 5183 s.data = null; 5184 5185 var beforeSend = s.beforeSend; 5186 5187 s.beforeSend = function(xhr, o) { 5188 // Send FormData() provided by user 5189 if (options.formData) { 5190 o.data = options.formData; 5191 } else { 5192 o.data = formdata; 5193 } 5194 5195 if (beforeSend) { 5196 beforeSend.call(this, xhr, o); 5197 } 5198 }; 5199 5200 return $.ajax(s); 5201 } 5202 5203 // private function for handling file uploads (hat tip to YAHOO!) 5204 function fileUploadIframe(a) { 5205 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 5206 var deferred = $.Deferred(); 5207 5208 // #341 5209 deferred.abort = function(status) { 5210 xhr.abort(status); 5211 }; 5212 5213 if (a) { 5214 // ensure that every serialized input is still enabled 5215 for (i = 0; i < elements.length; i++) { 5216 el = $(elements[i]); 5217 if (hasProp) { 5218 el.prop('disabled', false); 5219 } else { 5220 el.removeAttr('disabled'); 5221 } 5222 } 5223 } 5224 5225 s = $.extend(true, {}, $.ajaxSettings, options); 5226 s.context = s.context || s; 5227 id = 'jqFormIO' + new Date().getTime(); 5228 var ownerDocument = form.ownerDocument; 5229 var $body = $form.closest('body'); 5230 5231 if (s.iframeTarget) { 5232 $io = $(s.iframeTarget, ownerDocument); 5233 n = $io.attr2('name'); 5234 if (!n) { 5235 $io.attr2('name', id); 5236 } else { 5237 id = n; 5238 } 5239 5240 } else { 5241 $io = $('<iframe name="' + id + '" src="' + s.iframeSrc + '" />', ownerDocument); 5242 $io.css({position: 'absolute', top: '-1000px', left: '-1000px'}); 5243 } 5244 io = $io[0]; 5245 5246 5247 xhr = { // mock object 5248 aborted : 0, 5249 responseText : null, 5250 responseXML : null, 5251 status : 0, 5252 statusText : 'n/a', 5253 getAllResponseHeaders : function() {}, 5254 getResponseHeader : function() {}, 5255 setRequestHeader : function() {}, 5256 abort : function(status) { 5257 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 5258 5259 log('aborting upload... ' + e); 5260 this.aborted = 1; 5261 5262 try { // #214, #257 5263 if (io.contentWindow.document.execCommand) { 5264 io.contentWindow.document.execCommand('Stop'); 5265 } 5266 } catch (ignore) {} 5267 5268 $io.attr('src', s.iframeSrc); // abort op in progress 5269 xhr.error = e; 5270 if (s.error) { 5271 s.error.call(s.context, xhr, e, status); 5272 } 5273 5274 if (g) { 5275 $.event.trigger('ajaxError', [xhr, s, e]); 5276 } 5277 5278 if (s.complete) { 5279 s.complete.call(s.context, xhr, e); 5280 } 5281 } 5282 }; 5283 5284 g = s.global; 5285 // trigger ajax global events so that activity/block indicators work like normal 5286 if (g && $.active++ === 0) { 5287 $.event.trigger('ajaxStart'); 5288 } 5289 if (g) { 5290 $.event.trigger('ajaxSend', [xhr, s]); 5291 } 5292 5293 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 5294 if (s.global) { 5295 $.active--; 5296 } 5297 deferred.reject(); 5298 5299 return deferred; 5300 } 5301 5302 if (xhr.aborted) { 5303 deferred.reject(); 5304 5305 return deferred; 5306 } 5307 5308 // add submitting element to data if we know it 5309 sub = form.clk; 5310 if (sub) { 5311 n = sub.name; 5312 if (n && !sub.disabled) { 5313 s.extraData = s.extraData || {}; 5314 s.extraData[n] = sub.value; 5315 if (sub.type === 'image') { 5316 s.extraData[n + '.x'] = form.clk_x; 5317 s.extraData[n + '.y'] = form.clk_y; 5318 } 5319 } 5320 } 5321 5322 var CLIENT_TIMEOUT_ABORT = 1; 5323 var SERVER_ABORT = 2; 5324 5325 function getDoc(frame) { 5326 /* it looks like contentWindow or contentDocument do not 5327 * carry the protocol property in ie8, when running under ssl 5328 * frame.document is the only valid response document, since 5329 * the protocol is know but not on the other two objects. strange? 5330 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy 5331 */ 5332 5333 var doc = null; 5334 5335 // IE8 cascading access check 5336 try { 5337 if (frame.contentWindow) { 5338 doc = frame.contentWindow.document; 5339 } 5340 } catch (err) { 5341 // IE8 access denied under ssl & missing protocol 5342 log('cannot get iframe.contentWindow document: ' + err); 5343 } 5344 5345 if (doc) { // successful getting content 5346 return doc; 5347 } 5348 5349 try { // simply checking may throw in ie8 under ssl or mismatched protocol 5350 doc = frame.contentDocument ? frame.contentDocument : frame.document; 5351 } catch (err) { 5352 // last attempt 5353 log('cannot get iframe.contentDocument: ' + err); 5354 doc = frame.document; 5355 } 5356 5357 return doc; 5358 } 5359 5360 // Rails CSRF hack (thanks to Yvan Barthelemy) 5361 var csrf_token = $('meta[name=csrf-token]').attr('content'); 5362 var csrf_param = $('meta[name=csrf-param]').attr('content'); 5363 5364 if (csrf_param && csrf_token) { 5365 s.extraData = s.extraData || {}; 5366 s.extraData[csrf_param] = csrf_token; 5367 } 5368 5369 // take a breath so that pending repaints get some cpu time before the upload starts 5370 function doSubmit() { 5371 // make sure form attrs are set 5372 var t = $form.attr2('target'), 5373 a = $form.attr2('action'), 5374 mp = 'multipart/form-data', 5375 et = $form.attr('enctype') || $form.attr('encoding') || mp; 5376 5377 // update form attrs in IE friendly way 5378 form.setAttribute('target', id); 5379 if (!method || /post/i.test(method)) { 5380 form.setAttribute('method', 'POST'); 5381 } 5382 if (a !== s.url) { 5383 form.setAttribute('action', s.url); 5384 } 5385 5386 // ie borks in some cases when setting encoding 5387 if (!s.skipEncodingOverride && (!method || /post/i.test(method))) { 5388 $form.attr({ 5389 encoding : 'multipart/form-data', 5390 enctype : 'multipart/form-data' 5391 }); 5392 } 5393 5394 // support timout 5395 if (s.timeout) { 5396 timeoutHandle = setTimeout(function() { 5397 timedOut = true; cb(CLIENT_TIMEOUT_ABORT); 5398 }, s.timeout); 5399 } 5400 5401 // look for server aborts 5402 function checkState() { 5403 try { 5404 var state = getDoc(io).readyState; 5405 5406 log('state = ' + state); 5407 if (state && state.toLowerCase() === 'uninitialized') { 5408 setTimeout(checkState, 50); 5409 } 5410 5411 } catch (e) { 5412 log('Server abort: ', e, ' (', e.name, ')'); 5413 cb(SERVER_ABORT); // eslint-disable-line callback-return 5414 if (timeoutHandle) { 5415 clearTimeout(timeoutHandle); 5416 } 5417 timeoutHandle = undefined; 5418 } 5419 } 5420 5421 // add "extra" data to form if provided in options 5422 var extraInputs = []; 5423 5424 try { 5425 if (s.extraData) { 5426 for (var n in s.extraData) { 5427 if (s.extraData.hasOwnProperty(n)) { 5428 // if using the $.param format that allows for multiple values with the same name 5429 if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { 5430 extraInputs.push( 5431 $('<input type="hidden" name="' + s.extraData[n].name + '">', ownerDocument).val(s.extraData[n].value) 5432 .appendTo(form)[0]); 5433 } else { 5434 extraInputs.push( 5435 $('<input type="hidden" name="' + n + '">', ownerDocument).val(s.extraData[n]) 5436 .appendTo(form)[0]); 5437 } 5438 } 5439 } 5440 } 5441 5442 if (!s.iframeTarget) { 5443 // add iframe to doc and submit the form 5444 $io.appendTo($body); 5445 } 5446 5447 if (io.attachEvent) { 5448 io.attachEvent('onload', cb); 5449 } else { 5450 io.addEventListener('load', cb, false); 5451 } 5452 5453 setTimeout(checkState, 15); 5454 5455 try { 5456 form.submit(); 5457 5458 } catch (err) { 5459 // just in case form has element with name/id of 'submit' 5460 var submitFn = document.createElement('form').submit; 5461 5462 submitFn.apply(form); 5463 } 5464 5465 } finally { 5466 // reset attrs and remove "extra" input elements 5467 form.setAttribute('action', a); 5468 form.setAttribute('enctype', et); // #380 5469 if (t) { 5470 form.setAttribute('target', t); 5471 } else { 5472 $form.removeAttr('target'); 5473 } 5474 $(extraInputs).remove(); 5475 } 5476 } 5477 5478 if (s.forceSync) { 5479 doSubmit(); 5480 } else { 5481 setTimeout(doSubmit, 10); // this lets dom updates render 5482 } 5483 5484 var data, doc, domCheckCount = 50, callbackProcessed; 5485 5486 function cb(e) { 5487 if (xhr.aborted || callbackProcessed) { 5488 return; 5489 } 5490 5491 doc = getDoc(io); 5492 if (!doc) { 5493 log('cannot access response document'); 5494 e = SERVER_ABORT; 5495 } 5496 if (e === CLIENT_TIMEOUT_ABORT && xhr) { 5497 xhr.abort('timeout'); 5498 deferred.reject(xhr, 'timeout'); 5499 5500 return; 5501 5502 } 5503 if (e === SERVER_ABORT && xhr) { 5504 xhr.abort('server abort'); 5505 deferred.reject(xhr, 'error', 'server abort'); 5506 5507 return; 5508 } 5509 5510 if (!doc || doc.location.href === s.iframeSrc) { 5511 // response not received yet 5512 if (!timedOut) { 5513 return; 5514 } 5515 } 5516 5517 if (io.detachEvent) { 5518 io.detachEvent('onload', cb); 5519 } else { 5520 io.removeEventListener('load', cb, false); 5521 } 5522 5523 var status = 'success', errMsg; 5524 5525 try { 5526 if (timedOut) { 5527 throw 'timeout'; 5528 } 5529 5530 var isXml = s.dataType === 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 5531 5532 log('isXml=' + isXml); 5533 5534 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 5535 if (--domCheckCount) { 5536 // in some browsers (Opera) the iframe DOM is not always traversable when 5537 // the onload callback fires, so we loop a bit to accommodate 5538 log('requeing onLoad callback, DOM not available'); 5539 setTimeout(cb, 250); 5540 5541 return; 5542 } 5543 // let this fall through because server response could be an empty document 5544 // log('Could not access iframe DOM after mutiple tries.'); 5545 // throw 'DOMException: not available'; 5546 } 5547 5548 // log('response detected'); 5549 var docRoot = doc.body ? doc.body : doc.documentElement; 5550 5551 xhr.responseText = docRoot ? docRoot.innerHTML : null; 5552 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 5553 if (isXml) { 5554 s.dataType = 'xml'; 5555 } 5556 xhr.getResponseHeader = function(header){ 5557 var headers = {'content-type': s.dataType}; 5558 5559 return headers[header.toLowerCase()]; 5560 }; 5561 // support for XHR 'status' & 'statusText' emulation : 5562 if (docRoot) { 5563 xhr.status = Number(docRoot.getAttribute('status')) || xhr.status; 5564 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 5565 } 5566 5567 var dt = (s.dataType || '').toLowerCase(); 5568 var scr = /(json|script|text)/.test(dt); 5569 5570 if (scr || s.textarea) { 5571 // see if user embedded response in textarea 5572 var ta = doc.getElementsByTagName('textarea')[0]; 5573 5574 if (ta) { 5575 xhr.responseText = ta.value; 5576 // support for XHR 'status' & 'statusText' emulation : 5577 xhr.status = Number(ta.getAttribute('status')) || xhr.status; 5578 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 5579 5580 } else if (scr) { 5581 // account for browsers injecting pre around json response 5582 var pre = doc.getElementsByTagName('pre')[0]; 5583 var b = doc.getElementsByTagName('body')[0]; 5584 5585 if (pre) { 5586 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 5587 } else if (b) { 5588 xhr.responseText = b.textContent ? b.textContent : b.innerText; 5589 } 5590 } 5591 5592 } else if (dt === 'xml' && !xhr.responseXML && xhr.responseText) { 5593 xhr.responseXML = toXml(xhr.responseText); // eslint-disable-line no-use-before-define 5594 } 5595 5596 try { 5597 data = httpData(xhr, dt, s); // eslint-disable-line no-use-before-define 5598 5599 } catch (err) { 5600 status = 'parsererror'; 5601 xhr.error = errMsg = (err || status); 5602 } 5603 5604 } catch (err) { 5605 log('error caught: ', err); 5606 status = 'error'; 5607 xhr.error = errMsg = (err || status); 5608 } 5609 5610 if (xhr.aborted) { 5611 log('upload aborted'); 5612 status = null; 5613 } 5614 5615 if (xhr.status) { // we've set xhr.status 5616 status = ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 'success' : 'error'; 5617 } 5618 5619 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 5620 if (status === 'success') { 5621 if (s.success) { 5622 s.success.call(s.context, data, 'success', xhr); 5623 } 5624 5625 deferred.resolve(xhr.responseText, 'success', xhr); 5626 5627 if (g) {
5628 $.event.trigger('ajaxSuccess', [xhr, s]); 5629 } 5630 5631 } else if (status) { 5632 if (typeof errMsg === 'undefined') { 5633 errMsg = xhr.statusText; 5634 } 5635 if (s.error) { 5636 s.error.call(s.context, xhr, status, errMsg); 5637 } 5638 deferred.reject(xhr, 'error', errMsg); 5639 if (g) { 5640 $.event.trigger('ajaxError', [xhr, s, errMsg]); 5641 } 5642 } 5643 5644 if (g) { 5645 $.event.trigger('ajaxComplete', [xhr, s]); 5646 } 5647 5648 if (g && !--$.active) { 5649 $.event.trigger('ajaxStop'); 5650 } 5651 5652 if (s.complete) { 5653 s.complete.call(s.context, xhr, status); 5654 } 5655 5656 callbackProcessed = true; 5657 if (s.timeout) { 5658 clearTimeout(timeoutHandle); 5659 } 5660 5661 // clean up 5662 setTimeout(function() { 5663 if (!s.iframeTarget) { 5664 $io.remove(); 5665 } else { // adding else to clean up existing iframe response. 5666 $io.attr('src', s.iframeSrc); 5667 } 5668 xhr.responseXML = null; 5669 }, 100); 5670 } 5671 5672 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 5673 if (window.ActiveXObject) { 5674 doc = new ActiveXObject('Microsoft.XMLDOM'); 5675 doc.async = 'false'; 5676 doc.loadXML(s); 5677 5678 } else { 5679 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 5680 } 5681 5682 return (doc && doc.documentElement && doc.documentElement.nodeName !== 'parsererror') ? doc : null; 5683 }; 5684 var parseJSON = $.parseJSON || function(s) { 5685 /* jslint evil:true */ 5686 return window['eval']('(' + s + ')'); // eslint-disable-line dot-notation 5687 }; 5688 5689 var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4 5690 5691 var ct = xhr.getResponseHeader('content-type') || '', 5692 xml = ((type === 'xml' || !type) && ct.indexOf('xml') >= 0), 5693 data = xml ? xhr.responseXML : xhr.responseText; 5694 5695 if (xml && data.documentElement.nodeName === 'parsererror') { 5696 if ($.error) { 5697 $.error('parsererror'); 5698 } 5699 } 5700 if (s && s.dataFilter) { 5701 data = s.dataFilter(data, type); 5702 } 5703 if (typeof data === 'string') { 5704 if ((type === 'json' || !type) && ct.indexOf('json') >= 0) { 5705 data = parseJSON(data); 5706 } else if ((type === 'script' || !type) && ct.indexOf('javascript') >= 0) { 5707 $.globalEval(data); 5708 } 5709 } 5710 5711 return data; 5712 }; 5713 5714 return deferred; 5715 } 5716 }; 5717 5718 /** 5719 * ajaxForm() provides a mechanism for fully automating form submission. 5720 * 5721 * The advantages of using this method instead of ajaxSubmit() are: 5722 * 5723 * 1: This method will include coordinates for <input type="image"> elements (if the element 5724 * is used to submit the form). 5725 * 2. This method will include the submit element's name/value data (for the element that was 5726 * used to submit the form). 5727 * 3. This method binds the submit() method to the form for you. 5728 * 5729 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 5730 * passes the options argument along after properly binding events for submit elements and 5731 * the form itself. 5732 */ 5733 $.fn.ajaxForm = function(options, data, dataType, onSuccess) { 5734 if (typeof options === 'string' || (options === false && arguments.length > 0)) { 5735 options = { 5736 'url' : options, 5737 'data' : data, 5738 'dataType' : dataType 5739 }; 5740 5741 if (typeof onSuccess === 'function') { 5742 options.success = onSuccess; 5743 } 5744 } 5745 5746 options = options || {}; 5747 options.delegation = options.delegation && $.isFunction($.fn.on); 5748 5749 // in jQuery 1.3+ we can fix mistakes with the ready state 5750 if (!options.delegation && this.length === 0) { 5751 var o = {s: this.selector, c: this.context}; 5752 5753 if (!$.isReady && o.s) { 5754 log('DOM not ready, queuing ajaxForm'); 5755 $(function() { 5756 $(o.s, o.c).ajaxForm(options); 5757 }); 5758 5759 return this; 5760 } 5761 5762 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 5763 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 5764 5765 return this; 5766 } 5767 5768 if (options.delegation) { 5769 $(document) 5770 .off('submit.form-plugin', this.selector, doAjaxSubmit) 5771 .off('click.form-plugin', this.selector, captureSubmittingElement) 5772 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 5773 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 5774 5775 return this; 5776 } 5777 5778 if (options.beforeFormUnbind) { 5779 options.beforeFormUnbind(this, options); 5780 } 5781 5782 return this.ajaxFormUnbind() 5783 .on('submit.form-plugin', options, doAjaxSubmit) 5784 .on('click.form-plugin', options, captureSubmittingElement); 5785 }; 5786 5787 // private event handlers 5788 function doAjaxSubmit(e) { 5789 /* jshint validthis:true */ 5790 var options = e.data; 5791 5792 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 5793 e.preventDefault(); 5794 $(e.target).closest('form').ajaxSubmit(options); // #365 5795 } 5796 } 5797 5798 function captureSubmittingElement(e) { 5799 /* jshint validthis:true */ 5800 var target = e.target; 5801 var $el = $(target); 5802 5803 if (!$el.is('[type=submit],[type=image]')) { 5804 // is this a child element of the submit el? (ex: a span within a button) 5805 var t = $el.closest('[type=submit]'); 5806 5807 if (t.length === 0) { 5808 return; 5809 } 5810 target = t[0]; 5811 } 5812 5813 var form = target.form; 5814 5815 form.clk = target; 5816 5817 if (target.type === 'image') { 5818 if (typeof e.offsetX !== 'undefined') { 5819 form.clk_x = e.offsetX; 5820 form.clk_y = e.offsetY; 5821 5822 } else if (typeof $.fn.offset === 'function') { 5823 var offset = $el.offset(); 5824 5825 form.clk_x = e.pageX - offset.left; 5826 form.clk_y = e.pageY - offset.top; 5827 5828 } else { 5829 form.clk_x = e.pageX - target.offsetLeft; 5830 form.clk_y = e.pageY - target.offsetTop; 5831 } 5832 } 5833 // clear form vars 5834 setTimeout(function() { 5835 form.clk = form.clk_x = form.clk_y = null; 5836 }, 100); 5837 } 5838 5839 5840 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 5841 $.fn.ajaxFormUnbind = function() { 5842 return this.off('submit.form-plugin click.form-plugin'); 5843 }; 5844 5845 /** 5846 * formToArray() gathers form element data into an array of objects that can 5847 * be passed to any of the following ajax functions: $.get, $.post, or load. 5848 * Each object in the array has both a 'name' and 'value' property. An example of 5849 * an array for a simple login form might be: 5850 * 5851 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 5852 * 5853 * It is this array that is passed to pre-submit callback functions provided to the 5854 * ajaxSubmit() and ajaxForm() methods. 5855 */ 5856 $.fn.formToArray = function(semantic, elements, filtering) { 5857 var a = []; 5858 5859 if (this.length === 0) { 5860 return a; 5861 } 5862 5863 var form = this[0]; 5864 var formId = this.attr('id'); 5865 var els = (semantic || typeof form.elements === 'undefined') ? form.getElementsByTagName('*') : form.elements; 5866 var els2; 5867 5868 if (els) { 5869 els = $.makeArray(els); // convert to standard array 5870 } 5871 5872 // #386; account for inputs outside the form which use the 'form' attribute 5873 // FinesseRus: in non-IE browsers outside fields are already included in form.elements. 5874 if (formId && (semantic || /(Edge|Trident)\//.test(navigator.userAgent))) { 5875 els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet 5876 if (els2.length) { 5877 els = (els || []).concat(els2); 5878 } 5879 } 5880 5881 if (!els || !els.length) { 5882 return a; 5883 } 5884
5885 if ($.isFunction(filtering)) { 5886 els = $.map(els, filtering); 5887 } 5888 5889 var i, j, n, v, el, max, jmax; 5890 5891 for (i = 0, max = els.length; i < max; i++) { 5892 el = els[i]; 5893 n = el.name; 5894 if (!n || el.disabled) { 5895 continue; 5896 } 5897 5898 if (semantic && form.clk && el.type === 'image') { 5899 // handle image inputs on the fly when semantic == true 5900 if (form.clk === el) { 5901 a.push({name: n, value: $(el).val(), type: el.type}); 5902 a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y}); 5903 } 5904 continue; 5905 } 5906 5907 v = $.fieldValue(el, true); 5908 if (v && v.constructor === Array) { 5909 if (elements) { 5910 elements.push(el); 5911 } 5912 for (j = 0, jmax = v.length; j < jmax; j++) { 5913 a.push({name: n, value: v[j]}); 5914 } 5915 5916 } else if (feature.fileapi && el.type === 'file') { 5917 if (elements) { 5918 elements.push(el); 5919 } 5920 5921 var files = el.files; 5922 5923 if (files.length) { 5924 for (j = 0; j < files.length; j++) { 5925 a.push({name: n, value: files[j], type: el.type}); 5926 } 5927 } else { 5928 // #180 5929 a.push({name: n, value: '', type: el.type}); 5930 } 5931 5932 } else if (v !== null && typeof v !== 'undefined') { 5933 if (elements) { 5934 elements.push(el); 5935 } 5936 a.push({name: n, value: v, type: el.type, required: el.required}); 5937 } 5938 } 5939 5940 if (!semantic && form.clk) { 5941 // input type=='image' are not found in elements array! handle it here 5942 var $input = $(form.clk), input = $input[0]; 5943 5944 n = input.name; 5945 5946 if (n && !input.disabled && input.type === 'image') { 5947 a.push({name: n, value: $input.val()}); 5948 a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y}); 5949 } 5950 } 5951 5952 return a; 5953 }; 5954 5955 /** 5956 * Serializes form data into a 'submittable' string. This method will return a string 5957 * in the format: name1=value1&name2=value2 5958 */ 5959 $.fn.formSerialize = function(semantic) { 5960 // hand off to jQuery.param for proper encoding 5961 return $.param(this.formToArray(semantic)); 5962 }; 5963 5964 /** 5965 * Serializes all field elements in the jQuery object into a query string. 5966 * This method will return a string in the format: name1=value1&name2=value2 5967 */ 5968 $.fn.fieldSerialize = function(successful) { 5969 var a = []; 5970 5971 this.each(function() { 5972 var n = this.name; 5973 5974 if (!n) { 5975 return; 5976 } 5977 5978 var v = $.fieldValue(this, successful); 5979 5980 if (v && v.constructor === Array) { 5981 for (var i = 0, max = v.length; i < max; i++) { 5982 a.push({name: n, value: v[i]}); 5983 } 5984 5985 } else if (v !== null && typeof v !== 'undefined') { 5986 a.push({name: this.name, value: v}); 5987 } 5988 }); 5989 5990 // hand off to jQuery.param for proper encoding 5991 return $.param(a); 5992 }; 5993 5994 /** 5995 * Returns the value(s) of the element in the matched set. For example, consider the following form: 5996 * 5997 * <form><fieldset> 5998 * <input name="A" type="text"> 5999 * <input name="A" type="text"> 6000 * <input name="B" type="checkbox" value="B1"> 6001 * <input name="B" type="checkbox" value="B2"> 6002 * <input name="C" type="radio" value="C1"> 6003 * <input name="C" type="radio" value="C2"> 6004 * </fieldset></form> 6005 * 6006 * var v = $('input[type=text]').fieldValue(); 6007 * // if no values are entered into the text inputs 6008 * v === ['',''] 6009 * // if values entered into the text inputs are 'foo' and 'bar' 6010 * v === ['foo','bar'] 6011 * 6012 * var v = $('input[type=checkbox]').fieldValue(); 6013 * // if neither checkbox is checked 6014 * v === undefined 6015 * // if both checkboxes are checked 6016 * v === ['B1', 'B2'] 6017 * 6018 * var v = $('input[type=radio]').fieldValue(); 6019 * // if neither radio is checked 6020 * v === undefined 6021 * // if first radio is checked 6022 * v === ['C1'] 6023 * 6024 * The successful argument controls whether or not the field element must be 'successful' 6025 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). 6026 * The default value of the successful argument is true. If this value is false the value(s) 6027 * for each element is returned. 6028 * 6029 * Note: This method *always* returns an array. If no valid value can be determined the 6030 * array will be empty, otherwise it will contain one or more values. 6031 */ 6032 $.fn.fieldValue = function(successful) { 6033 for (var val = [], i = 0, max = this.length; i < max; i++) { 6034 var el = this[i]; 6035 var v = $.fieldValue(el, successful); 6036 6037 if (v === null || typeof v === 'undefined' || (v.constructor === Array && !v.length)) { 6038 continue; 6039 } 6040 6041 if (v.constructor === Array) { 6042 $.merge(val, v); 6043 } else { 6044 val.push(v); 6045 } 6046 } 6047 6048 return val; 6049 }; 6050 6051 /** 6052 * Returns the value of the field element. 6053 */ 6054 $.fieldValue = function(el, successful) { 6055 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 6056 6057 if (typeof successful === 'undefined') { 6058 successful = true; 6059 } 6060 6061 /* eslint-disable no-mixed-operators */ 6062 if (successful && (!n || el.disabled || t === 'reset' || t === 'button' || 6063 (t === 'checkbox' || t === 'radio') && !el.checked || 6064 (t === 'submit' || t === 'image') && el.form && el.form.clk !== el || 6065 tag === 'select' && el.selectedIndex === -1)) { 6066 /* eslint-enable no-mixed-operators */ 6067 return null; 6068 } 6069 6070 if (tag === 'select') { 6071 var index = el.selectedIndex; 6072 6073 if (index < 0) { 6074 return null; 6075 } 6076 6077 var a = [], ops = el.options; 6078 var one = (t === 'select-one'); 6079 var max = (one ? index + 1 : ops.length); 6080 6081 for (var i = (one ? index : 0); i < max; i++) { 6082 var op = ops[i]; 6083 6084 if (op.selected && !op.disabled) { 6085 var v = op.value; 6086 6087 if (!v) { // extra pain for IE... 6088 v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; 6089 } 6090 6091 if (one) { 6092 return v; 6093 } 6094 6095 a.push(v); 6096 } 6097 } 6098 6099 return a; 6100 } 6101 6102 return $(el).val().replace(rCRLF, '\r\n'); 6103 }; 6104 6105 /** 6106 * Clears the form data. Takes the following actions on the form's input fields: 6107 * - input text fields will have their 'value' property set to the empty string 6108 * - select elements will have their 'selectedIndex' property set to -1 6109 * - checkbox and radio inputs will have their 'checked' property set to false 6110 * - inputs of type submit, button, reset, and hidden will *not* be effected 6111 * - button elements will *not* be effected 6112 */ 6113 $.fn.clearForm = function(includeHidden) { 6114 return this.each(function() { 6115 $('input,select,textarea', this).clearFields(includeHidden); 6116 }); 6117 }; 6118 6119 /** 6120 * Clears the selected form elements. 6121 */ 6122 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 6123 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 6124 6125 return this.each(function() { 6126 var t = this.type, tag = this.tagName.toLowerCase(); 6127 6128 if (re.test(t) || tag === 'textarea') { 6129 this.value = ''; 6130 6131 } else if (t === 'checkbox' || t === 'radio') { 6132 this.checked = false;
6133 6134 } else if (tag === 'select') { 6135 this.selectedIndex = -1; 6136 6137 } else if (t === 'file') { 6138 if (/MSIE/.test(navigator.userAgent)) { 6139 $(this).replaceWith($(this).clone(true)); 6140 } else { 6141 $(this).val(''); 6142 } 6143 6144 } else if (includeHidden) { 6145 // includeHidden can be the value true, or it can be a selector string 6146 // indicating a special test; for example: 6147 // $('#myForm').clearForm('.special:hidden') 6148 // the above would clean hidden inputs that have the class of 'special' 6149 if ((includeHidden === true && /hidden/.test(t)) || 6150 (typeof includeHidden === 'string' && $(this).is(includeHidden))) { 6151 this.value = ''; 6152 } 6153 } 6154 }); 6155 }; 6156 6157 6158 /** 6159 * Resets the form data or individual elements. Takes the following actions 6160 * on the selected tags: 6161 * - all fields within form elements will be reset to their original value 6162 * - input / textarea / select fields will be reset to their original value 6163 * - option / optgroup fields (for multi-selects) will defaulted individually 6164 * - non-multiple options will find the right select to default 6165 * - label elements will be searched against its 'for' attribute 6166 * - all others will be searched for appropriate children to default 6167 */ 6168 $.fn.resetForm = function() { 6169 return this.each(function() { 6170 var el = $(this); 6171 var tag = this.tagName.toLowerCase(); 6172 6173 switch (tag) { 6174 case 'input': 6175 this.checked = this.defaultChecked; 6176 // fall through 6177 6178 case 'textarea': 6179 this.value = this.defaultValue; 6180 6181 return true; 6182 6183 case 'option': 6184 case 'optgroup': 6185 var select = el.parents('select'); 6186 6187 if (select.length && select[0].multiple) { 6188 if (tag === 'option') { 6189 this.selected = this.defaultSelected; 6190 } else { 6191 el.find('option').resetForm(); 6192 } 6193 } else { 6194 select.resetForm(); 6195 } 6196 6197 return true; 6198 6199 case 'select': 6200 el.find('option').each(function(i) { // eslint-disable-line consistent-return 6201 this.selected = this.defaultSelected; 6202 if (this.defaultSelected && !el[0].multiple) { 6203 el[0].selectedIndex = i; 6204 6205 return false; 6206 } 6207 }); 6208 6209 return true; 6210 6211 case 'label': 6212 var forEl = $(el.attr('for')); 6213 var list = el.find('input,select,textarea'); 6214 6215 if (forEl[0]) { 6216 list.unshift(forEl[0]); 6217 } 6218 6219 list.resetForm(); 6220 6221 return true; 6222 6223 case 'form': 6224 // guard against an input with the name of 'reset' 6225 // note that IE reports the reset function as an 'object' 6226 if (typeof this.reset === 'function' || (typeof this.reset === 'object' && !this.reset.nodeType)) { 6227 this.reset(); 6228 } 6229 6230 return true; 6231 6232 default: 6233 el.find('form,input,label,select,textarea').resetForm(); 6234 6235 return true; 6236 } 6237 }); 6238 }; 6239 6240 /** 6241 * Enables or disables any matching elements. 6242 */ 6243 $.fn.enable = function(b) { 6244 if (typeof b === 'undefined') { 6245 b = true; 6246 } 6247 6248 return this.each(function() { 6249 this.disabled = !b; 6250 }); 6251 }; 6252 6253 /** 6254 * Checks/unchecks any matching checkboxes or radio buttons and 6255 * selects/deselects and matching option elements. 6256 */ 6257 $.fn.selected = function(select) { 6258 if (typeof select === 'undefined') { 6259 select = true; 6260 } 6261 6262 return this.each(function() { 6263 var t = this.type; 6264 6265 if (t === 'checkbox' || t === 'radio') { 6266 this.checked = select; 6267 6268 } else if (this.tagName.toLowerCase() === 'option') { 6269 var $sel = $(this).parent('select'); 6270 6271 if (select && $sel[0] && $sel[0].type === 'select-one') { 6272 // deselect all other options 6273 $sel.find('option').selected(false); 6274 } 6275 6276 this.selected = select; 6277 } 6278 }); 6279 }; 6280 6281 // expose debug var 6282 $.fn.ajaxSubmit.debug = false; 6283 6284 // helper fn for console logging 6285 function log() { 6286 if (!$.fn.ajaxSubmit.debug) { 6287 return; 6288 } 6289 6290 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, ''); 6291 6292 if (window.console && window.console.log) { 6293 window.console.log(msg); 6294 6295 } else if (window.opera && window.opera.postError) { 6296 window.opera.postError(msg); 6297 } 6298 } 6299})); 6300; 6301window.pageHandleResponsive = true; 6302 6303$.when(window.Pages.rwdReady).done(function () { 6304 6305 toggleClassMedia("maxWidth600px", ".cpForm:media(this-max-width: 600px)"); 6306 toggleClassMedia("maxWidth515px", "#FormCenterContent:media(this-max-width: 515px)"); 6307 toggleClassMedia("maxWidth485px", "#FormCenterContent:media(this-max-width: 485px)"); 6308 toggleClassMedia("maxWidth440px", ".cpForm:media(this-max-width: 440px)"); 6309 toggleClassMedia("maxWidth395px", "#FormCenterContent:media(this-max-width: 395px)"); 6310 toggleClassMedia("maxWidth380px", ".cpForm:media(this-max-width: 380px)"); 6311}); 6312
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.