1if (typeof nfForms === 'undefined') var nfForms = []; 2 3let autocompleteSetupInterval; 4 5jQuery(document).ready(() => { 6 if (jQuery('.rmf-gdrp')) jQuery('.rmf-gdrp').hide(); 7 nfForms.forEach(form => { 8 const dateLimit = getDateLimit(form); 9 if (dateLimit > 0) limitDates(dateLimit); 10 }); 11}); 12 13// Add this debug function to check semester data 14function debugSemesterData() { 15 // console.log('ninjaFormDataJS.semestersHTML available:', typeof ninjaFormDataJS.semestersHTML !== 'undefined'); 16 if (typeof ninjaFormDataJS.semestersHTML !== 'undefined') { 17 console.log('semestersHTML content length:', ninjaFormDataJS.semestersHTML.length); 18 console.log('semestersHTML sample:', ninjaFormDataJS.semestersHTML.substring(0, 200)); 19 } 20} 21// End debug code 22 23// ==================== NEW: Program-Campus Mapping Helper Functions ==================== 24 25/** 26 * Get the mapped campuses for a given program value using the program_campus_mapping 27 * from the page configuration. 28 * 29 * @param {string} programValue - The program value to look up 30 * @param {Array} mappingData - The program_campus_mapping array from pageFieldConfig 31 * @returns {Array|null} Array of campus values mapped to the program, or null if not found 32 */ 33function getMappedCampusesForProgram(programValue, mappingData) { 34 if (!mappingData || !Array.isArray(mappingData) || !programValue) { 35 return null; 36 } 37 38 const mapping = mappingData.find(item => item.program === programValue); 39 return mapping && mapping.campuses ? mapping.campuses : null; 40} 41 42/** 43 * Get the program-campus mapping data from the page configuration. 44 * 45 * @returns {Array|null} The program_campus_mapping array or null if not found 46 */ 47function getProgramCampusMappingData() { 48 const pageConfig = ninjaFormDataJS.pageFieldConfig; 49 if (pageConfig && pageConfig.program_campus_mapping && Array.isArray(pageConfig.program_campus_mapping) && pageConfig.program_campus_mapping.length > 0) { 50 return pageConfig.program_campus_mapping; 51 } 52 return null; 53} 54 55/** 56 * Check if a program has campus mapping defined. 57 * 58 * @param {string} programValue - The program value to check 59 * @param {Array} mappingData - The program_campus_mapping array 60 * @returns {boolean} True if the program has a mapping with at least one campus 61 */ 62function hasProgramMapping(programValue, mappingData) { 63 if (!mappingData || !Array.isArray(mappingData) || !programValue) { 64 return false; 65 } 66 const mapping = mappingData.find(item => item.program === programValue); 67 return mapping && mapping.campuses && mapping.campuses.length > 0; 68} 69 70// ==================== END NEW FUNCTIONS ==================== 71 72jQuery(document).on('nfFormReady', (e, layoutView) => { 73 74 // debugSemesterData(); 75 76 const form = nfForms.find(f => layoutView.$el[0].id === `nf-form-${f.id}-cont`); 77 if (typeof form !== 'undefined') { 78 // Set global form context for rule engine 79 window.currentFormContext = form; 80 81 // Initialize global field variables 82 schoolFieldVal = getSchoolSelectField(form); 83 schoolLabelVal = getSchoolLabelField(form); 84 campusFieldVal = getCampusSelectField(form); 85 campusLabelVal = getCampusLabelField(form); 86 campusDataFieldVal = getCampusDataField(form); 87 programFieldVal = getProgramSelectField(form); 88 programLabelVal = getProgramLabelField(form); 89 semesterFieldVal = getSemesterSelectField(form); 90 semesterLabelVal = getSemesterLabelField(form); 91 countryFieldIdVal = getCountryField(form); 92 stateFieldIdVal = getStateField(form); 93 94 const submitButton = getSubmitButton(form); 95 const pageUrlField = getPageUrlField(form); 96 if (typeof pageUrlField !== 'undefined') captureURL(pageUrlField.id); 97 const thankYouPageField = getThankYouPageField(form); 98 if (typeof thankYouPageField !== 'undefined') setThankYouPage(thankYouPageField.id); 99 const srcParamField = getSrcParamTranslatedField(form); 100 if (typeof srcParamField !== 'undefined') getLeadSourceID(srcParamField.id); 101 102 // Fields for dropdowns 103 const schoolField = getSchoolSelectField(form); 104 const schoolLabel = getSchoolLabelField(form); 105 const campusField = getCampusSelectField(form); 106 const campusLabel = getCampusLabelField(form); 107 const campusDataField = getCampusDataField(form); 108 const programField = getProgramSelectField(form); 109 const programLabel = getProgramLabelField(form); 110 const semesterField = getSemesterSelectField(form); 111 const semesterLabel = getSemesterLabelField(form); 112 const countryFieldId = getCountryField(form) 113 const stateFieldId = getStateField(form) 114 115 // Determine the chaining order 116 const useDynamic = useDynamicCampusProgram(form); 117 118 // Handle forms with SCHOOL > PROGRAM > CAMPUS > SEMESTER chain (useDynamic === 'program') 119 console.log('schoolField', schoolField); 120 console.log('schoolLabel', schoolLabel); 121 console.log('campusField', campusField); 122 console.log('campusLabel', campusLabel); 123 console.log('programField', programField); 124 console.log('programLabel', programLabel); 125 console.log('semesterField', semesterField); 126 console.log('semesterLabel', semesterLabel); 127 128 if (typeof schoolField !== 'undefined' && typeof schoolLabel !== 'undefined' && 129 typeof campusField !== 'undefined' && typeof campusLabel !== 'undefined' && 130 typeof programField !== 'undefined' && typeof programLabel !== 'undefined' && 131 typeof semesterField !== 'undefined' && typeof semesterLabel !== 'undefined') { 132 133 if (useDynamic === 'program') {
134 // Show School > Program > Campus > Semester 135 schoolProgramCampusSemesterChain(schoolField.id, schoolLabel.id, programField.id, programLabel.id, campusField.id, campusLabel.id, semesterField.id, semesterLabel.id, countryFieldId, stateFieldId); 136 } else if (useDynamic === 'campus') { 137 // Show School > Campus > Program > Semester 138 schoolCampusProgramSemesterChain(schoolField.id, schoolLabel.id, programField.id, programLabel.id, campusField.id, campusLabel.id, semesterField.id, semesterLabel.id, countryFieldId, stateFieldId); 139 } else { 140 // Non-chained dropdown behavior for school forms - load all options 141 nonChainedSchoolDropDowns(schoolField.id, schoolLabel.id, programField.id, programLabel.id, campusField.id, campusLabel.id, semesterField.id, semesterLabel.id, countryFieldId, stateFieldId); 142 } 143 144 } 145 146 // Handle forms with PROGRAM > CAMPUS > SEMESTER chain (no school) - useDynamic === 'program' 147 else if (typeof campusField !== 'undefined' && typeof campusLabel !== 'undefined' && 148 typeof programField !== 'undefined' && typeof programLabel !== 'undefined' && 149 typeof semesterField !== 'undefined' && typeof semesterLabel !== 'undefined') { 150 151 if (useDynamic === 'program') { 152 // Show program > campus > semester chain 153 programCampusSemesterChainNoSchool(programField.id, programLabel.id, campusField.id, campusLabel.id, semesterField.id, semesterLabel.id, countryFieldId, stateFieldId); 154 } else if (useDynamic === 'campus') { 155 // Show campus > program > semester chain 156 campusProgramSemesterChainNoSchool(programField.id, programLabel.id, campusField.id, campusLabel.id, semesterField.id, semesterLabel.id, countryFieldId, stateFieldId); 157 } else { 158 // Non-chained dropdown behavior 159 nonChainedDropDownsNoSchool(programField.id, programLabel.id, campusField.id, campusLabel.id, semesterField.id, semesterLabel.id, campusDataField, countryFieldId, stateFieldId); 160 } 161 162 const utmProgramField = getUtmProgramField(form); 163 const utmCampusField = getUtmCampusField(form); 164 if (typeof utmCampusField !== 'undefined' && typeof utmCampusField.value !== 'undefined') { 165 selectProgramCampusUTM(programField.id, programLabel.id, campusField.id, campusLabel.id, campusDataField, utmCampusField, utmProgramField, useDynamic); 166 } 167 } 168 169 // Handle forms with only PROGRAM (no campus/semester) 170 else if (typeof programField !== 'undefined' && typeof programLabel !== 'undefined') { 171 const utmProgramField = getUtmProgramField(form); 172 selectProgramUTM(programField.id, programLabel.id, utmProgramField); 173 } 174 // Handle forms with only SCHOOL (no program/campus) 175 else if (typeof schoolField !== 'undefined' && typeof schoolLabel !== 'undefined') { 176 initializeSchoolField(schoolField.id, schoolLabel.id); 177 const utmSchoolField = getUtmSchoolField(form); 178 if (typeof utmSchoolField !== 'undefined') { 179 selectSchoolUTM(schoolField.id, schoolLabel.id, utmSchoolField); 180 } 181 } 182 183 // Pre-select the "Campus of Interest" and "Program of Interest" saved 184 // in the page's TCS Field Config (backend), if set. Both dropdowns are 185 // still populated with their full option lists (see the chain 186 // functions above), so this only sets the *default* selection -- the 187 // visitor remains free to pick a different Campus afterwards, and the 188 // existing Campus change handler will refilter the Program list to 189 // match dynamically. 190 const pageConfigForDefaults = ninjaFormDataJS.pageFieldConfig; 191 if (pageConfigForDefaults && typeof campusField !== 'undefined' && typeof programField !== 'undefined') { 192 setTimeout(function () { 193 const $campusSelectEl = jQuery("#nf-field-" + campusField.id); 194 const $programSelectEl = jQuery("#nf-field-" + programField.id);
195 const defaultCampus = pageConfigForDefaults.campus; 196 const defaultProgram = pageConfigForDefaults.program_of_interest; 197 198 function selectSavedProgram() { 199 if (defaultProgram && $programSelectEl.find('option[value="' + defaultProgram + '"]').length) { 200 $programSelectEl.val(defaultProgram).trigger('change'); 201 } 202 } 203 204 if (useDynamic === 'campus' && defaultCampus && $campusSelectEl.find('option[value="' + defaultCampus + '"]').length) { 205 // Campus-first chain: set the Campus first so its change 206 // handler rebuilds the Program list scoped to it, then 207 // apply the saved Program default from that filtered list. 208 $campusSelectEl.val(defaultCampus).trigger('change'); 209 selectSavedProgram(); 210 } else { 211 // Program-first (or non-chained) forms: keep the original 212 // behavior of selecting the Program first, then also apply 213 // the saved Campus default, if present. 214 selectSavedProgram(); 215 if (defaultCampus && $campusSelectEl.find('option[value="' + defaultCampus + '"]').length) { 216 $campusSelectEl.val(defaultCampus).trigger('change'); 217 } 218 } 219 }, 0); 220 } else if (pageConfigForDefaults && pageConfigForDefaults.program_of_interest && typeof programField !== 'undefined') { 221 // Fallback for forms with a Program field but no Campus field. 222 setTimeout(function () { 223 const $programSelectEl = jQuery("#nf-field-" + programField.id); 224 if ($programSelectEl.find('option[value="' + pageConfigForDefaults.program_of_interest + '"]').length) { 225 $programSelectEl.val(pageConfigForDefaults.program_of_interest).trigger('change'); 226 } 227 }, 0); 228 } 229 230 // Rest of your existing code for other form fields... 231 const timeZoneField = getTimezoneField(form); 232 if (typeof timeZoneField !== 'undefined') bindChangesPartChange(timeZoneField.id, submitButton.id); 233 const countryField = getCountryField(form); 234 const countryZipTypeField = getCountryZipTypeField(form); 235 const zipStatusField = getZipStatusField(form); 236 const zipCodeField = getZipCodeField(form); 237 const postalField = getPostalCodeField(form); 238 const stateField = getStateField(form); 239 const provinceField = getProvinceField(form); 240 const intStateField = getInternationalStateField(form); 241 const cityField = getCityField(form); 242 if (typeof countryField !== 'undefined' && typeof countryZipTypeField !== 'undefined' && 243 typeof zipStatusField !== 'undefined' && typeof zipCodeField !== 'undefined' && 244 typeof postalField !== 'undefined' && typeof stateField !== 'undefined' && 245 typeof intStateField !== 'undefined' && typeof cityField !== 'undefined') { 246 247 validateZipCode(countryField.id, countryZipTypeField.id, zipCodeField.id, postalField.id, stateField.id, provinceField.id, intStateField.id, zipStatusField.id, cityField.id, submitButton.id); 248 autocompleteSetupInterval = setInterval(() => { 249 setupAddressAutocomplete(countryField.id); 250 }); 251 } 252 jQuery(`#nf-form-${form.id}-cont .nf-next`).click(() => { 253 const latField = getLocalLatField(form); 254 const longField = getLocalLongField(form); 255 if (typeof latField !== 'undefined' && typeof longField !== 'undefined') { 256 getLatLong(latField.id, longField.id, submitButton.id); 257 } 258 const countryField = getCountryField(form); 259 if (typeof countryField !== 'undefined') { 260 setupAddressAutocomplete(countryField.id); 261 } 262 if (typeof countryCodeField !== 'undefined') gdprRequirements(countryCodeField.id); 263 }); 264 setEmailValidation(); 265 } 266}); 267 268// ==================== CHAINING FUNCTIONS ==================== 269// Helper function to initialize dropdown with default option 270function initializeDropdownWithDefault(fieldID, defaultText) { 271 const $field = jQuery("#nf-field-" + fieldID); 272 if ($field.length) { 273 $field.find('option').remove(); 274 $field.find('optgroup').remove(); 275 $field.append(`<option value="" label="">${defaultText || '--'}</option>`); 276 } 277} 278 279// Auto-select a dropdown's only real option (i.e. excluding the blank/placeholder 280// option) so the user isn't forced to manually pick a value when there is only one 281// available choice. Used for Program, Campus, and Semester fields. 282function autoSelectSingleOption(fieldID) { 283 if (!fieldID) return false;
284 const $select = jQuery("#nf-field-" + fieldID); 285 if (!$select.length) return false; 286 287 const optionsWithValue = $select.find("option").filter(function () { 288 const val = jQuery(this).val(); 289 return val && val.trim() !== ""; 290 }); 291 292 if (optionsWithValue.length !== 1) return false; 293 294 const onlyValue = optionsWithValue.eq(0).val(); 295 if ($select.val() === onlyValue) return false; // already selected, avoid re-triggering 296 297 optionsWithValue.prop("selected", true); 298 $select.trigger('change'); 299 return true; 300} 301 302// ==================== UPDATED: Filter campuses by program using mapping or data-class fallback ==================== 303 304/** 305 * Filter campus dropdown options based on the selected program. 306 * First tries to use program_campus_mapping from page config. 307 * If no mapping found for the program, falls back to data-class filtering. 308 * 309 * @param {string} programValue - The selected program value 310 * @param {string} campusID - The ID of the campus dropdown field 311 * @param {string} programID - The ID of the program dropdown field (needed for data-class fallback) 312 * @returns {boolean} True if mapping was applied, false if using data-class fallback 313 */ 314function filterCampusesByProgramMapping(programValue, campusID, programID) { 315 const mappingData = getProgramCampusMappingData(); 316 317 // Check if mapping exists for this specific program 318 if (mappingData && programValue) { 319 const mappedCampuses = getMappedCampusesForProgram(programValue, mappingData); 320 321 // If mapping exists for this program, use it 322 if (mappedCampuses !== null && mappedCampuses.length > 0) { 323 // Get the campus dropdown element 324 const $campusSelect = jQuery("#nf-field-" + campusID); 325 326 // Filter campus options based on mapping 327 $campusSelect.find('option').each(function() { 328 const $option = jQuery(this); 329 const optionValue = $option.val(); 330 331 // Skip the default/placeholder option 332 if (optionValue === '') return; 333 334 // Check if this campus is in the mapped campuses list 335 const shouldShow = mappedCampuses.includes(optionValue); 336 337 if (shouldShow) { 338 $option.show(); 339 $option.prop('disabled', false); 340 } else { 341 $option.hide(); 342 if ($option.is(':selected')) { 343 $option.prop('selected', false); 344 } 345 $option.prop('disabled', true); 346 } 347 }); 348 349 // After filtering, check if only one campus remains and auto-select it 350 const visibleOptions = $campusSelect.find('option').filter(function() { 351 return jQuery(this).val() !== '' && jQuery(this).is(':visible'); 352 }); 353 354 if (visibleOptions.length === 1) { 355 visibleOptions.prop('selected', true); 356 $campusSelect.trigger('change'); 357 } 358 359 return true; // Mapping was applied 360 } 361 } 362 363 // No mapping found for this program - FALL BACK TO ORIGINAL data-class filtering 364 if (programValue && programID) { 365 // Get the selected program option to read its data-class attribute 366 const $selectedProgram = jQuery("#nf-field-" + programID + " option:selected"); 367 let optionCampuses = $selectedProgram.attr('data-class'); 368 369 if (typeof optionCampuses !== 'undefined' && optionCampuses) { 370 optionCampuses = optionCampuses.split(' '); 371 const $campusSelect = jQuery("#nf-field-" + campusID); 372 373 $campusSelect.find('option').each(function() { 374 const $option = jQuery(this); 375 const optionValue = $option.val(); 376 377 // Skip the default/placeholder option 378 if (optionValue === '') return; 379 380 // Check if this campus is in the data-class list 381 const shouldShow = optionCampuses.indexOf(optionValue) !== -1; 382 383 if (shouldShow) { 384 $option.show(); 385 $option.prop('disabled', false); 386 } else { 387 $option.hide(); 388 if ($option.is(':selected')) { 389 $option.prop('selected', false); 390 } 391 $option.prop('disabled', true); 392 } 393 }); 394 395 // After filtering, check if only one campus remains and auto-select it 396 const visibleOptions = $campusSelect.find('option').filter(function() { 397 return jQuery(this).val() !== '' && jQuery(this).is(':visible'); 398 }); 399 400 if (visibleOptions.length === 1) { 401 visibleOptions.prop('selected', true); 402 $campusSelect.trigger('change'); 403 } 404 405 return false; // Using data-class fallback 406 } 407 } 408 409 // If no filtering method works, show all campuses 410 const $campusSelect = jQuery("#nf-field-" + campusID); 411 $campusSelect.find('option').show(); 412 $campusSelect.find('option').prop('disabled', false); 413 414 return false;
415} 416 417/** 418 * Filter programs based on selected campus using program_campus_mapping. 419 * Used in campus-first chains. Falls back to original behavior if no mapping. 420 * 421 * @param {string} campusValue - The selected campus value 422 * @param {string} programID - The ID of the program dropdown field 423 * @returns {boolean} True if mapping was applied, false if using fallback 424 */ 425function filterProgramsByCampusMapping(campusValue, programID) { 426 const mappingData = getProgramCampusMappingData(); 427 const $programSelect = jQuery("#nf-field-" + programID); 428 let hasMappingForAnyProgram = false; 429 430 // First, check if any program has mapping defined 431 if (mappingData) { 432 $programSelect.find('option').each(function() { 433 const $option = jQuery(this); 434 const optionValue = $option.val(); 435 436 // Skip the default/placeholder option 437 if (optionValue === '') return; 438 439 // Get mapped campuses for this program 440 const mappedCampuses = getMappedCampusesForProgram(optionValue, mappingData); 441 442 // If this program has mapping defined 443 if (mappedCampuses !== null && mappedCampuses.length > 0) { 444 hasMappingForAnyProgram = true; 445 // Show only if mapping includes the selected campus 446 const shouldShow = mappedCampuses.includes(campusValue); 447 if (shouldShow) { 448 $option.show(); 449 $option.prop('disabled', false); 450 } else { 451 $option.hide(); 452 if ($option.is(':selected')) { 453 $option.prop('selected', false); 454 } 455 $option.prop('disabled', true); 456 } 457 } 458 }); 459 } 460 461 // If no programs have mapping defined, fall back to original data-class filtering 462 if (!hasMappingForAnyProgram) { 463 // Reset all options to visible first 464 $programSelect.find('option').show(); 465 $programSelect.find('option').prop('disabled', false); 466 467 // Now apply data-class filtering 468 $programSelect.find('option').each(function() { 469 const $option = jQuery(this); 470 const optionValue = $option.val(); 471 472 // Skip the default/placeholder option 473 if (optionValue === '') return; 474 475 // Get the data-class attribute of this program option 476 const optionCampuses = ($option.attr('data-class') || '').split(' ').filter(
476Boolean); 477 478 // If the program has data-class and the selected campus is in it, keep it 479 // If the program has no data-class (empty), show it (fallback) 480 if (optionCampuses.length > 0) { 481 const shouldShow = optionCampuses.indexOf(campusValue) !== -1; 482 if (!shouldShow) { 483 $option.hide(); 484 if ($option.is(':selected')) { 485 $option.prop('selected', false); 486 } 487 $option.prop('disabled', true); 488 } 489 } 490 // If no data-class, keep it visible (fallback) 491 }); 492 493 return false; 494 } 495 496 return true; 497} 498 499// =========================================================================================================================== 500 501// UNIVERSAL RULE PROCESSOR (Supports "if", AND/OR after-if, before-if definitions) 502 503window.NF_DO_NOT_SUBMIT = false; 504window.NF_DO_NOT_SUBMIT_MSG = ''; 505window.currentFormContext = null; // Store current form context 506 507// These will be set when form is ready 508let schoolFieldVal, schoolLabelVal, campusFieldVal, campusLabelVal, campusDataFieldVal; 509let programFieldVal, programLabelVal, semesterFieldVal, semesterLabelVal; 510let countryFieldIdVal, stateFieldIdVal; 511 512function enforceDoNotSubmit() { 513 if (!window.currentFormContext) return false; 514 515 const form = window.currentFormContext; 516 const submitBtnId = getSubmitButton(form)?.id; 517 if (!submitBtnId) return false; 518 519 const $submitBtn = jQuery('#nf-field-' + submitBtnId); 520 521 if (!$submitBtn.length) return false; 522 523 if (window.NF_DO_NOT_SUBMIT) { 524 $submitBtn.prop('disabled', true).attr('aria-disabled', 'true').addClass('nf-disabled'); 525 showDO_NOT_SUBMIT_error(submitBtnId, window.NF_DO_NOT_SUBMIT_MSG); 526 } else { 527 $submitBtn.prop('disabled', false).removeAttr('aria-disabled').removeClass('nf-disabled'); 528 hideDO_NOT_SUBMIT_error(submitBtnId); 529 } 530 531 return true; 532} 533 534function runRuleEngineAfterChange() { 535 console.log('runRuleEngineAfterChange'); 536 537 const values = { 538 program: sessionStorage.getItem("nf_program") || jQuery("#nf-field-" + programFieldVal?.id)?.val(), 539 campus: sessionStorage.getItem("nf_campus") || jQuery("#nf-field-" + campusFieldVal?.id)?.val(), 540 semester: sessionStorage.getItem("nf_semester") || jQuery("#nf-field-" + semesterFieldVal?.id)?.val(), 541 country: sessionStorage.getItem("nf_country") || jQuery("#nf-field-" + countryFieldIdVal?.id)?.val(), 542 state: sessionStorage.getItem("nf_state") || jQuery("#nf-field-" + stateFieldIdVal?.id)?.val(), 543 }; 544 545 const fieldIDs = { 546 program: programFieldVal?.id, 547 campus: campusFieldVal?.id, 548 semester: semesterFieldVal?.id, 549 country: countryFieldIdVal?.id, 550 state: stateFieldIdVal?.id 551 }; 552 553 // STEP 1: RESET everything first 554 resetRuleEffects(fieldIDs); 555 556 // STEP 2: Apply rules fresh 557 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 558} 559 560var DoNotSubmitController = Backbone.Model.extend({ 561 initialize: function () { 562 // Step change (multi-part) 563 this.listenTo(nfRadio.channel('nfMP'), 'change:part', this.enforce); 564 565 // Submit button rendered / re-rendered 566 this.listenTo(nfRadio.channel('forms'), 'submit:ready', this.enforce); 567 }, 568 569 enforce: function () { 570 // Set up field change handlers once 571 if (countryFieldIdVal?.id) { 572 jQuery("#nf-field-" + countryFieldIdVal.id).off('change').on('change', function () { 573 runRuleEngineAfterChange(); 574 console.log('country change') 575 }); 576 } 577 578 if (stateFieldIdVal?.id) { 579 jQuery("#nf-field-" + stateFieldIdVal.id).off('change').on('change', function () { 580 runRuleEngineAfterChange(); 581 console.log('state change') 582 }); 583 } 584 585 // Run rule engine once 586 runRuleEngineAfterChange(); 587 } 588}); 589 590jQuery(document).ready(function () { 591 if (typeof nfRadio !== 'undefined') { 592 console.log("Page Change") 593 new DoNotSubmitController(); 594 } 595}); 596 597jQuery(document).on('nfFormReady', (e, layoutView) => { 598 // Run once when form is ready 599 setTimeout(enforceDoNotSubmit, 300); 600 601 // Re-check every time ANY field changes value 602 Backbone.Radio.channel('fields').on('change:modelValue', function (model) { 603 enforceDoNotSubmit(); 604 }); 605 606 // Re-check on part (page) change — very common source of re-enabling 607 const mpChannel = Backbone.Radio.channel('nfMP'); 608 mpChannel.on('change:part', function () { 609 setTimeout(enforceDoNotSubmit, 150); 610 }); 611 612 // Also useful in many Ninja Forms setups: 613 jQuery(document).on('change', '.ninja-forms-field', function () { 614 setTimeout(enforceDoNotSubmit, 100); 615 }); 616 617 // Bonus: after validation run 618 Backbone.Radio.channel('submit').on('validate:field', function () { 619 setTimeout(enforceDoNotSubmit, 120); 620 }); 621}); 622 623function beforeIFCompleted(beforeIF, values) { 624 console.log('beforeIF', beforeIF) 625 console.log('values', values) 626 if (!beforeIF.length) return false;
627 628 let status = false; 629 let first = true; 630 631 beforeIF.forEach(act => { 632 633 const valueMatch = (values[act.rule_sub_field] == act.rule_sub_value); 634 635 // FIRST ITEM (do-not-submit block) 636 if (first) { 637 status = valueMatch; 638 first = false; 639 return; 640 } 641 642 // OR logic 643 if (act.rule_sub_function === "or") { 644 status = status || valueMatch; 645 } 646 647 // AND logic 648 if (act.rule_sub_function === "and") { 649 status = status && valueMatch; 650 } 651 }); 652 653 return status; 654} 655 656function processRules(rules, values, fieldIDs) { 657 console.log('Rules', rules); 658 console.log('values', values); 659 console.log('fieldIDs', fieldIDs); 660 661 Object.keys(rules).forEach(ruleId => { 662 663 const rule = rules[ruleId]; 664 const nested = rule.rule_nested_function || []; 665 666 let beforeIF = []; 667 let afterIF = []; 668 let mainIF = null; 669 let foundIF = false; 670 671 // Always push main action (hide/show) 672 beforeIF.push({ 673 rule_sub_function: rule.rule_function, 674 rule_sub_field: rule.rule_field, 675 rule_sub_value: rule.rule_value 676 }); 677 678 // SPLIT: beforeIF / IF / afterIF 679 nested.forEach(item => { 680 if (item.rule_sub_function === "if") { 681 foundIF = true; 682 mainIF = item; 683 afterIF.push(item); // IF stays inside afterIF group 684 } else if (!foundIF) { 685 beforeIF.push(item); // multiple hide actions here 686 } else { 687 afterIF.push(item); // OR & AND go here 688 } 689 }); 690 691 if (!mainIF) return; 692 693 let status = false; // default OR-logic safe start 694 let first = true; 695 696 afterIF.forEach(item => { 697 698 if (item.rule_sub_function === "if") { 699 status = (values[item.rule_sub_field] == item.rule_sub_value); 700 first = false; 701 } 702 703 if (item.rule_sub_function === "or") { 704 status = status || (values[item.rule_sub_field] == item.rule_sub_value); 705 } 706 707 if (item.rule_sub_function === "and") { 708 status = status && (values[item.rule_sub_field] == item.rule_sub_value); 709 } 710 711 }); 712 713 // APPLY FINAL RESULT TO ALL hide actions 714 if (status && rule.rule_function === "hide") { 715 console.log('hide'); 716 beforeIF.forEach(act => { 717 hideField(fieldIDs[act.rule_sub_field], act.rule_sub_value); 718 }); 719 } 720 else if (status && rule.rule_function === "do-not-submit") { 721 console.log('do not submit'); 722 723 // Check: BeforeIF fields also must be selected 724 const beforeDone = beforeIFCompleted(beforeIF, values); 725 console.log('beforeDone', beforeDone); 726 727 if (beforeDone) { 728 console.log('iff do not submit'); 729 const form = window.currentFormContext; 730 if (!form) return; 731 const submitBtnId = getSubmitButton(form).id; 732 const $submitBtn = jQuery('#nf-field-' + submitBtnId); 733 734 window.NF_DO_NOT_SUBMIT = true; 735 window.NF_DO_NOT_SUBMIT_MSG = rule?.rule_dns_msg || "You cannot submit this form."; 736 737 $submitBtn.prop('disabled', true).addClass('nf-disabled'); 738 739 const errorMsg = rule.rule_dns_msg || "You cannot submit this form."; 740 showDO_NOT_SUBMIT_error(submitBtnId, errorMsg); 741 742 } else { 743 console.log('else do not submit'); 744 // Before fields NOT completed → keep button enabled 745 const form = window.currentFormContext; 746 if (!form) return; 747 const submitBtnId = getSubmitButton(form).id; 748 const $submitBtn = jQuery('#nf-field-' + submitBtnId); 749 750 window.NF_DO_NOT_SUBMIT = false;
751 window.NF_DO_NOT_SUBMIT_MSG = ""; 752 $submitBtn.prop('disabled', false).removeClass('nf-disabled'); 753 hideDO_NOT_SUBMIT_error(submitBtnId); 754 } 755 } 756 757 }); 758} 759 760function showDO_NOT_SUBMIT_error(fieldID, message) { 761 let $field = jQuery('#nf-field-' + fieldID); 762 let $container = $field.closest('.submit-container'); 763 764 // Remove old message if exists 765 $container.find('.rule-error').remove(); 766 767 // Add new message 768 $container.append( 769 `<div class="rule-error" style="color:#c00; margin-top:5px; font-size:13px;">${message}</div>` 770 ); 771} 772 773function hideDO_NOT_SUBMIT_error(fieldID) { 774 let $field = jQuery('#nf-field-' + fieldID); 775 let $container = $field.closest('.submit-container'); 776 777 // Remove old message if exists 778 $container.find('.rule-error').remove(); 779} 780 781function hideField(fieldId, fieldValue) { 782 const $el = jQuery('#nf-field-' + fieldId); 783 784 const oldVal = $el.val(); 785 786 // Remove option 787 const removed = $el.find('option[value="' + fieldValue + '"]'); 788 if (!removed.length) return; // nothing to do 789 790 // removed.remove(); 791 // removed.hide(); 792 removed.prop('disabled', true); 793 794 // If the removed value was selected → reset value & trigger change 795 if (oldVal == fieldValue) { 796 $el.val(''); 797 $el.trigger('change'); 798 $el.find('option[value="' + fieldValue + '"]').prop('disabled', true); 799 } 800} 801 802function showField(fieldName) { 803 const el = jQuery('#nf-field-' + fieldName); 804 el.find('option').show(); // show all again 805 el.find('option').prop('disabled', false); 806} 807 808function resetRuleEffects(fieldIDs) { 809 // Re-enable all options in all rule fields 810 Object.values(fieldIDs).forEach(fieldId => { 811 const $el = jQuery('#nf-field-' + fieldId); 812 if ($el.length) { 813 $el.find('option').prop('disabled', false); 814 } 815 }); 816 817 // Enable submit button 818 if (window.currentFormContext) { 819 const submitBtnId = getSubmitButton(window.currentFormContext).id; 820 const $submitBtn = jQuery('#nf-field-' + submitBtnId); 821 $submitBtn.prop('disabled', false).removeClass('nf-disabled'); 822 hideDO_NOT_SUBMIT_error(submitBtnId); 823 } 824} 825 826// =========================================================================================================================== 827 828// SCHOOL > PROGRAM > CAMPUS > SEMESTER chain (useDynamic === 'program') 829function schoolProgramCampusSemesterChain(schoolID, schoolLabelID, programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID, countryFieldId, stateFieldId) { 830 // Initialize all dropdowns with appropriate default options 831 initializeDropdownWithDefault(schoolID, 'Choose School'); 832 initializeDropdownWithDefault(programID, '--'); 833 initializeDropdownWithDefault(campusID, '--'); 834 initializeDropdownWithDefault(semesterID, '--'); 835 836 // Initialize school field (no default option needed as it should show all schools) 837 loadSchoolOptions(schoolID, schoolLabelID); 838 839 // School -> Program filtering 840 jQuery("#nf-field-" + schoolID).change(function () { 841 const schoolValue = jQuery(this).val(); 842 843 // Reset program dropdown with default option 844 initializeDropdownWithDefault(programID, 'Choose Program'); 845 846 if (schoolValue) { 847 jQuery("#nf-field-" + programID).append(ninjaFormDataJS.programsHTML); 848 849 // Filter programs based on selected school - USING data-school-id 850 jQuery("#nf-field-" + programID + " option").each((index, option) => { 851 let optionSchoolId = jQuery(option).attr('data-school-id'); 852 853 if (typeof optionSchoolId !== 'undefined' && schoolValue !== '' && option.value !== '') { 854 // Compare the school ID directly 855 if (optionSchoolId !== schoolValue) { 856 jQuery(option).remove(); 857 } 858 } 859 }); 860 861 jQuery("#nf-field-" + programID + " optgroup").each((index, option) => { 862 if (jQuery(option).is(':empty')) { 863 jQuery(option).remove(); 864 } 865 }); 866 } 867 868 // Reset dependent fields to default (Campus and Semester) 869 initializeDropdownWithDefault(campusID, '--'); 870 initializeDropdownWithDefault(semesterID, '--'); 871 872 const schoolLabel = jQuery("#nf-field-" + schoolID + " option:selected").text(); 873 jQuery("#nf-field-" + schoolLabelID).val(schoolLabel).trigger('change'); 874 875 // If only one Program remains available, auto-select it instead of 876 // requiring the user to choose it manually. 877 autoSelectSingleOption(programID); 878 879 runRuleEngine(); 880 }); 881 882 // Program -> Campus filtering - UPDATED to use program_campus_mapping with fallback 883 jQuery("#nf-field-" + programID).change(function () { 884 const programValue = jQuery(this).val(); 885 886 // Reset campus dropdown with default option 887 initializeDropdownWithDefault(campusID, 'Choose Campus'); 888 889 if (programValue) { 890 jQuery("#nf-field-" + campusID).append(ninjaFormDataJS.campusesHTML); 891 892 // Try to filter using mapping, fallback to data-class if no mapping 893 filterCampusesByProgramMapping(programValue, campusID, programID); 894 895 jQuery("#nf-field-" + campusID + " optgroup").each((index, option) => { 896 if (jQuery(option).is(':empty')) { 897 jQuery(option).remove(); 898 } 899 }); 900 } 901 902 // Reset dependent field (Semester) to default 903 initializeDropdownWithDefault(semesterID, '--'); 904 905 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 906 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 907 908 // If only one Campus remains available, auto-select it instead of 909 // requiring the user to choose it manually. 910 autoSelectSingleOption(campusID); 911 912 runRuleEngine(); 913 }); 914 915 // Campus -> Semester filtering - FIXED VERSION 916 jQuery("#nf-field-" + campusID).change(function () { 917 const campusValue = jQuery(this).val(); 918 const programValue = jQuery("#nf-field-" + programID).val(); // Get current program value 919 920 // Reset semester dropdown with default option 921 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 922 923 if (campusValue && programValue) { 924 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 925 926 // Filter semesters based on selected program AND campus 927 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 928 let optionSemesters = jQuery(option).attr('data-class'); 929 930 if (typeof optionSemesters !== 'undefined' && campusValue !== '' && programValue !== '') { 931 // The data-class contains pairs like "67-104 67-802" 932 // We need to check if any pair matches the selected program and campus 933 const semesterPairs = optionSemesters.split(' '); 934 let shouldKeep = false;
935 936 semesterPairs.forEach(pair => { 937 const [programId, campusId] = pair.split('-'); 938 if (programId === programValue && campusId === campusValue) { 939 shouldKeep = true; 940 } 941 }); 942 943 if (!shouldKeep && option.value !== '') { 944 jQuery(option).remove(); 945 } 946 } 947 }); 948 949 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 950 if (jQuery(option).is(':empty')) { 951 jQuery(option).remove(); 952 } 953 }); 954 955 singleSemesterSelect(semesterID, campusID); 956 } 957 958 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 959 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 960 runRuleEngine(); 961 }); 962 963 // Semester change handler 964 jQuery("#nf-field-" + semesterID).change(function () { 965 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 966 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 967 runRuleEngine(); 968 }); 969 970 jQuery("#nf-field-" + countryFieldId?.id).change(function () { 971 runRuleEngine(); 972 }) 973 jQuery("#nf-field-" + stateFieldId?.id).change(function () { 974 runRuleEngine(); 975 }) 976 977 function runRuleEngine() { 978 979 const values = { 980 school: jQuery("#nf-field-" + schoolID).val(), 981 program: jQuery("#nf-field-" + programID).val(), 982 campus: jQuery("#nf-field-" + campusID).val(), 983 semester: jQuery("#nf-field-" + semesterID).val(), 984 country: jQuery("#nf-field-" + countryFieldId?.id)?.val(), 985 state: jQuery("#nf-field-" + stateFieldId?.id)?.val(), 986 }; 987 988 const fieldIDs = { 989 school: schoolID, 990 program: programID, 991 campus: campusID, 992 semester: semesterID, 993 country: countryFieldId?.id, 994 state: stateFieldId?.id 995 }; 996 997 // STEP 1: RESET everything first 998 resetRuleEffects(fieldIDs); 999 1000 // STEP 2: Apply rules fresh 1001 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 1002 } 1003 1004 runRuleEngine(); // run once on load 1005} 1006 1007// ========================================================================================================================================================================================= 1008 1009// SCHOOL > CAMPUS > PROGRAM > SEMESTER chain (useDynamic === 'campus') 1010function schoolCampusProgramSemesterChain(schoolID, schoolLabelID, programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID, countryFieldId, stateFieldId) { 1011 // Initialize all dropdowns with appropriate default options 1012 initializeDropdownWithDefault(schoolID, 'Choose School'); 1013 initializeDropdownWithDefault(programID, '--'); 1014 initializeDropdownWithDefault(campusID, '--'); 1015 initializeDropdownWithDefault(semesterID, '--'); 1016 1017 // Initialize school field 1018 loadSchoolOptions(schoolID, schoolLabelID); 1019 1020 // School -> Campus filtering 1021 jQuery("#nf-field-" + schoolID).change(function () { 1022 const schoolValue = jQuery(this).val(); 1023 1024 // Reset campus dropdown with default option 1025 initializeDropdownWithDefault(campusID, 'Choose Campus'); 1026 1027 if (schoolValue) { 1028 jQuery("#nf-field-" + campusID).append(ninjaFormDataJS.campusesHTML); 1029 1030 // Filter campuses based on selected school - USING data-school-id 1031 jQuery("#nf-field-" + campusID + " option").each((index, option) => { 1032 let optionSchoolId = jQuery(option).attr('data-school-id'); 1033 1034 if (typeof optionSchoolId !== 'undefined' && schoolValue !== '' && option.value !== '') { 1035 // Compare the school ID directly 1036 if (optionSchoolId !== schoolValue) { 1037 jQuery(option).remove(); 1038 } 1039 } 1040 }); 1041 1042 jQuery("#nf-field-" + campusID + " optgroup").each((index, option) => { 1043 if (jQuery(option).is(':empty')) { 1044 jQuery(option).remove(); 1045 } 1046 }); 1047 } 1048 1049 // Reset dependent fields to default (Program and Semester) 1050 initializeDropdownWithDefault(programID, '--'); 1051 initializeDropdownWithDefault(semesterID, '--'); 1052 1053 const schoolLabel = jQuery("#nf-field-" + schoolID + " option:selected").text(); 1054 jQuery("#nf-field-" + schoolLabelID).val(schoolLabel).trigger('change'); 1055 1056 // If only one Campus remains available, auto-select it instead of 1057 // requiring the user to choose it manually. 1058 autoSelectSingleOption(campusID); 1059 1060 runRuleEngine(); 1061 }); 1062 1063 // Campus -> Program filtering - UPDATED to use program_campus_mapping 1064 jQuery("#nf-field-" + campusID).change(function () { 1065 const campusValue = jQuery(this).val(); 1066 1067 // Reset program dropdown with default option 1068 initializeDropdownWithDefault(programID, 'Choose Program'); 1069 1070 if (campusValue) { 1071 jQuery("#nf-field-" + programID).append(ninjaFormDataJS.programsHTML); 1072 1073 // Try to filter programs using mapping 1074 const mappingApplied = filterProgramsByCampusMapping(campusValue, programID); 1075 1076 // If no mapping was applied, show all programs 1077 if (!mappingApplied) { 1078 // Filter programs based on selected campus 1079 jQuery("#nf-field-" + programID + " option").each((index, option) => { 1080 let optionCampuses = jQuery(option).attr('data-class'); 1081 if (typeof optionCampuses !== 'undefined' && campusValue !== '') { 1082 optionCampuses = optionCampuses.split(' '); 1083 if (optionCampuses.indexOf(campusValue) === -1 && option.value !== '') { 1084 jQuery(option).remove(); 1085 } 1086 } 1087 }); 1088 } 1089 1090 jQuery("#nf-field-" + programID + " optgroup").each((index, option) => { 1091 if (jQuery(option).is(':empty')) { 1092 jQuery(option).remove(); 1093 } 1094 }); 1095 } 1096 1097 // Reset dependent field (Semester) to default 1098 initializeDropdownWithDefault(semesterID, '--'); 1099 1100 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1101 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1102 1103 // If only one Program remains available, auto-select it instead of 1104 // requiring the user to choose it manually. 1105 autoSelectSingleOption(programID); 1106 1107 runRuleEngine(); 1108 }); 1109 1110 // Program -> Semester filtering 1111 jQuery("#nf-field-" + programID).change(function () { 1112 const programValue = jQuery(this).val(); 1113 const campusValue = jQuery("#nf-field-" + campusID).val(); 1114 1115 // Reset semester dropdown with default option 1116 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1117 1118 if (programValue && campusValue) { 1119 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 1120 1121 // Filter semesters based on selected program AND campus 1122 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 1123 let optionSemesters = jQuery(option).attr('data-class'); 1124 let optionPrograms = jQuery(option).attr('data-program-solo-id'); 1125 1126 let shouldRemove = false;
1127 1128 // Filter by campus and program using data-class (format: "program-campus" pairs) 1129 if (typeof optionSemesters !== 'undefined' && campusValue !== '' && programValue !== '') { 1130 const semesterPairs = optionSemesters.split(' '); 1131 let hasMatchingPair = false; 1132 1133 semesterPairs.forEach(pair => { 1134 const [programId, campusId] = pair.split('-'); 1135 if (programId === programValue && campusId === campusValue) { 1136 hasMatchingPair = true; 1137 } 1138 }); 1139 1140 if (!hasMatchingPair && option.value !== '') { 1141 shouldRemove = true; 1142 } 1143 } 1144 1145 // Additional filter by program using data-program-solo-id 1146 if (!shouldRemove && typeof optionPrograms !== 'undefined' && programValue !== '') { 1147 optionPrograms = optionPrograms.split(' '); 1148 if (optionPrograms.indexOf(programValue) === -1 && option.value !== '') { 1149 shouldRemove = true; 1150 } 1151 } 1152 1153 if (shouldRemove) { 1154 jQuery(option).remove(); 1155 } 1156 }); 1157 1158 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 1159 if (jQuery(option).is(':empty')) { 1160 jQuery(option).remove(); 1161 } 1162 }); 1163 1164 singleSemesterSelect(semesterID, campusID); 1165 } 1166 1167 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1168 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1169 runRuleEngine(); 1170 }); 1171 1172 // Semester change handler 1173 jQuery("#nf-field-" + semesterID).change(function () { 1174 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 1175 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 1176 runRuleEngine(); 1177 }); 1178 1179 jQuery("#nf-field-" + countryFieldId?.id).change(function () { 1180 runRuleEngine(); 1181 }) 1182 jQuery("#nf-field-" + stateFieldId?.id).change(function () { 1183 runRuleEngine(); 1184 }) 1185 1186 function runRuleEngine() { 1187 1188 const values = { 1189 school: jQuery("#nf-field-" + schoolID).val(), 1190 program: jQuery("#nf-field-" + programID).val(), 1191 campus: jQuery("#nf-field-" + campusID).val(), 1192 semester: jQuery("#nf-field-" + semesterID).val(), 1193 country: jQuery("#nf-field-" + countryFieldId?.id)?.val(), 1194 state: jQuery("#nf-field-" + stateFieldId?.id)?.val(), 1195 }; 1196 1197 const fieldIDs = { 1198 school: schoolID, 1199 program: programID, 1200 campus: campusID, 1201 semester: semesterID, 1202 country: countryFieldId?.id, 1203 state: stateFieldId?.id 1204 }; 1205 1206 // STEP 1: RESET everything first 1207 resetRuleEffects(fieldIDs); 1208 1209 // STEP 2: Apply rules fresh 1210 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 1211 } 1212 1213 runRuleEngine(); // run once on load 1214} 1215 1216// PROGRAM > CAMPUS > SEMESTER chain (no school, useDynamic === 'program') - UPDATED 1217function programCampusSemesterChainNoSchool(programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID, countryFieldId, stateFieldId) { 1218 console.log('programCampusSemesterChainNoSchool'); 1219 1220 // Initialize program field with all options 1221 const $programSelect = jQuery('#nf-field-' + programID); 1222 $programSelect.find('option').remove(); 1223 $programSelect.find('optgroup').remove(); 1224 $programSelect.append('<option value="" label="">Choose Program</option>'); 1225 $programSelect.append(ninjaFormDataJS.programsHTML); 1226 1227 // Initialize other fields with default options 1228 initializeDropdownWithDefault(campusID, '--'); 1229 initializeDropdownWithDefault(semesterID, '--'); 1230 1231 // Program -> Campus filtering - UPDATED to use program_campus_mapping with fallback 1232 jQuery("#nf-field-" + programID).change(function () { 1233 const programValue = jQuery(this).val(); 1234 1235 // Reset campus dropdown with default option 1236 initializeDropdownWithDefault(campusID, 'Choose Campus'); 1237 // Reset semester dropdown with default option 1238 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1239 1240 if (programValue) { 1241 jQuery("#nf-field-" + campusID).append(ninjaFormDataJS.campusesHTML); 1242 1243 // Try to filter using mapping, fallback to data-class if no mapping 1244 filterCampusesByProgramMapping(programValue, campusID, programID); 1245 1246 jQuery("#nf-field-" + campusID + " optgroup").each((index, option) => { 1247 if (jQuery(option).is(':empty')) { 1248 jQuery(option).remove(); 1249 } 1250 }); 1251 1252 // Populate Semester filtered by the selected Program alone 1253 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 1254 1255 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 1256 if (option.value === '') return; 1257 const optionPrograms = (jQuery(option).attr('data-program-solo-id') || '').split(' ').filter(
1257Boolean); 1258 if (optionPrograms.indexOf(programValue) === -1) { 1259 jQuery(option).remove(); 1260 } 1261 }); 1262 1263 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 1264 if (jQuery(option).is(':empty')) { 1265 jQuery(option).remove(); 1266 } 1267 }); 1268 } 1269 1270 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1271 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1272 1273 // If only one Campus remains available, auto-select it instead of 1274 // requiring the user to choose it manually. 1275 autoSelectSingleOption(campusID); 1276 1277 runRuleEngine(); 1278 }); 1279 1280 // Campus -> Semester filtering 1281 jQuery("#nf-field-" + campusID).change(function () { 1282 const campusValue = jQuery(this).val(); 1283 const programValue = jQuery("#nf-field-" + programID).val(); // Get current program value 1284 1285 // Reset semester dropdown with default option 1286 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1287 1288 if (campusValue && programValue) { 1289 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 1290 1291 // Filter semesters based on selected campus and program 1292 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 1293 let optionSemesters = jQuery(option).attr('data-class'); 1294 1295 if (typeof optionSemesters !== 'undefined' && campusValue !== '' && programValue !== '') { 1296 // The data-class contains pairs like "67-104 67-802" 1297 // We need to check if any pair matches the selected program and campus 1298 const semesterPairs = optionSemesters.split(' '); 1299 let shouldKeep = false; 1300 1301 semesterPairs.forEach(pair => { 1302 const [programId, campusId] = pair.split('-'); 1303 if (programId === programValue && campusId === campusValue) { 1304 shouldKeep = true; 1305 } 1306 }); 1307 1308 if (!shouldKeep && option.value !== '') { 1309 jQuery(option).remove(); 1310 } 1311 } 1312 }); 1313 1314 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 1315 if (jQuery(option).is(':empty')) { 1316 jQuery(option).remove(); 1317 } 1318 }); 1319 1320 singleSemesterSelect(semesterID, campusID); 1321 } 1322 1323 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1324 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1325 runRuleEngine(); 1326 }); 1327 1328 // Semester change handler 1329 jQuery("#nf-field-" + semesterID).change(function () { 1330 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 1331 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 1332 runRuleEngine(); 1333 }); 1334 1335 // Program label change handler 1336 jQuery("#nf-field-" + programID).change(function () { 1337 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1338 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1339 runRuleEngine(); 1340 }); 1341 1342 jQuery("#nf-field-" + countryFieldId?.id).change(function () { 1343 runRuleEngine(); 1344 }) 1345 jQuery("#nf-field-" + stateFieldId?.id).change(function () { 1346 runRuleEngine(); 1347 }) 1348 1349 function runRuleEngine() { 1350 console.log('runRuleEngine'); 1351 1352 const values = { 1353 program: jQuery("#nf-field-" + programID).val(), 1354 campus: jQuery("#nf-field-" + campusID).val(), 1355 semester: jQuery("#nf-field-" + semesterID).val(), 1356 country: jQuery("#nf-field-" + countryFieldId?.id)?.val(), 1357 state: jQuery("#nf-field-" + stateFieldId?.id)?.val(), 1358 }; 1359 1360 sessionStorage.setItem("nf_program", values.program || ""); 1361 sessionStorage.setItem("nf_campus", values.campus || ""); 1362 sessionStorage.setItem("nf_semester", values.semester || ""); 1363 sessionStorage.setItem("nf_country", values.country || ""); 1364 sessionStorage.setItem("nf_state", values.state || ""); 1365 1366 const fieldIDs = { 1367 program: programID, 1368 campus: campusID, 1369 semester: semesterID, 1370 country: countryFieldId?.id, 1371 state: stateFieldId?.id 1372 }; 1373 1374 // STEP 1: RESET everything first 1375 resetRuleEffects(fieldIDs); 1376 1377 // STEP 2: Apply rules fresh 1378 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 1379 } 1380 1381 // If only one Program is available on load, auto-select it. This cascades
1382 // (via the change handlers above) to auto-select Campus and Semester too, 1383 // when each of those also only has a single available option. 1384 autoSelectSingleOption(programID); 1385 1386 runRuleEngine(); // run once on load 1387} 1388 1389// Non-chained dropdown behavior (no school, useDynamic === '') 1390// CAMPUS > PROGRAM > SEMESTER chain (no school, useDynamic === 'campus') - UPDATED 1391function campusProgramSemesterChainNoSchool(programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID, countryFieldId, stateFieldId) { 1392 // Initialize campus field with "Choose Campus" on page load 1393 const $campusSelect = jQuery('#nf-field-' + campusID); 1394 $campusSelect.find('option').remove(); 1395 $campusSelect.find('optgroup').remove(); 1396 $campusSelect.append('<option value="" label="">Choose Campus</option>'); 1397 $campusSelect.append(ninjaFormDataJS.campusesHTML); 1398 1399 // Initialize program field with "--" on page load 1400 initializeDropdownWithDefault(programID, '--'); 1401 1402 // Initialize semester field with "--" on page load 1403 initializeDropdownWithDefault(semesterID, '--'); 1404 1405 // Campus -> Program filtering - UPDATED to use program_campus_mapping 1406 jQuery("#nf-field-" + campusID).change(function () { 1407 const campusValue = jQuery(this).val(); 1408 1409 // Reset program dropdown with "Choose Program" after campus selection 1410 initializeDropdownWithDefault(programID, 'Choose Program'); 1411 1412 if (campusValue) { 1413 jQuery("#nf-field-" + programID).append(ninjaFormDataJS.programsHTML); 1414 1415 // Try to filter programs using mapping 1416 const mappingApplied = filterProgramsByCampusMapping(campusValue, programID); 1417 1418 // If no mapping was applied, show all programs 1419 if (!mappingApplied) { 1420 // Filter programs based on selected campus 1421 jQuery("#nf-field-" + programID + " option").each((index, option) => { 1422 let optionCampuses = jQuery(option).attr('data-class'); 1423 if (typeof optionCampuses !== 'undefined' && campusValue !== '') { 1424 optionCampuses = optionCampuses.split(' '); 1425 if (optionCampuses.indexOf(campusValue) === -1 && option.value !== '') { 1426 jQuery(option).remove(); 1427 } 1428 } 1429 }); 1430 } 1431 1432 jQuery("#nf-field-" + programID + " optgroup").each((index, option) => { 1433 if (jQuery(option).is(':empty')) { 1434 jQuery(option).remove(); 1435 } 1436 }); 1437 } 1438 1439 // Reset dependent field (Semester) to "--" 1440 initializeDropdownWithDefault(semesterID, '--'); 1441 1442 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1443 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1444 1445 // If only one Program remains available, auto-select it instead of 1446 // requiring the user to choose it manually. 1447 autoSelectSingleOption(programID); 1448 1449 runRuleEngine(); 1450 }); 1451 1452 // Program -> Semester filtering 1453 jQuery("#nf-field-" + programID).change(function () { 1454 const programValue = jQuery(this).val(); 1455 const campusValue = jQuery("#nf-field-" + campusID).val(); 1456 1457 // Reset semester dropdown with "Choose Semester" after program selection 1458 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1459 1460 if (programValue && campusValue) { 1461 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 1462 1463 // Filter semesters based on selected program AND campus 1464 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 1465 let optionSemesters = jQuery(option).attr('data-class'); 1466 let optionPrograms = jQuery(option).attr('data-program-solo-id'); 1467 1468 let shouldRemove = false; 1469 1470 // Filter by campus and program using data-class (format: "program-campus" pairs) 1471 if (typeof optionSemesters !== 'undefined' && campusValue !== '' && programValue !== '') { 1472 const semesterPairs = optionSemesters.split(' '); 1473 let hasMatchingPair = false;
1474 1475 semesterPairs.forEach(pair => { 1476 const [programId, campusId] = pair.split('-'); 1477 if (programId === programValue && campusId === campusValue) { 1478 hasMatchingPair = true; 1479 } 1480 }); 1481 1482 if (!hasMatchingPair && option.value !== '') { 1483 shouldRemove = true; 1484 } 1485 } 1486 1487 // Additional filter by program using data-program-solo-id 1488 if (!shouldRemove && typeof optionPrograms !== 'undefined' && programValue !== '') { 1489 optionPrograms = optionPrograms.split(' '); 1490 if (optionPrograms.indexOf(programValue) === -1 && option.value !== '') { 1491 shouldRemove = true; 1492 } 1493 } 1494 1495 if (shouldRemove) { 1496 jQuery(option).remove(); 1497 } 1498 }); 1499 1500 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 1501 if (jQuery(option).is(':empty')) { 1502 jQuery(option).remove(); 1503 } 1504 }); 1505 1506 singleSemesterSelect(semesterID, campusID); 1507 } 1508 1509 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1510 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1511 runRuleEngine(); 1512 }); 1513 1514 // Semester change handler 1515 jQuery("#nf-field-" + semesterID).change(function () { 1516 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 1517 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 1518 runRuleEngine(); 1519 }); 1520 1521 jQuery("#nf-field-" + countryFieldId?.id).change(function () { 1522 runRuleEngine(); 1523 }) 1524 jQuery("#nf-field-" + stateFieldId?.id).change(function () { 1525 runRuleEngine(); 1526 }) 1527 1528 function runRuleEngine() { 1529 1530 const values = { 1531 program: jQuery("#nf-field-" + programID).val(), 1532 campus: jQuery("#nf-field-" + campusID).val(), 1533 semester: jQuery("#nf-field-" + semesterID).val(), 1534 country: jQuery("#nf-field-" + countryFieldId?.id)?.val(), 1535 state: jQuery("#nf-field-" + stateFieldId?.id)?.val(), 1536 }; 1537 1538 const fieldIDs = { 1539 program: programID, 1540 campus: campusID, 1541 semester: semesterID, 1542 country: countryFieldId?.id, 1543 state: stateFieldId?.id 1544 }; 1545 1546 // STEP 1: RESET everything first 1547 resetRuleEffects(fieldIDs); 1548 1549 // STEP 2: Apply rules fresh 1550 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 1551 } 1552 1553 // If only one Campus is available on load, auto-select it. This cascades 1554 // (via the change handlers above) to auto-select Program and Semester too, 1555 // when each of those also only has a single available option. 1556 autoSelectSingleOption(campusID); 1557 1558 runRuleEngine(); // run once on load 1559} 1560 1561function programCampusSemesterChain(programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID) { 1562 // Initialize dropdowns with defaults 1563 initializeDropdownWithDefault(campusID, 'Choose Campus'); 1564 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1565 1566 // Program -> Campus filtering - UPDATED to use program_campus_mapping with fallback 1567 programCampusDropDowns(programID, programLabelID, campusID, campusLabelID); 1568 1569 // Campus -> Semester filtering 1570 campusSemesterDropDowns(campusID, campusLabelID, semesterID, semesterLabelID); 1571} 1572 1573// CAMPUS > PROGRAM > SEMESTER chain (no school, useDynamic === 'campus') 1574function campusProgramSemesterChain(programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID) { 1575 // Initialize dropdowns with defaults 1576 initializeDropdownWithDefault(programID, 'Choose Program'); 1577 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1578 1579 // Campus -> Program filtering - UPDATED to use program_campus_mapping 1580 campusProgramDropDowns(programID, programLabelID, campusID, campusLabelID); 1581 1582 // Campus -> Semester filtering 1583 campusSemesterDropDowns(campusID, campusLabelID, semesterID, semesterLabelID); 1584} 1585 1586// UPDATED: Program -> Campus filtering with program_campus_mapping and fallback 1587function programCampusDropDowns(programID, programLabelID, campusID, campusLabelID) { 1588 // Remove any empty data-solo-campus-id options 1589 jQuery(`#nf-field-${programID} option[data-solo-campus-id=""]`).remove(); 1590 1591 jQuery("#nf-field-" + programID).change(() => { 1592 const programValue = jQuery("#nf-field-" + programID).val(); 1593 1594 // Reset campus dropdown with default option 1595 initializeDropdownWithDefault(campusID, 'Choose Campus'); 1596 1597 if (programValue) { 1598 jQuery("#nf-field-" + campusID).append(ninjaFormDataJS.campusesHTML); 1599 1600 // Try to filter using mapping, fallback to data-class if no mapping 1601 filterCampusesByProgramMapping(programValue, campusID, programID); 1602 1603 jQuery("#nf-field-" + campusID + " optgroup").each((index, option) => { 1604 if (jQuery(option).is(':empty')) { 1605 jQuery(option).remove(); 1606 } 1607 }); 1608 } 1609 1610 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1611 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1612 }); 1613 1614 // Campus change handler for label updates 1615 jQuery("#nf-field-" + campusID).change(() => { 1616 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1617 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1618 }); 1619} 1620 1621// UPDATED: Campus -> Program filtering with program_campus_mapping 1622function campusProgramDropDowns(programID, programLabelID, campusID, campusLabelID) { 1623 jQuery("#nf-field-" + campusID).change(() => { 1624 const campusValue = jQuery("#nf-field-" + campusID).val(); 1625 1626 // Reset program dropdown with default option 1627 initializeDropdownWithDefault(programID, 'Choose Program'); 1628 1629 if (campusValue) { 1630 jQuery("#nf-field-" + programID).append(ninjaFormDataJS.programsHTML); 1631 1632 // Try to filter programs using mapping 1633 const mappingApplied = filterProgramsByCampusMapping(campusValue, programID); 1634 1635 // If no mapping was applied, show all programs 1636 if (!mappingApplied) { 1637 jQuery("#nf-field-" + programID + " option").show(); 1638 jQuery("#nf-field-" + programID + " option").prop('disabled', false); 1639 } 1640 1641 jQuery("#nf-field-" + programID + " optgroup").each((index, option) => { 1642 if (jQuery(option).is(':empty')) { 1643 jQuery(option).remove(); 1644 } 1645 }); 1646 } 1647 1648 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1649 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1650 }); 1651 1652 jQuery("#nf-field-" + programID).change(() => { 1653 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1654 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1655 }); 1656} 1657 1658// UPDATED FUNCTION: Campus -> Semester filtering (standalone) 1659function campusSemesterDropDowns(campusID, campusLabelID, semesterID, semesterLabelID) { 1660 jQuery("#nf-field-" + campusID).change(function () { 1661 const campusValue = jQuery(this).val(); 1662 1663 // Reset and populate semester dropdown with default 1664 initializeDropdownWithDefault(semesterID, 'Choose Semester'); 1665 1666 if (campusValue) { 1667 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 1668 1669 // Filter semesters based on selected campus 1670 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 1671 let optionSemesters = jQuery(option).attr('data-class'); 1672 1673 if (typeof optionSemesters !== 'undefined' && campusValue !== '') { 1674 optionSemesters = optionSemesters.split(' '); 1675 if (optionSemesters.indexOf(campusValue) === -1 && option.value !== '') { 1676 jQuery(option).remove(); 1677 } 1678 } 1679 }); 1680 1681 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 1682 if (jQuery(option).is(':empty')) { 1683 jQuery(option).remove(); 1684 } 1685 }); 1686 1687 singleSemesterSelect(semesterID, campusID); 1688 } 1689 1690 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1691 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1692 }); 1693 1694 // Semester change handler 1695 jQuery("#nf-field-" + semesterID).change(function () { 1696 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 1697 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 1698 }); 1699} 1700 1701// Add the missing selector functions 1702function getUtmSchoolField(form) { 1703 return form.fields.find(f => f.key === 'utm_school'); 1704} 1705 1706// Add the missing school initialization function for standalone school fields 1707function initializeSchoolField(schoolID, schoolLabelID) { 1708 jQuery("#nf-field-" + schoolID).change(function () { 1709 const schoolLabel = jQuery("#nf-field-" + schoolID + " option:selected").text(); 1710 jQuery("#nf-field-" + schoolLabelID).val(schoolLabel).trigger('change'); 1711 }); 1712 1713 // Set initial value if there's a selected option 1714 const selectedSchool = jQuery("#nf-field-" + schoolID + " option:selected"); 1715 if (selectedSchool.length > 0 && selectedSchool.val() !== '') { 1716 const initialSchoolLabel = selectedSchool.text(); 1717 jQuery("#nf-field-" + schoolLabelID).val(initialSchoolLabel).trigger('change'); 1718 } 1719} 1720 1721// Add the missing UTM function for schools 1722function selectSchoolUTM(schoolID, schoolLabelID, utmSchool) { 1723 if (utmSchool && utmSchool.value) { 1724 jQuery('#nf-field-' + schoolID).val(utmSchool.value).trigger('change'); 1725 const schoolLabel = jQuery("#nf-field-" + schoolID + " option:selected").text(); 1726 jQuery("#nf-field-" + schoolLabelID).val(schoolLabel).trigger('change'); 1727 } 1728} 1729 1730// UPDATED FUNCTION: Directly load school options 1731function loadSchoolOptions(schoolID, schoolLabelID) { 1732 1733 const $schoolSelect = jQuery('#nf-field-' + schoolID); 1734 if (!$schoolSelect.length) { 1735 console.error('School select element not found: #nf-field-' + schoolID); 1736 return; 1737 } 1738 1739 // Clear existing options 1740 $schoolSelect.find('option').remove(); 1741 $schoolSelect.find('optgroup').remove(); 1742 1743 // Add default option first 1744 $schoolSelect.append('<option value="" label="">Choose School</option>'); 1745 1746 // Append the schoolsHTML that's now available from PHP 1747 if (typeof ninjaFormDataJS.schoolsHTML !== 'undefined' && ninjaFormDataJS.schoolsHTML) { 1748 $schoolSelect.append(ninjaFormDataJS.schoolsHTML); 1749 } else { 1750 console.warn('ninjaFormDataJS.schoolsHTML is undefined'); 1751 } 1752 1753 // Initialize the change handler 1754 initializeSchoolField(schoolID, schoolLabelID); 1755} 1756 1757// Add non-chained behavior for school forms 1758// Non-chained dropdown behavior for school forms (useDynamic === '') 1759function nonChainedSchoolDropDowns(schoolID, schoolLabelID, programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID, countryFieldId, stateFieldId) { 1760 // Initialize all dropdowns with their full options including defaults 1761 loadSchoolOptions(schoolID, schoolLabelID); // This now includes "Choose School" 1762 1763 // Initialize program field with all options 1764 const $programSelect = jQuery('#nf-field-' + programID); 1765 $programSelect.find('option').remove(); 1766 $programSelect.find('optgroup').remove(); 1767 $programSelect.append('<option value="" label="">Choose Program</option>'); 1768 $programSelect.append(ninjaFormDataJS.programsHTML); 1769 1770 // Initialize campus field with all options 1771 const $campusSelect = jQuery('#nf-field-' + campusID); 1772 $campusSelect.find('option').remove(); 1773 $campusSelect.find('optgroup').remove(); 1774 $campusSelect.append('<option value="" label="">Choose Campus</option>'); 1775 $campusSelect.append(ninjaFormDataJS.campusesHTML); 1776 1777 // Initialize semester field with all options 1778 const $semesterSelect = jQuery('#nf-field-' + semesterID); 1779 $semesterSelect.find('option').remove(); 1780 $semesterSelect.find('optgroup').remove(); 1781 $semesterSelect.append('<option value="" label="">Choose Semester</option>'); 1782 $semesterSelect.append(ninjaFormDataJS.semestersHTML); 1783
1784 // Set up change handlers for labels only (no filtering) 1785 jQuery("#nf-field-" + schoolID).change(function () { 1786 const schoolLabel = jQuery("#nf-field-" + schoolID + " option:selected").text(); 1787 jQuery("#nf-field-" + schoolLabelID).val(schoolLabel).trigger('change'); 1788 runRuleEngine(); 1789 }); 1790 1791 jQuery("#nf-field-" + programID).change(function () { 1792 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1793 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1794 runRuleEngine(); 1795 }); 1796 1797 jQuery("#nf-field-" + campusID).change(function () { 1798 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1799 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1800 runRuleEngine(); 1801 }); 1802 1803 jQuery("#nf-field-" + semesterID).change(function () { 1804 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 1805 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 1806 runRuleEngine(); 1807 }); 1808 1809 jQuery("#nf-field-" + countryFieldId?.id).change(function () { 1810 runRuleEngine(); 1811 }) 1812 jQuery("#nf-field-" + stateFieldId?.id).change(function () { 1813 runRuleEngine(); 1814 }) 1815 1816 function runRuleEngine() { 1817 1818 const values = { 1819 school: jQuery("#nf-field-" + schoolID).val(), 1820 program: jQuery("#nf-field-" + programID).val(), 1821 campus: jQuery("#nf-field-" + campusID).val(), 1822 semester: jQuery("#nf-field-" + semesterID).val(), 1823 country: jQuery("#nf-field-" + countryFieldId?.id)?.val(), 1824 state: jQuery("#nf-field-" + stateFieldId?.id)?.val(), 1825 }; 1826 1827 const fieldIDs = { 1828 school: schoolID, 1829 program: programID, 1830 campus: campusID, 1831 semester: semesterID, 1832 country: countryFieldId?.id, 1833 state: stateFieldId?.id 1834 }; 1835 1836 // STEP 1: RESET everything first 1837 resetRuleEffects(fieldIDs); 1838 1839 // STEP 2: Apply rules fresh 1840 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 1841 } 1842 1843 // Since Program, Campus, and Semester are independent (non-chained) here, 1844 // auto-select each one on load if it only has a single available option. 1845 autoSelectSingleOption(programID); 1846 autoSelectSingleOption(campusID); 1847 autoSelectSingleOption(semesterID); 1848 1849 runRuleEngine(); // run once on load 1850} 1851 1852// Complete school > program > campus > semester chaining function 1853function schoolProgramCampusDropDowns(schoolID, schoolLabelID, programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID) { 1854 // Initialize school field 1855 initializeSchoolField(schoolID, schoolLabelID); 1856 1857 // When school changes, filter programs 1858 jQuery("#nf-field-" + schoolID).change(function () { 1859 const schoolValue = jQuery(this).val(); 1860 1861 // Reset and populate program dropdown 1862 jQuery("#nf-field-" + programID + " option").remove(); 1863 jQuery("#nf-field-" + programID + " optgroup").remove(); 1864 jQuery("#nf-field-" + programID).append('<option value="" label="">Choose Program</option>'); 1865 jQuery("#nf-field-" + programID).append(ninjaFormDataJS.programsHTML); 1866 1867 // Filter programs based on selected school 1868 jQuery("#nf-field-" + programID + " option").each((index, option) => { 1869 let optionSchools = jQuery(option).attr('data-schools'); 1870 if (typeof optionSchools !== 'undefined' && schoolValue !== '') { 1871 optionSchools = optionSchools.split(' '); 1872 if (optionSchools.indexOf(schoolValue) === -1 && option.value !== '') { 1873 jQuery(option).remove(); 1874 } 1875 } 1876 }); 1877 1878 jQuery("#nf-field-" + programID + " optgroup").each((index, option) => { 1879 if (jQuery(option).is(':empty')) { 1880 jQuery(option).remove(); 1881 } 1882 }); 1883 1884 // Trigger program change to filter campuses 1885 jQuery("#nf-field-" + programID).trigger('change'); 1886 1887 const schoolLabel = jQuery("#nf-field-" + schoolID + " option:selected").text(); 1888 jQuery("#nf-field-" + schoolLabelID).val(schoolLabel).trigger('change'); 1889 }); 1890 1891 // Program change handler - UPDATED to use program_campus_mapping with fallback 1892 jQuery("#nf-field-" + programID).change(function () { 1893 const programValue = jQuery(this).val(); 1894 1895 // Reset and populate campus dropdown 1896 jQuery("#nf-field-" + campusID + " option").remove(); 1897 jQuery("#nf-field-" + campusID + " optgroup").remove(); 1898 jQuery("#nf-field-" + campusID).append('<option value="" label="">Choose Campus</option>'); 1899 jQuery("#nf-field-" + campusID).append(ninjaFormDataJS.campusesHTML); 1900 1901 if (programValue) { 1902 // Try to filter using mapping, fallback to data-class if no mapping 1903 filterCampusesByProgramMapping(programValue, campusID, programID); 1904 } 1905 1906 jQuery("#nf-field-" + campusID + " optgroup").each((index, option) => { 1907 if (jQuery(option).is(':empty')) { 1908 jQuery(option).remove(); 1909 } 1910 }); 1911 1912 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 1913 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 1914 1915 // Trigger campus change to filter semesters 1916 jQuery("#nf-field-" + campusID).trigger('change'); 1917 }); 1918 1919 // Campus change handler 1920 jQuery("#nf-field-" + campusID).change(function () { 1921 const campusValue = jQuery(this).val(); 1922 1923 // Reset and populate semester dropdown 1924 jQuery("#nf-field-" + semesterID + " option").remove(); 1925 jQuery("#nf-field-" + semesterID + " optgroup").remove(); 1926 jQuery("#nf-field-" + semesterID).append('<option value="" label="">Choose Semester</option>'); 1927 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 1928 1929 // Filter semesters based on selected campus 1930 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 1931 let optionSemesters = jQuery(option).attr('data-class'); 1932 1933 if (typeof optionSemesters !== 'undefined' && campusValue !== '') { 1934 optionSemesters = optionSemesters.split(' '); 1935 if (optionSemesters.indexOf(campusValue) === -1 && option.value !== '') { 1936 jQuery(option).remove(); 1937 } 1938 } 1939 }); 1940 1941 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 1942 if (jQuery(option).is(':empty')) { 1943 jQuery(option).remove(); 1944 } 1945 }); 1946 1947 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 1948 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 1949 1950 singleSemesterSelect(semesterID, campusID); 1951 }); 1952 1953 // Semester change handler 1954 jQuery("#nf-field-" + semesterID).change(function () { 1955 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 1956 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 1957 }); 1958} 1959 1960//selector functions 1961 1962function getPageUrlField(form) { 1963 return form.fields.find(f => f.key === 'page_url'); 1964} 1965 1966function getThankYouPageField(form) { 1967 return form.fields.find(f => f.key === 'thank_you_page'); 1968} 1969 1970function getSubmitButton(form) { 1971 return form.fields.find(f => f.key.includes('submit')); 1972} 1973 1974function getTimezoneField(form) { 1975 return form.fields.find(f => f.key === 'selected_timezone'); 1976} 1977 1978function getProgramSelectField(form) { 1979 return form.fields.find(f => f.key === 'program_select'); 1980} 1981 1982function getCampusSelectField(form) { 1983 return form.fields.find(f => f.key === 'campus_select'); 1984} 1985 1986function getCampusDataField(form) { 1987 return form.fields.find(f => f.key === 'use_campus_data_attribute'); 1988} 1989 1990function getUtmCampusField(form) { 1991 return form.fields.find(f => f.key === 'utm_campus'); 1992} 1993 1994// School Field Functions 1995function getSchoolSelectField(form) { 1996 return form.fields.find(f => f.key === 'school_select'); 1997} 1998 1999function getSchoolLabelField(form) { 2000 return form.fields.find(f => f.key === 'school_label'); 2001} 2002 2003function getCampusLabelField(form) { 2004 return form.fields.find(f => f.key === 'campus_label'); 2005} 2006 2007function getUtmProgramField(form) { 2008 return form.fields.find(f => f.key === 'utm_program'); 2009} 2010 2011function getProgramLabelField(form) { 2012 return form.fields.find(f => f.key === 'program_label'); 2013} 2014 2015function getSemesterSelectField(form) { 2016 return form.fields.find(f => f.key === 'semester_select'); 2017} 2018 2019function getSemesterLabelField(form) { 2020 return form.fields.find(f => f.key === 'semester_label'); 2021} 2022 2023function getSrcParamTranslatedField(form) { 2024 return form.fields.find(f => f.key === 'src_parameter_translated'); 2025} 2026 2027function getCountryZipTypeField(form) { 2028 return form.fields.find(f => f.key === 'country_zip_type'); 2029} 2030 2031function getZipStatusField(form) { 2032 return form.fields.find(f => f.key === 'zip_status'); 2033} 2034 2035function getPostalCodeField(form) { 2036 return form.fields.find(f => f.key === 'postal_code'); 2037} 2038 2039function getZipCodeField(form) { 2040 return form.fields.find(f => f.key === 'zip_code'); 2041} 2042 2043function getCountryField(form) { 2044 return form.fields.find(f => f.key === 'list_country_main'); 2045} 2046 2047function getCityField(form) { 2048 return form.fields.find(f => f.key === 'visible_city'); 2049} 2050 2051function getStateField(form) { 2052 return form.fields.find(f => f.key === 'visible_state'); 2053} 2054 2055function getProvinceField(form) { 2056 return form.fields.find(f => f.key === 'province_ca'); 2057} 2058 2059function getInternationalStateField(form) { 2060 return form.fields.find(f => f.key === 'int_state'); 2061} 2062 2063function getLocalLatField(form) { 2064 return form.fields.find(f => f.key === 'local_latitude'); 2065} 2066 2067function getLocalLongField(form) { 2068 return form.fields.find(f => f.key === 'local_longitude'); 2069} 2070 2071function getCountryCodeField(form) { 2072 return form.fields.find(f =>
2072 f.key === 'gdpr_country_code'); 2073} 2074 2075function getEmailField(form) { 2076 return form.fields.find(f => f.type === 'email'); 2077} 2078 2079function getDateLimit(form) { 2080 const i = ninjaFormDataJS.ninjaFormsIDs.indexOf(form.id); 2081 if (ninjaFormDataJS.ninjaDateSettings[i] > 0) return Number(ninjaFormDataJS.ninjaDateSettings[i]); 2082 else return 0; 2083} 2084 2085// Looks up this form's per-form "Data Sync" setting (Campus First / Program 2086// First / None), configured under DataFlow Sync > General > Campus-Program 2087// Chaining and localized as ninjaFormDataJS.formCampusSettings, keyed to 2088// ninjaFormDataJS.ninjaFormsIDs by form ID. 2089function getFormDataSyncSetting(form) { 2090 const i = ninjaFormDataJS.ninjaFormsIDs.indexOf(form.id); 2091 return i > -1 ? ninjaFormDataJS.formCampusSettings[i] : undefined; 2092} 2093 2094function useDynamicCampusProgram(form) { 2095 const dataSyncSetting = getFormDataSyncSetting(form); // 'campus' | 'program' | 'no' | undefined 2096 2097 // Page-level override: the "Form is Program First?" checkbox in the 2098 // Page Editor (TCS Field Config metabox) FORCIBLY wins over the Data 2099 // Sync setting whenever it's checked -- e.g. Data Sync says 2100 // "Campus First" (or even "None") but this page has the checkbox 2101 // checked -> use Program First regardless. 2102 const pageConfig = ninjaFormDataJS.pageFieldConfig; 2103 if (pageConfig && pageConfig.program_first) { 2104 return 'program'; 2105 } 2106 2107 // Data Sync explicitly says this form uses no campus/program chaining at 2108 // all, and the checkbox above didn't override it -- no chaining. 2109 if (dataSyncSetting === 'no') { 2110 return false; 2111 } 2112 2113 // No page-level override -- default to whatever Data Sync says for this form. 2114 if (dataSyncSetting === 'campus') return 'campus'; 2115 if (dataSyncSetting === 'program') return 'program'; 2116 2117 // No Data Sync entry configured for this form -- fall back to the 2118 // Program-first progressive-filtering chain -- Program shows every 2119 // option, Campus and Semester stay empty until a Program is chosen. 2120 return 'program'; 2121} 2122 2123//custom NinjaForm features 2124function captureURL(pageUrlId) { 2125 jQuery('#nf-field-' + pageUrlId).val(window.location.href).trigger('change'); 2126} 2127 2128function setThankYouPage(thankYouPageId) { 2129 if (ninjaFormDataJS.thankYouPage !== 'no') { 2130 jQuery('#nf-field-' + thankYouPageId).val(ninjaFormDataJS.thankYouPage).trigger('change'); 2131 } 2132} 2133 2134function getLatLong(localLatitudeId, localLongitudeId, submitButtonId) { 2135 jQuery(`#nf-field-${submitButtonId}`).prop('disabled', true); 2136 jQuery.ajax({ 2137 url: `https://www.googleapis.com/geolocation/v1/geolocate?key=${ninjaFormDataJS.googleAPIKey}`, 2138 cache: false, 2139 dataType: "json", 2140 type: "POST", 2141 complete: () => { 2142 jQuery(`#nf-field-${submitButtonId}`).prop('disabled', false); 2143 }, 2144 success: result => { 2145 const lat = result.location.lat; 2146 const lng = result.location.lng; 2147 jQuery(`#nf-field-${localLatitudeId}`).val(lat).trigger('change'); 2148 jQuery(`#nf-field-${localLongitudeId}`).val(lng).trigger('change'); 2149 }, 2150 error: err => { console.log(err); } 2151 }); 2152} 2153 2154function gdprRequirements(countryCodeID) { 2155 jQuery.ajax({ 2156 url: `https://www.googleapis.com/geolocation/v1/geolocate?key=${ninjaFormDataJS.googleAPIKey}`, 2157 cache: false, 2158 dataType: 'json', 2159 type: 'POST', 2160 success: result => { 2161 const lat = result.location.lat; 2162 const lng = result.location.lng; 2163 jQuery.ajax({ 2164 url: `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&language=en&key=${ninjaFormDataJS.googleAPIKey}`, 2165 cache: false, 2166 dataType: 'json', 2167 type: 'GET', 2168 success: result => { 2169 const countryCode = result.results.find(x => x.types.indexOf('country') >= 0).address_components[0].short_name.toUpperCase(); 2170 jQuery(`#nf-field-${countryCodeID}`).val(countryCode).trigger('change'); 2171 }, 2172 error: err => { console.log(err); } 2173 }); 2174 }, 2175 error: err => { console.log(err); } 2176 }); 2177} 2178 2179function validateZipCode(countryFieldID, countryZipTypeID, zipFieldID, postalFieldID, stateFieldID, provFieldID, intStateFieldID, countryZipStatusID, cityFieldID, submitButtonID) { 2180 const zipCodeTimeZoneValidationController = Marionette.Object.extend({ 2181 initialize: function () { 2182 const fieldsChannel = Backbone.Radio.channel('fields'); 2183 this.listenTo(fieldsChannel, 'change:modelValue', this.validate); 2184 }, 2185 validate: model => { 2186 if (model.get('type') !== 'zip' && model.get('type') !== 'listcountry') return; 2187 const fieldValue = model.get('value'); 2188 const countriesNoPostal = [{ 2189 "name": "Angola", 2190 "code": "AO" 2191 }, { 2192 "name": "Antigua and Barbuda", 2193 "code": "AG" 2194 }, { 2195 "name": "Aruba", 2196 "code": "AW" 2197 }, { 2198 "name": "Bahamas", 2199 "code": "BS" 2200 }, { 2201 "name": "Belize", 2202 "code": "BZ" 2203 }, { 2204 "name": "Benin", 2205 "code": "BJ" 2206 }, { 2207 "name": "Botswana", 2208 "code": "BW" 2209 }, { 2210 "name": "Burkina Faso", 2211 "code": "BF" 2212 }, { 2213 "name": "Burundi", 2214 "code": "BI"
2215 }, { 2216 "name": "Cameroon", 2217 "code": "CM" 2218 }, { 2219 "name": "Central African Republic", 2220 "code": "CF" 2221 }, { 2222 "name": "Comoros", 2223 "code": "KM" 2224 }, { 2225 "name": "Congo", 2226 "code": "CG" 2227 }, { 2228 "name": "Congo, the Democratic Republic of the", 2229 "code": "CD" 2230 }, { 2231 "name": "Cook Islands", 2232 "code": "CK" 2233 }, { 2234 "name": "Cote d'Ivoire", 2235 "code": "CI" 2236 }, { 2237 "name": "Djibouti", 2238 "code": "DJ" 2239 }, { 2240 "name": "Dominica", 2241 "code": "DM" 2242 }, { 2243 "name": "Equatorial Guinea", 2244 "code": "GQ" 2245 }, { 2246 "name": "Eritrea", 2247 "code": "ER" 2248 }, { 2249 "name": "Fiji", 2250 "code": "FJ" 2251 }, { 2252 "name": "French Southern Territories", 2253 "code": "TF" 2254 }, { 2255 "name": "Gambia", 2256 "code": "GM" 2257 }, { 2258 "name": "Ghana", 2259 "code": "GH" 2260 }, { 2261 "name": "Grenada", 2262 "code": "GD" 2263 }, { 2264 "name": "Guinea", 2265 "code": "GN" 2266 }, { 2267 "name": "Guyana", 2268 "code": "GY" 2269 }, { 2270 "name": "Hong Kong", 2271 "code": "HK" 2272 }, { 2273 "name": "Ireland", 2274 "code": "IE" 2275 }, { 2276 "name": "Israel", 2277 "code": "IL" 2278 }, { 2279 "name": "Jamaica", 2280 "code": "JM" 2281 }, { 2282 "name": "Kenya", 2283 "code": "KE" 2284 }, { 2285 "name": "Kiribati", 2286 "code": "KI" 2287 }, { 2288 "name": "Macao", 2289 "code": "MO" 2290 }, { 2291 "name": "Malawi", 2292 "code": "MW" 2293 }, { 2294 "name": "Mali", 2295 "code": "ML" 2296 }, { 2297 "name": "Mauritania", 2298 "code": "MR" 2299 }, { 2300 "name": "Mauritius", 2301 "code": "MU" 2302 }, { 2303 "name": "Montserrat", 2304 "code": "MS" 2305 }, { 2306 "name": "Nauru", 2307 "code": "NR" 2308 }, { 2309 "name": "Netherlands Antilles", 2310 "code": "AN" 2311 }, { 2312 "name": "Nigeria", 2313 "code": "NG" 2314 }, { 2315 "name": "Niue", 2316 "code": "NU" 2317 }, { 2318 "name": "North Korea", 2319 "code": "KP" 2320 }, { 2321 "name": "Panama", 2322 "code": "PA" 2323 }, { 2324 "name": "Qatar", 2325 "code": "QA" 2326 }, { 2327 "name": "Rwanda", 2328 "code": "RW" 2329 }, { 2330 "name": "Saint Kitts and Nevis", 2331 "code": "KN" 2332 }, { 2333 "name": "Saint Lucia", 2334 "code": "LC" 2335 }, { 2336 "name": "Sao Tome and Principe", 2337 "code": "ST" 2338 }, { 2339 "name": "Saudi Arabia", 2340 "code": "SA" 2341 }, { 2342 "name": "Seychelles", 2343 "code": "SC" 2344 }, { 2345 "name": "Sierra Leone", 2346 "code": "SL" 2347 }, { 2348 "name": "Solomon Islands", 2349 "code": "SB" 2350 }, { 2351 "name": "Somalia", 2352 "code": "SO" 2353 }, { 2354 "name": "South Africa", 2355 "code": "ZA" 2356 }, { 2357 "name": "Suriname", 2358 "code": "SR" 2359 }, { 2360 "name": "Syria", 2361 "code": "SY" 2362 }, { 2363 "name": "Tanzania, United Republic of", 2364 "code": "TZ" 2365 }, { 2366 "name": "Timor-Leste", 2367 "code": "TL" 2368 }, { 2369 "name": "Tokelau", 2370 "code": "TK" 2371 }, { 2372 "name": "Tonga", 2373 "code": "TO" 2374 }, { 2375 "name": "Trinidad and Tobago", 2376 "code": "TT" 2377 }, { 2378 "name": "Tuvalu", 2379 "code": "TV" 2380 }, { 2381 "name": "Uganda", 2382 "code": "UG" 2383 }, { 2384 "name": "United Arab Emirates", 2385 "code": "AE" 2386 }, { 2387 "name": "Vanuatu", 2388 "code": "VU" 2389 }, { 2390 "name": "Yemen", 2391 "code": "YE" 2392 }, { 2393 "name": "Zimbabwe", 2394 "code": "ZW" 2395 }]; 2396 if (model.get('type') === 'listcountry') { 2397 jQuery('#nf-field-' + countryZipStatusID).val("ready").trigger('change'); 2398 jQuery('#nf-field-' + postalFieldID).val(""); 2399 jQuery('#nf-field-' + zipFieldID).val(""); 2400 jQuery('#nf-field-' + intStateFieldID).val("").trigger('change'); 2401 jQuery('#nf-field-' + cityFieldID).val(""); 2402 Backbone.Radio.channel('fields').request('remove:error', postalFieldID, 'required-error'); 2403 if (fieldValue === "US") { 2404 jQuery('#nf-field-' + countryZipTypeID).val("us").trigger('change'); 2405 } else if (fieldValue === "CA") { 2406 jQuery('#nf-field-' + countryZipTypeID).val("ca").trigger('change'); 2407 } else { 2408 const countryPostalResult = jQuery.map(countriesNoPostal, val => { 2409 return val.code === fieldValue ? 'nopost' : null; 2410 }); 2411 if (countryPostalResult[0] === 'nopost') { 2412 jQuery('#nf-field-' + countryZipTypeID).val("nopost").trigger('change'); 2413 jQuery('#nf-field-' + countryZipStatusID).val("filled").trigger('change'); 2414 jQuery("label[for=nf-field-" + postalFieldID + "]").html('Postal Code'); 2415 jQuery('#nf-field-' + cityFieldID).val(""); 2416 jQuery('#nf-field-' + intStateFieldID).val("").trigger('change'); 2417 Backbone.Radio.channel('fields').request('remove:error', postalFieldID, 'required-error'); 2418 Backbone.Radio.channel('fields').request('remove:error', zipFieldID, 'required-error'); 2419 } else { 2420 jQuery('#nf-field-' + countryZipTypeID).val("int").trigger('change'); 2421 jQuery("label[for=nf-field-" + postalFieldID + "]").html('Postal Code <span class="ninja-forms-req-symbol">*</span>'); 2422 } 2423 } 2424 } 2425 if (model.get('type') === 'zip') { 2426 if (!model.get('value')) return; 2427 console.log('ZIP VALIDATION START - zipValue:', model.get('value'), 'countryZipType:', jQuery('#nf-field-' + countryZipTypeID).val()); 2428 if (jQuery('#nf-field-' + countryZipTypeID).val() === 'nopost') { 2429 jQuery('#nf-field-' + countryZipStatusID).val("filled").trigger('change'); 2430 } else { 2431 jQuery('#nf-field-' + zipFieldID).parent().append('<img src="' + ninjaFormDataJS.loadingGifPath + '" alt="tcspp-loader" class="zip-loader" width="118" height="15" />'); 2432 jQuery('#nf-field-' + zipFieldID).prop('disabled', true); 2433 jQuery('#nf-field-' + submitButtonID).prop('disabled', true); 2434 const country = jQuery(`#nf-field-${countryFieldID}`).find(':selected').text(); 2435 const apiKey = ninjaFormDataJS.googleAPIKey || 'AIzaSyBadUrU21eiyl9PJ0I7VovULzAPwlL7FUo'; 2436 const searchAddress = model.get('value') + ' ' + country; 2437 const apiUrl = "https://maps.googleapis.com/ma
2437ps/api/geocode/json?address=" + encodeURIComponent(searchAddress) + '&language=en' + "&key=" + apiKey; 2438 2439 console.log('ZIP API CALL:', { 2440 zipValue: model.get('value'), 2441 country: country, 2442 searchAddress: searchAddress, 2443 hasAPIKey: !!apiKey, 2444 apiKeyLength: apiKey ? apiKey.length : 0, 2445 url: apiUrl.substring(0, 100) + '...' 2446 }); 2447 2448 jQuery.ajax({ 2449 url: apiUrl, 2450 cache: false, 2451 dataType: "json", 2452 type: "GET", 2453 complete: () => { 2454 console.log('ZIP AJAX COMPLETE'); 2455 jQuery('.zip-loader').remove(); 2456 jQuery('#nf-field-' + zipFieldID).prop('disabled', false); 2457 jQuery('#nf-field-' + submitButtonID).prop('disabled', false); 2458 }, 2459 success: result => { 2460 console.log('ZIP AJAX SUCCESS - results length:', result.results.length); 2461 console.log('ZIP AJAX FULL RESPONSE:', result); 2462 2463 // Handle API key restriction errors - but validate format first 2464 if (result.status === 'REQUEST_DENIED') { 2465 console.log('ZIP - API Request Denied (likely referer restriction). Validating format...'); 2466 2467 // Basic format validation for common zip codes 2468 const zipValue = model.get('value'); 2469 const countryZipType = jQuery('#nf-field-' + countryZipTypeID).val(); 2470 let isValidFormat = false; 2471 2472 if (countryZipType === 'us') { 2473 // US: 5 digits (12345) or 5+4 (12345-6789) 2474 isValidFormat = /^\d{5}(-\d{4})?$/.test(zipValue); 2475 } else if (countryZipType === 'ca') { 2476 // Canada: A1A 1A1 format (letter-digit-letter space digit-letter-digit) 2477 isValidFormat = /^[A-Z]\d[A-Z]\s?\d[A-Z]\d$/i.test(zipValue); 2478 } else if (countryZipType === 'int') { 2479 // International: at least 3 characters, alphanumeric 2480 isValidFormat = /^[A-Z0-9\s\-]{3,}$/i.test(zipValue); 2481 } 2482 2483 if (isValidFormat) { 2484 console.log('ZIP - Format valid. Accepting zip code despite API restriction.'); 2485 Backbone.Radio.channel('fields').request('remove:error', model.get('id'), 'zip-code-error'); 2486 jQuery('#nf-field-' + countryZipStatusID).val("filled").trigger('change'); 2487 } else { 2488 console.log('ZIP - Format invalid for type:', countryZipType, 'value:', zipValue); 2489 Backbone.Radio.channel('fields').request('add:error', model.get('id'), 'zip-code-error', 'Please Enter A Valid Zip Code'); 2490 jQuery('#nf-field-' + countryZipStatusID).val("ready").trigger('change'); 2491 } 2492 return; 2493 } 2494 2495 if (typeof jQuery('#nf-field-' + countryFieldID).val() === 'undefined') return; 2496 if (result.results.length > 0) { 2497 const address = result.results.find(r => typeof r.address_components.find(ac => typeof ac.types.find(t => t === 'postal_code') !== 'undefined') !== 'undefined'); 2498 2499 console.log('ZIP - Found address with postal code:', typeof address !== 'undefined'); 2500 if (typeof address !== 'undefined') { 2501 console.log('ZIP - Address components:', address.address_components.map(c => ({types: c.types, short_name: c.short_name}))); 2502 2503 if (address.address_components.find(ac => typeof ac.types.find(t => t === 'country') !== 'undefined').short_name === jQuery('#nf-field-' + countryFieldID).val()) { 2504 Backbone.Radio.channel('fields').request('remove:error', model.get('id'), 'zip-code-error', 'Please Enter A Valid Zip Code'); 2505 jQuery('#nf-field-' + countryZipStatusID).val("filled").trigger('change'); 2506 if (address.address_components.find(ac => typeof ac.types.find(t => t === 'country') !== 'undefined').short_name === 'CA' || address.address_components.find(ac => typeof ac.types.find(t => t === 'country') !== 'undefined').short_name === 'US') { 2507 // Find components by type, not by index 2508 const localityComponent = address.address_components.find(ac => ac.types.includes('locality')); 2509 const stateComponent = address.address_components.find(ac => ac.types.includes('administrative_area_level_1')); 2510 2511 console.log('ZIP - Found city:', localityComponent?.short_name, 'state:', stateComponent?.short_name); 2512 2513 if (jQuery('#nf-field-' + countryZipTypeID).val() === 'us') { 2514 if (localityComponent) jQuery('#nf-field-' + cityFieldID).val(localityComponent.short_name).trigger('change'); 2515 if (stateComponent) jQuery('#nf-field-' + stateFieldID).val(stateComponent.short_name).trigger('change'); 2516 } 2517 if (jQuery('#nf-field-' + countryZipTypeID).val() === 'ca') { 2518 if (localityComponent) jQuery('#nf-field-' + cityFieldID).val(localityComponent.short_name).trigger('change'); 2519 if (stateComponent) jQuery('#nf-field-' + provFieldID).val(stateComponent.short_name).trigger('change'); 2520 } 2521 } else { 2522 if (jQuery('#nf-field-' + countryZipTypeID).val() === 'int') { 2523 const localityComponent = address.address_components.find(ac => ac.types.includes('locality')); 2524 if (localityComponent) jQuery('#nf-field-' + cityFieldID).val(localityComponent.short_name).trigger('change'); 2525 } 2526 } 2527 } else { 2528 console.log('ZIP - Country mismatch'); 2529 Backbone.Radio.channel('fields').request('add:error', model.get('id'), 'zip-code-error', 'Please Enter A Valid ' + (jQuery('#nf-field-' + countryFieldID + " > option[value='" + (jQuery('#nf-field-' + countryFieldID).val()) + "']").text()) + ' postal code.'); 2530 jQuery('#nf-field-' + countryZipStatusID).val("ready").trigger('change'); 2531 } 2532 } 2533 else { 2534 console.log('ZIP - No address with postal code found'); 2535 Backbone.Radio.channel('fields').request('add:error', model.get('id'), 'zip-code-error', 'Please Enter A Valid Zip Code'); 2536 } 2537 } 2538 else { 2539 console.log('ZIP - No results returned'); 2540 Backbone.Radio.channel('fields').request('add:error', model.get('id'), 'zip-code-error', 'Please Enter A Valid Zip Code'); 2541 } 2542 }, 2543 error: (xhr, status, error) => { 2544 console.log('ZIP AJAX ERROR:', {status, error, responseText: xhr.responseText}); 2545 // Only accept on REQUEST_DENIED (API restriction), reject on other errors 2546 Backbone.Radio.channel('fields').request('add:error', model.get('id'), 'zip-code-error', 'Please Enter A Valid Zip Code'); 2547 jQuery('#nf-field-' + countryZipStatusID).val("ready").trigger('change'); 2548 } 2549 }); 2550 } 2551 } 2552 } 2553 }); 2554 new zipCodeTimeZoneValidationController(); 2555} 2556 2557function setEmailValidation() { 2558 const emailValidator = Marionette.Object.extend({ 2559 initialize: function () { 2560 const submitChannel = Backbone.Radio.channel('submit'); 2561 this.listenTo(submitChannel, 'validate:field', this.validate); 2562 const fieldsChannel = Backbone.Radio.channel('fields'); 2563 this.listenTo(fieldsChannel, 'change:modelValue', this.validate); 2564 }, 2565 validate: function (model) { 2566 if (model.get('type') !== 'email') return; 2567 const fieldValue = model.get('value'); 2568 const modelId = model.get('id'); 2569 const errorId = 'custom-email-error'; 2570 const errorMessage = 'Please enter a valid email address!'; 2571 const fieldsChannel = Backbone.Radio.channel('fields'); 2572 if (/^[A-z|0-9|.]+@[A-z|0-9|.]+.[A-z]+$/.test(fieldValue)) { 2573 fieldsChannel.request('remove:error', modelId, errorId); 2574 } else { 2575 fieldsChannel.request('add:error', modelId, errorId, errorMessage); 2576 } 2577 } 2578 }); 2579 new emailValidator(); 2580} 2581 2582function limitDates(dateLimit) { 2583 const dateLimiter = Marionette.Object.extend({ 2584 initialize: function () { 2585 const pikadayChannel = Backbone.Radio.channel('pikaday'); 2586 this.listenTo(pikadayChannel, 'init', this.modifyDatepicker); 2587 },
2588 modifyDatepicker: (dateObject, fieldModel) => { 2589 let currentDate = new Date(); 2590 const tomorrow = currentDate.setDate(currentDate.getDate() + 1); 2591 currentDate = new Date(); 2592 const futureweek = currentDate.setDate(currentDate.getDate() + dateLimit); 2593 dateObject.pikaday._o.disableWeekends = true; 2594 dateObject.pikaday._o.firstDay = 0; 2595 dateObject.pikaday._o.maxDate = futureweek; 2596 dateObject.pikaday._o.minDate = tomorrow; 2597 dateObject.pikaday._o.disableDayFn = (date) => { 2598 const disableDays = ninjaFormDataJS.holidayBlackOuts; 2599 if (_.indexOf(disableDays, moment(date).format("YYYY-MM-DD")) !== -1) { 2600 return true; 2601 } 2602 } 2603 } 2604 }); 2605 new dateLimiter(); 2606} 2607 2608function getLeadSourceID(srcParamTranslatedID) { 2609 const leadSourceID = ninjaFormDataJS.leadSourceID; 2610 2611 jQuery('#nf-field-' + srcParamTranslatedID) 2612 .val(leadSourceID) 2613 .trigger('change'); 2614 2615 // Safely push to dataLayer only if it exists 2616 if (typeof window.dataLayer !== 'undefined' && Array.isArray(window.dataLayer)) { 2617 window.dataLayer.push({ 2618 'tcsLeadSourceID': leadSourceID 2619 }); 2620 } else { 2621 console.warn('dataLayer not found: skipping GTM push'); 2622 } 2623} 2624 2625function programCampusDropDowns(programID, programLabelID, campusID, campusLabelID) { 2626 // Remove any empty data-solo-campus-id options 2627 jQuery(`#nf-field-${programID} option[data-solo-campus-id=""]`).remove(); 2628 2629 jQuery("#nf-field-" + programID).change(() => { 2630 const programValue = jQuery("#nf-field-" + programID).val(); 2631 2632 // Reset campus dropdown with default option 2633 initializeDropdownWithDefault(campusID, 'Choose Campus'); 2634 2635 if (programValue) { 2636 jQuery("#nf-field-" + campusID).append(ninjaFormDataJS.campusesHTML); 2637 2638 // Try to filter using mapping, fallback to data-class if no mapping 2639 filterCampusesByProgramMapping(programValue, campusID, programID); 2640 2641 jQuery("#nf-field-" + campusID + " optgroup").each((index, option) => { 2642 if (jQuery(option).is(':empty')) { 2643 jQuery(option).remove(); 2644 } 2645 }); 2646 } 2647 2648 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 2649 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 2650 }); 2651 2652 // Campus change handler for label updates 2653 jQuery("#nf-field-" + campusID).change(() => { 2654 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 2655 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 2656 }); 2657} 2658 2659// Start for semester based on campus 2660function programSemesterDropDowns(campusID, campusLabelID, semesterID, semesterLabelID, programID, programLabelID, selectedFirst) { 2661 2662 jQuery("#nf-field-" + semesterID + " option").remove(); 2663 jQuery("#nf-field-" + semesterID + " optgroup").remove(); 2664 jQuery("#nf-field-" + semesterID).append('<option value="" label="">--</option>'); 2665 2666 let selectedProgramSemesterHTML = ''; 2667 let selectedCampusSemesterHTML = ''; 2668 let semester_selected_ProgValue = ''; 2669 2670 if (selectedFirst == 'program') { 2671 jQuery("#nf-field-" + programID).change(() => { 2672 const semesterProgValue = jQuery("#nf-field-" + programID).val(); 2673 semester_selected_ProgValue = semesterProgValue; 2674 jQuery("#nf-field-" + semesterID + " option").remove(); 2675 jQuery("#nf-field-" + semesterID + " optgroup").remove(); 2676 jQuery("#nf-field-" + semesterID).append('<option value="" label="">Choose Semester</option>'); 2677 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 2678 2679 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 2680 let optionSemestersProgid = jQuery(option).attr('data-program-solo-id'); 2681 2682 if (typeof optionSemestersProgid !== 'undefined' && semesterProgValue !== '') { 2683 optionSemestersProgid = optionSemestersProgid.split(' '); 2684 var Semester_len = optionSemestersProgid.length; 2685 2686 if (optionSemestersProgid.indexOf(semesterProgValue) === 0) { 2687 //option.setAttribute('selected','selected'); 2688 } 2689 if (optionSemestersProgid.indexOf(semesterProgValue) === -1 && option.value !== '') { 2690 jQuery(option).remove(); 2691 } 2692 } 2693 }); 2694 2695 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 2696 if (jQuery(option).is(':empty')) { 2697 jQuery(option).remove(); 2698 } 2699 }); 2700 2701 selectedProgramSemesterHTML = jQuery("#nf-field-" + semesterID).html(); 2702 2703 }); 2704 2705 jQuery("#nf-field-" + campusID).change(() => { 2706 const semesterValue = jQuery("#nf-field-" + campusID).val(); 2707 jQuery("#nf-field-" + semesterID + " option").remove(); 2708 jQuery("#nf-field-" + semesterID + " optgroup").remove(); 2709 jQuery("#nf-field-" + semesterID).append(selectedProgramSemesterHTML); 2710 2711 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 2712 let option_value_Semesters = jQuery(option).attr('data-class'); 2713 let optionSemesters = ''; 2714 2715 if (typeof option_value_Semesters !== 'undefined' && semesterValue !== '') { 2716 optionSemesters = option_value_Semesters 2717 .split(" ") // Split the string into an array 2718 .filter(pair => pair.startsWith(semester_selected_ProgValue + "-")) // Filter pairs matching the selected ID 2719 .map(pair => pair.split("-")[1]) // Extract only the values 2720 .join(" "); 2721 } 2722 2723 if (typeof optionSemesters !== 'undefined' && semesterValue !== '') { 2724 optionSemesters = optionSemesters.split(' '); 2725 var Semester_len = optionSemesters.length; 2726 2727 if (optionSemesters.indexOf(semesterValue) === 0) { 2728 //option.setAttribute('selected','selected'); 2729 } 2730 if (optionSemesters.indexOf(semesterValue) === -1 && option.value !== '') { 2731 jQuery(option).remove(); 2732 } 2733 } 2734 }); 2735 2736 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 2737 if (jQuery(option).is(':empty')) { 2738 jQuery(option).remove(); 2739 } 2740 }); 2741 2742 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 2743 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 2744 2745 singleSemesterSelect(semesterID, campusID); 2746 }); 2747 jQuery("#nf-field-" + semesterID).change(() => { 2748 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 2749 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 2750 }); 2751 2752 } else if (selectedFirst == 'campus') { 2753 2754 jQuery("#nf-field-" + campusID).change(() => { 2755 const semesterValue = jQuery("#nf-field-" + campusID).val(); 2756 jQuery("#nf-field-" + semesterID + " option").remove(); 2757 jQuery("#nf-field-" + semesterID + " optgroup").remove(); 2758 jQuery("#nf-field-" + semesterID).append('<option value="" label="">Choose Semester</option>'); 2759 jQuery("#nf-field-" + semesterID).append(ninjaFormDataJS.semestersHTML); 2760 2761 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 2762 let optionSemesters = jQuery(option).attr('data-class'); 2763 2764 if (typeof optionSemesters !== 'undefined' && semesterValue !== '') { 2765 optionSemesters = optionSemesters.split(' '); 2766 var Semester_len = optionSemesters.length; 2767 2768 if (optionSemesters.indexOf(semesterValue) === 0) { 2769 //option.setAttribute('selected','selected'); 2770 } 2771 if (optionSemesters.indexOf(semesterValue) === -1 && option.value !== '') { 2772 jQuery(option).remove(); 2773 } 2774 } 2775 }); 2776 2777 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 2778 if (jQuery(option).is(':empty')) { 2779 jQuery(option).remove(); 2780 } 2781 }); 2782 2783 selectedCampusSemesterHTML = jQuery("#nf-field-" + semesterID).html(); 2784 2785 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 2786 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 2787 2788 singleSemesterSelect(semesterID, campusID); 2789 }); 2790 2791 jQuery("#nf-field-" + programID).change(() => { 2792 const semesterProgValue = jQuery("#nf-field-" + programID).val(); 2793 jQuery("#nf-field-" + semesterID + " option").remove(); 2794 jQuery("#nf-field-" + semesterID + " optgroup").remove(); 2795 jQuery("#nf-field-" + semesterID).append(selectedCampusSemesterHTML); 2796 2797 jQuery("#nf-field-" + semesterID + " option").each((index, option) => { 2798 let optionSemestersProgid = jQuery(option).attr('data-program-solo-id'); 2799 2800 if (typeof optionSemestersProgid !== 'undefined' && semesterProgValue !== '') { 2801 optionSemestersProgid = optionSemestersProgid.split(' '); 2802 var Semester_len = optionSemestersProgid.length; 2803 2804 if (optionSemestersProgid.indexOf(semesterProgValue) === 0) { 2805 //option.setAttribute('selected','selected'); 2806 } 2807 if (optionSemestersProgid.indexOf(semesterProgValue) === -1 && option.value !== '') { 2808 jQuery(option).remove(); 2809 } 2810 } 2811 }); 2812 2813 jQuery("#nf-field-" + semesterID + " optgroup").each((index, option) => { 2814 if (jQuery(option).is(':empty')) { 2815 jQuery(option).remove(); 2816 } 2817 }); 2818 }); 2819 2820 jQuery("#nf-field-" + semesterID).change(() => { 2821 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 2822 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 2823 }); 2824 2825 } 2826 2827} 2828 2829function singleSemesterSelect(semesterID, campusID) { 2830 var $select = jQuery("#nf-field-" + semesterID); 2831 var optionsWithValue = $select.find("option").filter(function () { 2832 return jQuery(this).val().trim() !== ""; 2833 }); 2834 2835 var optionCountWithValue = optionsWithValue.length; 2836 2837 if (optionCountWithValue === 1) { 2838 optionsWithValue.prop("selected", true); 2839 jQuery("#nf-field-" + semesterID).trigger('change'); 2840 } 2841} 2842 2843// End for semester 2844 2845function nonChainedDropDowns(programID, programLabelID, campusID, campusLabelID, campusDataField) { 2846 // Initialize both dropdowns with default options 2847 initializeDropdownWithDefault(programID, '--'); 2848 initializeDropdownWithDefault(campusID, '--'); 2849 2850 jQuery("#nf-field-" + campusID).change(function () { 2851 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 2852 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 2853 }); 2854 2855 jQuery("#nf-field-" + programID).change(function () { 2856 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 2857 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 2858 2859 if (typeof campusDataField !== 'undefined') { 2860 const dataSoloCampusID = jQuery("#nf-field-" + programID + " option:selected").data('solo-campus-id'); 2861 jQuery("#nf-field-" + campusDataField.id).val(dataSoloCampusID).trigger('change'); 2862 if (typeof dataSoloCampusID !== 'undefined') { 2863 const soloCampuses = Object.keys(ninjaFormDataJS.campusData).map(function (key) { 2864 return ninjaFormDataJS.campusData[key]; 2865 }); 2866 const soloCampus = soloCampuses.find(o => o.campus_value == dataSoloCampusID); 2867 if (typeof soloCampus !== 'undefined') { 2868 jQuery("#nf-field-" + campusLabelID).val(soloCampus.campus_label).trigger('change'); 2869 } 2870 } 2871 } 2872 }); 2873} 2874 2875// Non-chained dropdown behavior for forms without school (useDynamic === '') 2876function nonChainedDropDownsNoSchool(programID, programLabelID, campusID, campusLabelID, semesterID, semesterLabelID, campusDataField, countryFieldId, stateFieldId) { 2877 2878 // Initialize all dropdowns with their full options including defaults 2879 // Initialize program field with all options 2880 const $programSelect = jQuery('#nf-field-' + programID); 2881 $programSelect.find('option').remove(); 2882 $programSelect.find('optgroup').remove(); 2883 $programSelect.append('<option value="" label="">Choose Program</option>'); 2884 $programSelect.append(ninjaFormDataJS.programsHTML); 2885 2886 // Initialize campus field with all options 2887 const $campusSelect = jQuery('#nf-field-' + campusID); 2888 $campusSelect.find('option').remove(); 2889 $campusSelect.find('optgroup').remove(); 2890 $campusSelect.append('<option value="" label="">Choose Campus</option>'); 2891 $campusSelect.append(ninjaFormDataJS.campusesHTML); 2892 2893 // Initialize semester field with all options 2894 const $semesterSelect = jQuery('#nf-field-' + semesterID); 2895 $semesterSelect.find('option').remove(); 2896 $semesterSelect.find('optgroup').remove(); 2897 $semesterSelect.append('<option value="" label="">Choose Semester</option>'); 2898 $semesterSelect.append(ninjaFormDataJS.semestersHTML); 2899
2900 // Set up change handlers for labels only (no filtering) 2901 jQuery("#nf-field-" + programID).change(function () { 2902 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 2903 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 2904 2905 // Handle campus data field if it exists 2906 if (typeof campusDataField !== 'undefined' && campusDataField) { 2907 const dataSoloCampusID = jQuery("#nf-field-" + programID + " option:selected").data('solo-campus-id'); 2908 jQuery("#nf-field-" + campusDataField.id).val(dataSoloCampusID).trigger('change'); 2909 if (typeof dataSoloCampusID !== 'undefined') { 2910 const soloCampuses = Object.keys(ninjaFormDataJS.campusData).map(function (key) { 2911 return ninjaFormDataJS.campusData[key]; 2912 }); 2913 const soloCampus = soloCampuses.find(o => o.campus_value == dataSoloCampusID); 2914 if (typeof soloCampus !== 'undefined') { 2915 jQuery("#nf-field-" + campusLabelID).val(soloCampus.campus_label).trigger('change'); 2916 } 2917 } 2918 } 2919 runRuleEngine(); 2920 }); 2921 2922 jQuery("#nf-field-" + campusID).change(function () { 2923 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 2924 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 2925 runRuleEngine(); 2926 }); 2927 2928 jQuery("#nf-field-" + semesterID).change(function () { 2929 const semesterLabel = jQuery("#nf-field-" + semesterID + " option:selected").text(); 2930 jQuery("#nf-field-" + semesterLabelID).val(semesterLabel).trigger('change'); 2931 runRuleEngine(); 2932 }); 2933 2934 jQuery("#nf-field-" + countryFieldId?.id).change(function () { 2935 runRuleEngine(); 2936 }) 2937 jQuery("#nf-field-" + stateFieldId?.id).change(function () { 2938 runRuleEngine(); 2939 }) 2940 2941 function runRuleEngine() { 2942 2943 const values = { 2944 program: jQuery("#nf-field-" + programID).val(), 2945 campus: jQuery("#nf-field-" + campusID).val(), 2946 semester: jQuery("#nf-field-" + semesterID).val(), 2947 country: jQuery("#nf-field-" + countryFieldId?.id)?.val(), 2948 state: jQuery("#nf-field-" + stateFieldId?.id)?.val(), 2949 }; 2950 2951 const fieldIDs = { 2952 program: programID, 2953 campus: campusID, 2954 semester: semesterID, 2955 country: countryFieldId?.id, 2956 state: stateFieldId?.id 2957 }; 2958 2959 // STEP 1: RESET everything first 2960 resetRuleEffects(fieldIDs); 2961 2962 // STEP 2: Apply rules fresh 2963 processRules(ninjaFormDataJS.rulesData, values, fieldIDs); 2964 } 2965 2966 // Since Program, Campus, and Semester are independent (non-chained) here, 2967 // auto-select each one on load if it only has a single available option. 2968 autoSelectSingleOption(programID); 2969 autoSelectSingleOption(campusID); 2970 autoSelectSingleOption(semesterID); 2971 2972 runRuleEngine(); // run once on load 2973} 2974 2975function selectProgramCampusUTM(programID, programLabelID, campusID, campusLabelID, campusTarget, utmCampus, utmProgram, dynamicFormType) { 2976 if (dynamicFormType === 'program' && typeof utmProgram !== 'undefined' && utmProgram.value) { 2977 selectProgramUTM(programID, programLabelID, utmProgram); 2978 } 2979 if (typeof campusTarget !== 'undefined') { 2980 const dataSoloCampusID = jQuery("#nf-field-" + programID + " option:selected").data('solo-campus-id'); 2981 jQuery("#nf-field-" + campusTarget.id).val(dataSoloCampusID).trigger('change'); 2982 if (typeof dataSoloCampusID !== 'undefined') { 2983 const soloCampuses = Object.keys(ninjaFormDataJS.campusData).map(function (key) { 2984 return ninjaFormDataJS.campusData[key]; 2985 }); 2986 const soloCampus = soloCampuses.find(o => o.campus_value == dataSoloCampusID); 2987 if (typeof soloCampus !== 'undefined') { 2988 jQuery("#nf-field-" + campusLabelID).val(soloCampus.campus_label).trigger('change'); 2989 } 2990 } 2991 } 2992 else if (utmCampus.value) { 2993 jQuery('#nf-field-' + campusID).val(utmCampus.value).trigger('change'); 2994 const campusLabel = jQuery("#nf-field-" + campusID + " option:selected").text(); 2995 jQuery("#nf-field-" + campusLabelID).val(campusLabel).trigger('change'); 2996 } 2997 if (dynamicFormType !== 'program' && typeof utmProgram !== 'undefined' && utmProgram.value) { 2998 selectProgramUTM(programID, programLabelID, utmProgram); 2999 } 3000} 3001 3002function selectProgramUTM(programID, programLabelID, utmProgram) { 3003 if (utmProgram.value) { 3004 jQuery('#nf-field-' + programID).val(utmProgram.value).trigger('change'); 3005 const programLabel = jQuery("#nf-field-" + programID + " option:selected").text(); 3006 jQuery("#nf-field-" + programLabelID).val(programLabel).trigger('change'); 3007 } 3008} 3009 3010function bindChangesPartChange(timezonefillID, submitButtonID) { 3011 const bindOnChangesOnPartChange = Marionette.Object.extend({ 3012 initialize: function () { 3013 const fieldsChannel = Backbone.Radio.channel('fields'); 3014 this.listenTo(fieldsChannel, 'change:model', this.fieldModelChange); 3015 }, 3016 fieldModelChange: (fieldModel, formModel, conditionModel) => { 3017 if (fieldModel.changed.visible !== true) return; 3018 if ('time_zone_drop_down' == fieldModel.get('key')) { 3019 forumlateTimeZones(fieldModel.get('id'), fieldModel.get('value'), timezonefillID, submitButtonID); 3020 } 3021 } 3022 }); 3023 new bindOnChangesOnPartChange(); 3024} 3025 3026function forumlateTimeZones(modelID, modelValue, timezonefillID, submitButtonID) { 3027 jQuery("#nf-field-" + modelID).change(function () { 3028 if (modelValue) { 3029 const fieldValue = jQuery("#nf-field-" + modelID).val(); 3030 jQuery("#nf-field-" + modelID).parent().append('<img src="' + ninjaFormDataJS.loadingGifPath + '" alt="tcspp-loader" class="timezone-loader" width="118" height="15" />'); 3031 jQuery("#nf-field-" + modelID).prop('disabled', true); 3032 jQuery('#nf-field-' + submitButtonID).prop('disabled', true); 3033 jQuery.ajax({
3034 url: "https://maps.googleapis.com/maps/api/geocode/json?address=" + fieldValue + '&language=en' + "&key=" + ninjaFormDataJS.googleAPIKey, 3035 cache: false, 3036 dataType: "json", 3037 type: "GET", 3038 complete: function () { 3039 jQuery(".timezone-loader").remove(); 3040 jQuery("#nf-field-" + modelID).prop('disabled', false); 3041 jQuery('#nf-field-' + submitButtonID).prop('disabled', false); 3042 }, 3043 success: function (result, success) { 3044 if (success == 'success') { 3045 const lat = result.results[0].geometry.location.lat; 3046 const lng = result.results[0].geometry.location.lng; 3047 const timestamp = Math.floor((new Date).getTime() / 1000); 3048 jQuery.ajax({ 3049 url: "https://maps.googleapis.com/maps/api/timezone/json?location=" + lat + "," + lng + "×tamp=" + timestamp + '&language=en' + "&key=" + ninjaFormDataJS.googleAPIKey, 3050 cache: false, 3051 dataType: "json", 3052 type: "GET", 3053 success: function (result, success) { 3054 const dst = Number(result.dstOffset); 3055 const raw = Number(result.rawOffset); 3056 const adjustedTime = dst + raw; 3057 const isNegative = Math.sign(adjustedTime); 3058 const baseNums = Math.abs(adjustedTime); 3059 if (baseNums > 0) { 3060 const timeinHours = secondsTimeSpanToHMS(baseNums); 3061 let appendDig = ""; 3062 if (isNegative < 0) appendDig = '-'; 3063 else appendDig = '+'; 3064 const timeString = appendDig + timeinHours; 3065 jQuery("#nf-field-" + timezonefillID).val(timeString).trigger('change'); 3066 } 3067 }, 3068 error: function (result, success) { } 3069 }); 3070 } 3071 } 3072 }); 3073 } 3074 }); 3075} 3076 3077function secondsTimeSpanToHMS(seconds) { 3078 const hours = Math.floor(seconds / 3600); 3079 seconds -= hours * 3600; 3080 const minutes = Math.floor(seconds / 60); 3081 seconds -= minutes * 60; 3082 return (hours < 10 ? '0' + hours : hours) + ":" + (minutes < 10 ? '0' + minutes : minutes); //zero padding on minutes and seconds 3083} 3084 3085function setupAddressAutocomplete(countryFieldId) { 3086 jQuery(`#nf-field-${countryFieldId}`).prop('autocomplete', 'country'); 3087 if (typeof jQuery(`#nf-field-${countryFieldId}`)[0] !== 'undefined') { 3088 clearInterval(autocompleteSetupInterval); 3089 } 3090} 3091// NOTE: useDynamicCampusProgram(form) now lives in one place, alongside the 3092// other form-field selector helpers above (see getFormDataSyncSetting).
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.