1/** 2 * static/js/portal-scripts.js 3 * 4 * Scripts for GLA Portal Section Only 5 * 6 */ 7 8var MYCACHEBUSTER = 1; 9 10// Define some globals if they are not defined yet (parent HTML file should define these) 11if (typeof applicationScodeUnformattedPrice === "undefined") { 12 var applicationScodeUnformattedPrice = ""; 13} 14if (typeof applicationScodeFormattedPrice === "undefined") { 15 var applicationScodeFormattedPrice = ""; 16} 17if (typeof applicationDiscountAppliesToDueNow === "undefined") { 18 var applicationDiscountAppliesToDueNow = false; 19} 20if (typeof applicationScodeUnformattedDueNowPrice === "undefined") { 21 applicationScodeUnformattedDueNowPrice = ""; 22} 23if (typeof applicationScodeFormattedDueNowPrice === "undefined") { 24 applicationScodeFormattedDueNowPrice = ""; 25} 26if (typeof applicationDiscountPercent === "undefined") { 27 applicationDiscountPercent = 0; 28} 29 30(function ($) { 31 // Polyfill for browsers w/o HTML5 form validation (*cough* Safari ) 32 if($.webshims) { $.webshims.polyfill('forms'); } 33 34 $(function () { 35 var $line_item_template = $('.line-item.trip').first().clone(); 36 var selected_products = []; 37 38 // Grab data out of the multi value field 39 if ( $('#id_products').val() != '' ) { 40 $('#id_products :selected').each(function(i, option){ 41 toggleTrip($(option).val(), 'add', true); 42 }); 43 // Update Scholarship if it is applied and is discount percent 44 if ($('#scholarship-discount-percent').length) { 45 applicationDiscountPercent = parseFloat($('#scholarship-discount-percent').text()); 46 } 47 } 48 49 $(document).ready(function() { 50 if (typeof ENROLL_IS_GROUP !== 'undefined' && ENROLL_IS_GROUP === 'True') { 51 if (typeof ENROLL_IS_CHAPERONE !== 'undefined' && ENROLL_IS_CHAPERONE === 'True') { 52 // Convert placeholders for signature fields to use "Chaperone" text 53 54 // Terms and conditions form 55 if ($('#id_parent_signature_terms').length) { 56 var parentSigTermsEle_Placeholder = $('#id_parent_signature_terms').attr('placeholder'); 57 var chaperoneSigTermsEle_Placeholder = parentSigTermsEle_Placeholder.replace('Parent', 'Chaperone'); 58 $('#id_parent_signature_terms').attr('placeholder', chaperoneSigTermsEle_Placeholder); 59 } 60 61 // Medical Form 62 if ($('#id_parent_signature_medical').length) { 63 var parentSigMedicalEle_Placeholder = $('#id_parent_signature_medical').attr('placeholder'); 64 var chaperoneSigMedicalEle_Placeholder = parentSigMedicalEle_Placeholder.replace('Parent', 'Chaperone'); 65 $('#id_parent_signature_medical').attr('placeholder', chaperoneSigMedicalEle_Placeholder); 66 var parentSigMedicalLabelText = $('label[for="id_parent_signature_medical"]').text(); 67 var chaperoneSigMedicalLabelText = parentSigMedicalLabelText.replace('Parent', 'Chaperone'); 68 $('label[for="id_parent_signature_medical"]').text(chaperoneSigMedicalLabelText); 69 } 70 } 71 72 // Code of conduct page change placeholder for student signature 73 if ($('#id_student_signature_coc').length) { 74 var studentSigCocEle_Placeholder = $('#id_student_signature_coc').attr('placeholder'); 75 var travelerSigCocEle_Placeholder = studentSigCocEle_Placeholder.replace('Student', 'Traveler'); 76 $('#id_student_signature_coc').attr('placeholder', travelerSigCocEle_Placeholder); 77 var studentSigCocLabelText = $('label[for="id_student_signature_coc"]').text(); 78 var travelerSigCocLabelText = studentSigCocLabelText.replace('Student', 'Traveler'); 79 $('label[for="id_student_signature_coc"]').text(travelerSigCocLabelText); 80 } 81 } 82 }); 83 84 // Set product selection based on Select button click 85 $(document).on("click", '.product-select > a', function(e){ 86 e.preventDefault(); 87 var product_id = $(this).attr('href').split('-')[1]; 88 89 if ( $(this).hasClass('select-button') ) { 90 toggleTrip(product_id, 'add'); 91 } else { 92 toggleTrip(product_id, 'remove'); 93 } 94 }); 95 96 $(document).on("click", '.product-date-select-buttons > a', function(e){ 97 e.preventDefault(); 98 var parentEle = $(this).parent(); 99 100 if ($(this).hasClass('date-select-button')) { 101 parentEle.parent().find('td > .product-date-select-section').show(); 102 parentEle.closest('.product-name-grouping').removeClass('collapsed').addClass('expanded'); 103 parentEle.addClass('selected'); 104 } else { 105 parentEle.removeClass('selected'); 106 parentEle.closest('.product-name-grouping').removeClass('expanded').addClass('collapsed'); 107 parentEle.parent().find('td > .product-date-select-section').hide(); 108 } 109 }); 110 111 // In line items table remove link 112 $(document).on("click", '.line-item a.remove', function(e){ 113 e.preventDefault(); 114 var product_id = $(this).attr('href').split('-')[1]; 115 toggleTrip(product_id, 'remove'); 116 }); 117 118 // Smooth Scroll to Element on the Page 119 function scrollTo(target_selector) { 120 $('html,body').animate({ 121 scrollTop: $(target_selector).offset().top 122 }, 1000); 123 return false;
124 } 125 126 function formatScholarshipMoney(amount) { 127 return accounting.formatMoney(amount, { 128 symbol: '$', 129 precision: 2, 130 thousand: ',', 131 decimal: '.', 132 format: { 133 pos: '%s%v', 134 neg: '-%s%v', 135 zero: '%s%v' 136 } 137 }); 138 } 139 140 // Check if value is a date 141 function isDate(date) { 142 return (new Date(date) !== "Invalid Date" && !isNaN(new Date(date))) ? true : false; 143 } 144 145 // Check if a trip has start & end dates 146 function tripHasDates($trip) { 147 return ( isDate($trip.data('startdate')) && isDate($trip.data('enddate')) ) ? true : false; 148 } 149 150 // Check if a date range overlaps 151 // via http://stackoverflow.com/questions/22784883/check-if-more-than-two-date-ranges-overlap 152 function dateRangeOverlaps(a_start, a_end, b_start, b_end) { 153 if (a_start <= b_start && b_start <= a_end) return true; // b starts in a 154 if (a_start <= b_end && b_end <= a_end) return true; // b ends in a 155 if (b_start < a_start && a_end < b_end) return true; // a in b 156 return false; 157 } 158 159 // Check if trip's start and end dates overlap with an already added trip 160 function areDatesOverlapping($trip_a) { 161 if ( selected_products.length > 0 && tripHasDates($trip_a) ) { 162 var start_a = Date.parse($trip_a.data('startdate')); 163 var end_a = Date.parse($trip_a.data('enddate')); 164 var has_overlap = false; 165 166 $.each(selected_products, function() { 167 var $trip_b = $('#product-' + this); 168 if (tripHasDates($trip_b) && dateRangeOverlaps(start_a, end_a, Date.parse($trip_b.data('startdate')), Date.parse($trip_b.data('enddate'))) ) { 169 has_overlap = true; 170 return false; 171 } 172 }); 173 return has_overlap; 174 } else { 175 return false; 176 } 177 } 178 179 // Check if user is trying to choose more than one program where at least one is a group program 180 function checkGroupProgram($trip_a) { 181 if ( selected_products.length > 0 ) { 182 var failedCheck = false; 183 $.each(selected_products, function() { 184 var $trip_b = $('#product-' + this); 185 var isGroup = $trip_a.hasClass('group'); 186 if ($trip_b.hasClass('group') != isGroup) { 187 failedCheck = true; 188 return true; 189 } 190 }); 191 return failedCheck; 192 } else { 193 return false; 194 } 195 } 196 197 function slugifyDestinationName(name) { 198 return String(name || '') 199 .toLowerCase() 200 .trim() 201 .replace(/[^a-z0-9]+/g, '-') 202 .replace(/^-+|-+$/g, ''); 203 } 204 205 function getDestinationContextByProductId(productId) { 206 var product_basic_object = null; 207 208 for (var i = 0; i < product_to_destination_list.length; i++) { 209 if (product_to_destination_list[i].product_id == productId) { 210 product_basic_object = product_to_destination_list[i]; 211 break; 212 } 213 } 214 215 if (!product_basic_object) { 216 return null; 217 } 218 219 var destination_slug = ''; 220 var is_special = false;
221 222 if (product_basic_object.country_name == 'Multi-Program') { 223 destination_slug = 'multi-programs'; 224 is_special = true; 225 } else if (product_basic_object.country_name == 'Special Offers') { 226 destination_slug = 'special_offers'; 227 is_special = true; 228 } else if (product_basic_object.country_name == 'Fellowships') { 229 destination_slug = 'fellowships'; 230 is_special = true; 231 } else if (product_basic_object.country_name == 'Add-ons') { 232 destination_slug = 'addons'; 233 is_special = true; 234 } else { 235 destination_slug = slugifyDestinationName(product_basic_object.country_name); 236 if (product_basic_object.group) { 237 destination_slug += '_group'; 238 } 239 } 240 241 var targetYear = is_special 242 ? window.backendPayload.primary_year.toString() 243 : String(product_basic_object.year || window.backendPayload.primary_year); 244 var yearData = is_special ? null : window.backendPayload.data_by_year[targetYear]; 245 246 return { 247 destination_slug: destination_slug, 248 is_special: is_special, 249 yearData: yearData, 250 targetYear: targetYear 251 }; 252 } 253 254 function ensureProductRowLoaded(productId) { 255 var $trip = $('#product-' + productId); 256 if ($trip.length) { 257 return $trip; 258 } 259 260 var destinationContext = getDestinationContextByProductId(productId); 261 if (!destinationContext) { 262 return $trip; 263 } 264 265 addTableToDestination( 266 destinationContext.destination_slug, 267 destinationContext.yearData, 268 destinationContext.targetYear 269 ); 270 271 return $('#product-' + productId); 272 } 273 274 // Adds or removes a trip 275 function toggleTrip(id, action, prevent_input) { 276 var $trip = $('#product-' + id); 277 278 // If trip is not on the DOM yet, then we must add it 279 if (!$trip[0]) { 280 var destinationContext = getDestinationContextByProductId(id); 281 if (destinationContext) { 282 addTableToDestination( 283 destinationContext.destination_slug, 284 destinationContext.yearData, 285 destinationContext.targetYear 286 ); 287 } 288 289 $trip = $('#product-' + id); 290 } 291 292 // Add 293 if ( action == 'add' || (!action && !$trip.hasClass('picked')) ) { // Can toggle if no action specified 294 if (areDatesOverlapping($trip)) { 295 alert("This trip overlaps another trip you selected. \r\n\nTo add this trip, please remove trips with overlapping dates first."); 296 return false; 297 } else if (checkGroupProgram($trip)) { 298 alert("You cannot choose both a group trip and a non-group trip. \r\n\nTo add this trip, please remove problematic trips."); 299 return false; 300 } else { 301 $trip.find('.product-select').addClass('selected'); 302 selected_products.push(id); 303 304 if ( !prevent_input ) { 305 $('#id_products').find("option[value="+id+"]").prop("selected", true); 306 scrollTo('#create-your-account'); 307 } 308 } 309 // Remove 310 } else { 311 $trip.find('.product-select').removeClass('selected'); 312 selected_products.splice(selected_products.indexOf(id), 1); // Removes product from array 313 $('#lineitem-' + id).slideUp(400); // Hides row in totals 314 315 if ( !prevent_input ) { 316 $('#id_products').find("option[value="+id+"]").prop("selected", false); 317 } 318 } 319 showOrHideGroupFields(); 320 highlightPicked(); 321 buildLineItems(); 322 updateTotals(); 323 } 324 325 // var originalOptions = $('#id_birthdate_year').html(); 326 var originalWhyDoINeedAnAccountText = $('#why-do-i-need-an-account').html(); 327 328 // Shows or hides group fields depending on group enrollment or not. 329 function showOrHideGroupFields() { 330 if (selected_products.length > 0 && $('#product-' + selected_products[0]).hasClass('group')) { 331 // Group enrollment, update form accordingly. 332 updateFormForGroupEnrollment(); 333 } else { 334 // Non-Group enrollment, use default student values here 335 updateFormForNonGroupEnrollment(); 336 } 337 } 338 339 /** 340 * Updates the enrollment form fields to be used for a group enrollment option. 341 */ 342 function updateFormForGroupEnrollment() { 343 // Change label of student email address field 344 $('label[for="id_email"]').html('Traveler\'s Email Address'); 345 346 // Update help text on the right hand side of step 2 347 var newWhyDoINeedAnAccountText = originalWhyDoINeedAnAccountText; 348 // Replace 'student' with 'traveler' 349 if (newWhyDoINeedAnAccountText) { 350 newWhyDoINeedAnAccountText = newWhyDoINeedAnAccountText.replace(new RegExp('student', 'g'), 'traveler'); 351 $('#why-do-i-need-an-account').html(newWhyDoINeedAnAccountText); 352 } 353 354 // Change label of contact info step (3) personal info portion 355 $('#enrollment_contact-info_personal-info-header').html('Traveler\'s Information'); 356 357 // Change label of school question 358 $('label[for="id_name_of_school_attending"]').html('Name of your school or organization'); 359 360 // Remove T-shirt size option 361 $('#id_t_shirt_size').parent('.form-group').css('display', 'none'); 362 $('#id_t_shirt_size').parent('.form-group').removeClass('required'); 363 $('#id_t_shirt_size').prop('required', ''); 364 365 // Show is_chaperone checkbox 366 $('#is_chaperone_checkbox_wrapper').show(); 367 368 // // Change dropdown options for year of birth (need to save the previous options in-memory) 369 // var options = '<option value="" selected="selected">-- Year --</option>'; 370 // var currentYear = (new Date()).getFullYear(); 371 // for (var year = currentYear; year >= 1900; year--) { 372 // options = options 373 // + '<option value="' 374 // + year 375 // + '">' 376 // + year 377 // + '</option>'; 378 // } 379 // $('#id_birthdate_year').html(options); 380 381 // Change parent to emergency contact 382 $('#primary-parent-info').html('Emergency Contact'); 383 $('label[for=id_parent_email]').html('Emergency Contact\'s Email Address'); 384 $('label[for=id_add_second_parent]').html('Add a second emergency contact?'); 385 $('#secondary-parent-info').html('Secondary Emergency Contact'); 386 $('label[for=id_secondary_parent_email]').html('Emergency Contact\'s Email Address'); 387 $('.parent-email-different-text').html( 388 'The emergency contact\'s email address must be different from the' 389 + ' traveler\'s email address. Both must be valid since you will' 390 + ' receive important communications related to your program' 391 + ' at this address.' 392 ); 393 394 // Change the student address label 395 $('#student-address-header').html('Traveler\'s Permanent Address'); 396 397 // Hide secondary parent address form 398 $('#second-parent-address').css('display', 'none'); 399 $('#second-parent-address .form-group').removeClass('required'); 400 $('#second-parent-address .form-group :input').prop('required', false); 401 402 // // Add event handler to hide parent address if student over 18 403 // var birthdate = new Date(); 404 // $('#id_birthdate_month').on('change', function() { 405 // birthdate.setMonth($('#id_birthdate_month').val());
406 // renderParentAddressDependingOnBday(birthdate); 407 // }); 408 // $('#id_birthdate_day').on('change', function() { 409 // birthdate.setDate($('#id_birthdate_day').val()); 410 // renderParentAddressDependingOnBday(birthdate); 411 // }); 412 // $('#id_birthdate_year').on('change', function() { 413 // birthdate.setYear($('#id_birthdate_year').val()); 414 // renderParentAddressDependingOnBday(birthdate); 415 // }); 416 // // Default to what is selected already 417 // if ($('#id_birthdate_month').val()) { 418 // birthdate.setMonth($('#id_birthdate_month').val()); 419 // } 420 // if ($('#id_birthdate_day').val()) { 421 // birthdate.setDate($('#id_birthdate_day').val()); 422 // } 423 // if ($('#id_birthdate_year').val()) { 424 // birthdate.setYear($('#id_birthdate_year').val()); 425 // } 426 // renderParentAddressDependingOnBday(birthdate); 427 428 // If no parent, do nothing 429 if (typeof no_parent != 'undefined' && no_parent == 'True') { 430 $('.parent-emergency-contact-fields').css('display', 'block'); 431 $('#primary-parent-address .form-group').addClass('required'); 432 $('#primary-parent-address .form-group :input').prop('required', true); 433 $('.parent-emergency-contact-address-fields').css('display', 'block'); 434 $('.parent-emergency-contact-address-fields .form-group').addClass('required'); 435 $('.parent-emergency-contact-address-fields .form-group :input').prop('required', true); 436 } 437 // If no emergency contact, show emergency contact form 438 if (typeof no_emerg != 'undefined' && no_emerg == 'True') { 439 $('.parent-emergency-contact-fields').css('display', 'block'); 440 $('#primary-parent-address .form-group').addClass('required'); 441 $('#primary-parent-address .form-group :input').prop('required', true); 442 $('.parent-emergency-contact-address-fields').css('display', 'none'); 443 $('.parent-emergency-contact-address-fields .form-group').removeClass('required'); 444 $('.parent-emergency-contact-address-fields .form-group :input').prop('required', false); 445 } else { 446 // If have both, then don't show anything 447 $('.parent-emergency-contact-fields').css('display', 'none'); 448 $('#primary-parent-address .form-group').removeClass('required'); 449 $('#primary-parent-address .form-group :input').prop('required', false); 450 $('.parent-emergency-contact-address-fields').css('display', 'none'); 451 $('.parent-emergency-contact-address-fields .form-group').removeClass('required'); 452 $('.parent-emergency-contact-address-fields .form-group :input').prop('required', false); 453 } 454 } 455 456 /** 457 * Updates the enrollment form fields to not use the group enrollment option. 458 */ 459 function updateFormForNonGroupEnrollment() { 460 // Revert label of student email address field 461 $('label[for="id_email"]').html('Student\'s Email Address'); 462 463 // Revert help text on the right hand side of step 2 464 $('#why-do-i-need-an-account').html(originalWhyDoINeedAnAccountText); 465 466 // Change label of contact info step (3) personal info portion 467 $('#enrollment_contact-info_personal-info-header').html('Student\'s Information'); 468 469 // Change label of school question 470 $('label[for=id_name_of_school_attending]').html('Name of School Attending'); 471 472 // Re-add T-shirt size option 473 $('#id_t_shirt_size').parent('.form-group').css('display', 'block'); 474 $('#id_t_shirt_size').parent('.form-group').addClass('required'); 475 $('#id_t_shirt_size').prop('required', 'required'); 476 477 // // Change dropdown options for year of birth 478 // $('#id_birthdate_year').html(originalOptions); 479 480 // Hide is_chaperone checkbox (and un-check is_chaperone field) 481 $('#is_chaperone_checkbox_wrapper').hide(); 482 $('input[name="is_chaperone"]').prop('checked', false); 483 484 // Change emergency contact back to parent 485 $('#primary-parent-info').html('Primary Parent\'s Information'); 486 $('label[for=id_parent_email]').html('Parent\'s Email Address'); 487 $('label[for=id_add_second_parent]').html('Add a second parent?'); 488 $('#secondary-parent-info').html('Secondary Parent\'s Information'); 489 $('label[for=id_secondary_parent_email]').html('Parent\'s Email Address'); 490 $('.parent-email-different-text').html( 491 'The parent\'s email address must be different from the' 492 + ' student\'s email address. Both must be valid since you will' 493 + ' receive important communications related to your program' 494 + ' at this address.' 495 ); 496 497 // Change the student address label 498 $('#student-address-header').html('Student\'s Address'); 499 500 // Show secondary parent address fields if checked 501 hide_or_show_second_parent_form(); 502 503 // // Take off event handler that removes the primary parent address if student over 18 504 // $('#id_birthdate_month').off('change'); 505 // $('#id_birthdate_day').off('change'); 506 // $('#id_birthdate_year').off('change'); 507 508 // Show parent address 509 $('#primary-parent-address').css('display', 'block'); 510 $('#primary-parent-address .form-group').addClass('required'); 511 $('#primary-parent-address .form-group :input').prop('required', true); 512 513 // Show parent information fields for update 514 $('.parent-emergency-contact-fields').css('display', 'block'); 515 $('.parent-emergency-contact-fields .form-group').addClass('required'); 516 $('.parent-emergency-contact-fields .form-group :input').prop('required', true); 517 518 // If no parent, then show parent address fields 519 if (typeof no_parent != 'undefined' && no_parent == 'True') { 520 $('.parent-emergency-contact-address-fields').css('display', 'block'); 521 $('.parent-emergency-contact-address-fields .form-group').addClass('required'); 522 $('.parent-emergency-contact-address-fields .form-group :input').prop('required', true); 523 } 524 } 525 526 // Displays the parent address if the birthday passed in is under 18 years old
527 function renderParentAddressDependingOnBday(birthday) { 528 if (((new Date().getTime() - birthday.getTime()) / (1000 * 60 * 60 * 24 * 365)) >= 18) { 529 $('#primary-parent-address').css('display', 'none'); 530 $('#primary-parent-address .form-group').removeClass('required'); 531 $('#primary-parent-address .form-group :input').prop('required', false); 532 } else { 533 $('#primary-parent-address').css('display', 'block'); 534 $('#primary-parent-address .form-group').addClass('required'); 535 $('#primary-parent-address .form-group :input').prop('required', true); 536 } 537 } 538 539 function findProductById(targetId, jsonData) { 540 var sourceData = jsonData || window.backendPayload || window.backend || {}; 541 var normalizedTargetId = String(targetId || '').trim(); 542 var specialCategoryTypeMap = { 543 'addon_groupings': 'Add-On', 544 'fellowship_groupings': 'Fellowship', 545 'special_offers_groupings': 'Program', 546 'multi_program_groupings': 'Program' 547 }; 548 549 function normalizeId(idValue) { 550 return String(idValue || '').trim(); 551 } 552 553 function toProductResult(product, inferredType) { 554 if (!product) { 555 return null; 556 } 557 558 return { 559 'prodid': product.id || product.product_id, 560 'summary': product.program_summary || product.name, 561 'tuition': product.tuition, 562 'duenow': product.due_upon_application, 563 'name': product.name, 564 'country': product.country, 565 'startdate': product.start_date, 566 'enddate': product.end_date, 567 'type': inferredType || 'Program' 568 }; 569 } 570 571 function findInProgramGroupings(groupings, defaultType) { 572 if (!Array.isArray(groupings)) { 573 return null; 574 } 575 576 for (var i = 0; i < groupings.length; i++) { 577 var grouping = groupings[i] || {}; 578 var years = grouping.years || []; 579 580 for (var y = 0; y < years.length; y++) { 581 var year = years[y] || {}; 582 var products = year.products || []; 583 584 for (var p = 0; p < products.length; p++) { 585 var product = products[p] || {}; 586 if (normalizeId(product.id) === normalizedTargetId) { 587 return toProductResult(product, defaultType || 'Program'); 588 } 589 } 590 } 591 } 592 593 return null; 594 } 595 596 function findInDestinationProgramList(destinationProgramList) { 597 if (!Array.isArray(destinationProgramList)) { 598 return null; 599 } 600 601 for (var i = 0; i < destinationProgramList.length; i++) { 602 var destinationEntry = destinationProgramList[i] || {}; 603 604 var found = findInProgramGroupings(destinationEntry.individual_programs, 'Program'); 605 if (found) { 606 return found; 607 } 608 609 found = findInProgramGroupings(destinationEntry.group_programs, 'Program'); 610 if (found) { 611 return found; 612 } 613 } 614 615 return null; 616 } 617 618 var yearsData = sourceData.data_by_year || {}; 619 for (var yearKey in yearsData) { 620 if (!Object.prototype.hasOwnProperty.call(yearsData, yearKey)) { 621 continue; 622 } 623 624 var yearPayload = yearsData[yearKey] || {}; 625 var foundProduct = findInDestinationProgramList(yearPayload.destination_program_list); 626 if (foundProduct) { 627 return foundProduct; 628 } 629 } 630 631 var specialCategories = sourceData.special_categories || {}; 632 for (var categoryKey in specialCategories) { 633 if (!Object.prototype.hasOwnProperty.call(specialCategories, categoryKey)) { 634 continue; 635 } 636 637 var foundSpecialProduct = findInProgramGroupings( 638 specialCategories[categoryKey], 639 specialCategoryTypeMap[categoryKey] || 'Program' 640 ); 641 if (foundSpecialProduct) { 642 return foundSpecialProduct; 643 } 644 } 645 646 // Last resort fallback for unexpected payload shape. 647 var fallbackResult = null; 648 function recursiveFallback(node) { 649 if (!node || typeof node !== 'object' || fallbackResult) { 650 return; 651 } 652 653 if (Array.isArray(node)) { 654 for (var i = 0; i < node.length; i++) { 655 recursiveFallback(node[i]); 656 } 657 return; 658 } 659 660 if ( 661 normalizeId(node.id) === normalizedTargetId && 662 node.tuition !== undefined && 663 node.due_upon_application !== undefined 664 ) { 665 fallbackResult = toProductResult(node, 'Program'); 666 return; 667 } 668 669 for (var key in node) { 670 if (Object.prototype.hasOwnProperty.call(node, key)) { 671 recursiveFallback(node[key]); 672 } 673 } 674 } 675 676 recursiveFallback(sourceData); 677 return fallbackResult; 678 } 679 680 // Highlights any Regions or Destinations that have a picked program 681 function highlightPicked() { 682 $('.region, .location-container, tr.product-row, tr.product-name-grouping').removeClass('picked'); 683 684 $.each(selected_products, function() { 685 var $trip = ensureProductRowLoaded(this); 686 687 if (!$trip.length) { 688 return true; 689 } 690 691 $trip.addClass('picked'); 692 $trip.closest('tr.product-name-grouping').addClass('picked'); 693 var $destination = $('#' + $trip.data('containerid')); 694 $destination.addClass('picked').closest('.region').addClass('picked'); 695 }); 696 } 697 698 // Builds the table of line items and total fees due at the bottom of the page 699 function buildLineItems() { 700 var isGroup = false;
701 var isFuture = false; 702 var hideAppFee = true; 703 var primaryYear = window.backendPayload.primary_year.toString().trim(); 704 705 if (selected_products.length > 0) { 706 $('.no-products-selected').hide(); 707 $('.program-totals').show(); 708 709 $.each(selected_products, function() { 710 var prod_id = this; 711 var $t = $('#product-' + prod_id); 712 if ($t.hasClass('group')) { isGroup = true; } 713 714 // Check product year 715 var productInfo = product_to_destination_list.filter(function(p) { 716 return p.product_id == prod_id; 717 })[0]; 718 719 if (productInfo && productInfo.year) { 720 if (productInfo.year.toString().trim() !== primaryYear) { 721 isFuture = true; 722 } 723 } 724 }); 725 } else { 726 $('.program-totals').hide(); 727 $('.no-products-selected').show(); 728 } 729 730 $('.line-item').remove(); 731 732 // Add Trip Line Items 733 $.each(selected_products, function() { 734 var prod_id = this; 735 var $trip = $('#product-' + prod_id); 736 737 var tripData = $trip.data() || {}; 738 if (tripData.tuition === undefined || tripData.duenow === undefined) { 739 tripData = findProductById(prod_id, window.backendPayload) || tripData; 740 } 741 742 if (tripData.type == 'Program') { hideAppFee = false; } 743 console.log('hideappfee? ', hideAppFee); 744 745 746 var $line_item = $line_item_template.clone(); 747 $line_item.attr('id','#lineitem-' + prod_id); 748 $line_item.find('.summary').html(tripData.summary || $trip.data('summary') || ''); 749 $line_item.find('.total').html(accounting.formatMoney(tripData.tuition || 0, "$", 2)); 750 $line_item.find('.duenow').html(accounting.formatMoney(tripData.duenow || 0, "$", 2)); 751 $line_item.find('.action').html('<a href="#product-' + (tripData.prodid || prod_id) + '" class="remove">remove</a>'); 752 $line_item.appendTo($('tbody.trip-lineitems')); 753 }); 754 755 // Add Fee Line Items 756 $.each(fees, function(index, fee) { 757 var $line_item = $line_item_template.clone(); 758 var finalTotal, finalDue; 759 760 if (index === 0 && isGroup) { 761 finalTotal = fee.total_group; 762 finalDue = fee.duenow_group; 763 } else if (index === 0 && isFuture) { 764 finalTotal = fee.total_future; 765 finalDue = fee.duenow_future; 766 } else { 767 finalTotal = fee.total; 768 finalDue = fee.duenow; 769 } 770 771 $line_item.attr('id','#lineitem-' + fee.id); 772 $line_item.find('.summary').html(fee.summary); 773 $line_item.find('.total').html(accounting.formatMoney(finalTotal, "$", 2)); 774 $line_item.find('.duenow').html(accounting.formatMoney(finalDue, "$", 2)); 775 $line_item.appendTo($('tbody.fee-lineitems')); 776 }); 777 778 if (hideAppFee) { $('tbody.fee-lineitems').hide(); } 779 else { $('tbody.fee-lineitems').show(); } 780 } 781 782 /** 783 * Updates the total prices displayed on the final application step of this page. 784 * Handles Standard, Group, and Future Year fee logic using direct data lookup. 785 */ 786 function updateTotals() { 787 var total_tuition = 0; 788 var total_trips_duenow = 0; 789 var total_fees = 0; 790 var total_fees_duenow = 0; 791 var total_scholarships = 0; 792 var total_scholarships_due_now = 0; 793 794 var hideAppFee = true; 795 var isGroup = false; 796 var isFuture = false;
797 var primaryYear = window.backendPayload.primary_year.toString().trim(); 798 var parsedDiscountPercent = parseFloat(applicationDiscountPercent || 0); 799 var hasDiscountPercent = !isNaN(parsedDiscountPercent) && parsedDiscountPercent > 0; 800 801 // 1. Analyze selected products to determine tuition and fee types 802 $.each(selected_products, function() { 803 var prod_id = this; 804 var $trip = $('#product-' + prod_id); 805 var tripData = $trip.data() || {}; 806 807 if (tripData.tuition === undefined || tripData.duenow === undefined) { 808 tripData = findProductById(prod_id, window.backendPayload) || tripData; 809 } 810 811 // Sum Tuition (using || 0 in case the row isn't in the DOM yet) 812 total_tuition += parseFloat(tripData.tuition || 0); 813 total_trips_duenow += parseFloat(tripData.duenow || 0); 814 815 // Only show Application Fee if the product type is 'Program' 816 if (tripData.type === 'Program') { 817 hideAppFee = false; 818 } 819 820 // Check for Group status 821 if ($trip.hasClass('group')) { 822 isGroup = true; 823 } 824 825 // Check product year 826 var productInfo = product_to_destination_list.filter(function(p) { 827 return p.product_id == prod_id; 828 })[0]; 829 830 if (productInfo && productInfo.year) { 831 var tripYear = productInfo.year.toString().trim(); 832 // If the trip year is different from the primary year, it's a Future fee 833 if (tripYear !== primaryYear) { 834 isFuture = true; 835 } 836 } 837 }); 838 839 // 2. Calculate Fees based on priority: Group > Future > Standard 840 $.each(fees, function(index, fee) { 841 if (hideAppFee) return false; // Break loop if no programs selected 842 843 var feePrice = 0; 844 845 // Index 0 is typically the Application Fee 846 if (index === 0) { 847 if (isGroup) { 848 feePrice = fee.total_group; 849 } else if (isFuture) { 850 feePrice = fee.total_future; 851 } else { 852 feePrice = fee.total; 853 } 854 } else { 855 // Standard behavior for any secondary fees 856 feePrice = fee.total; 857 } 858 859 total_fees += parseFloat(feePrice || 0); 860 // Application fees are 100% due now 861 total_fees_duenow += parseFloat(feePrice || 0); 862 }); 863 864 // 3. Apply scholarship discounts 865 if (hasDiscountPercent) { 866 var percentDiscountAmount = -1 * (total_tuition) * (parsedDiscountPercent / 100); 867 total_scholarships += percentDiscountAmount; 868 $('.scholarship-total').html(formatScholarshipMoney(percentDiscountAmount)); 869 } else if (applicationScodeUnformattedPrice && applicationScodeUnformattedPrice.length > 0) { 870 var floatUnitPrice = parseFloat(applicationScodeUnformattedPrice); 871 if (!isNaN(floatUnitPrice) && floatUnitPrice !== 0) { 872 if (floatUnitPrice > 0) floatUnitPrice = -floatUnitPrice; 873 total_scholarships += floatUnitPrice; 874 } 875 } 876 877 if (applicationDiscountAppliesToDueNow && applicationScodeUnformattedDueNowPrice && applicationScodeUnformattedDueNowPrice.length > 0) { 878 var floatDueNowPrice = parseFloat(applicationScodeUnformattedDueNowPrice); 879 if (!isNaN(floatDueNowPrice) && floatDueNowPrice !== 0) { 880 if (floatDueNowPrice > 0) floatDueNowPrice = -floatDueNowPrice; 881 total_scholarships_due_now += floatDueNowPrice; 882 } 883 } 884 885 // 4. Final Grand Totals 886 var grand_total = total_fees + total_tuition + total_scholarships; 887 var grand_total_due_now = total_trips_duenow + total_fees_duenow + total_scholarships_due_now; 888 889 // Prevent negative displays 890 if (grand_total < 0) grand_total = 0; 891 if (grand_total_due_now < 0) grand_total_due_now = 0; 892 893 // 5. Update UI Labels 894 $('.grand-total').html(accounting.formatMoney(grand_total, "$", 2)); 895 $('.total-duenow').html(accounting.formatMoney(grand_total_due_now, "$", 2)); 896 } 897 898 // Show Secondary Parent's Information form if "Add a second parent?" checkbox is checked 899 function hide_or_show_second_parent_form() { 900 if ($('#id_add_second_parent').is(':checked')) { 901 if (selected_products.length <= 0 || !$('#product-' + selected_products[0]).hasClass('group')) { 902 $('#second-parent-address').css('display', 'block'); 903 $('#second-parent-address .form-group').addClass('required'); 904 $('#second-parent-address .form-group :input').prop('required', true); 905 } 906 $('#second-parent-info').css('display', 'block'); 907 $('#second-parent-info .form-group').addClass('required'); 908 $('#second-parent-info .form-group :input').prop('required', true); 909 } else { 910 $('#second-parent-info').css('display', 'none'); 911 $('#second-parent-address').css('display', 'none'); 912 $('#second-parent-info .form-group').removeClass('required'); 913 $('#second-parent-address .form-group').removeClass('required'); 914 $('#second-parent-info .form-group :input').prop('required', false); 915 $('#second-parent-address .form-group :input').prop('required', false); 916 } 917 } 918 $('#id_add_second_parent').change(function () { 919 hide_or_show_second_parent_form(); 920 }); 921 $(document).ready(function () { 922 hide_or_show_second_parent_form(); 923 if (selected_products.length > 0) showOrHideGroupFields(); 924 }); 925 926 // Show Advisor's Information form if "Add an advisor?" checkbox is checked 927 function hide_or_show_advisor_form() { 928 if ($('#id_add_advisor').is(':checked')) { 929 $('#advisor-info').css('display', 'block'); 930 $('#advisor-info .form-group').addClass('required'); 931 $('#advisor-info .form-group :input').prop('required', true); 932 } else { 933 $('#advisor-info').css('display', 'none'); 934 $('#advisor-info .form-group').removeClass('required'); 935 $('#advisor-info .form-group :input').prop('required', false); 936 } 937 } 938 // Show error message for privacy policy checkbox if unchecked 939 function hide_or_show_privacy_policy_error() { 940 if ($('#id_privacy_policy').is(':checked')) { 941 $('#privacy_policy_error').css('display', 'none'); 942 $('#id_privacy_policy').closest('.form-group').removeClass('has-error'); 943 } else { 944 $('#privacy_policy_error').css('display', 'block'); 945 $('#id_privacy_policy').closest('.form-group').addClass('has-error'); 946 } 947 } 948 $('#id_add_advisor').change(function () { 949 hide_or_show_advisor_form(); 950 }); 951 $('#id_privacy_policy').change(function () { 952 hide_or_show_privacy_policy_error(); 953 }); 954 $(document).ready(function () { 955 hide_or_show_advisor_form(); 956 }); 957 958 // Fill Parent Billing if "Same as Student" checkbox is checked 959 function set_parent_billing(checked_only) { 960 if ($('#id_parent_address_same_as_student').is(':checked')) { 961 $('#id_parent_mailing_street').val($('#id_student_mailing_street').val()).prop('readonly', true); 962 $('#id_parent_mailing_city').val($('#id_student_mailing_city').val()).prop('readonly', true);; 963 $('#id_parent_mailing_state').val($('#id_student_mailing_state').val()).prop('readonly', true);; 964 $('#id_parent_mailing_postal_code').val($('#id_student_mailing_postal_code').val()).prop('readonly', true);
965 $('#id_parent_mailing_country').val( $('#id_student_mailing_country option:selected').val() ).attr('readonly', true).css('pointer-events','none'); 966 } else if ( !checked_only) { 967 //Clear on uncheck 968 $('#id_parent_mailing_street').val('').prop('readonly', false); 969 $('#id_parent_mailing_city').val('').prop('readonly', false); 970 $('#id_parent_mailing_state').val('').prop('readonly', false); 971 $('#id_parent_mailing_postal_code').val('').prop('readonly', false); 972 $('#id_parent_mailing_country').val('').attr('readonly', false).css('pointer-events','auto'); 973 } 974 975 set_secondary_parent_billing(true); 976 } 977 $('#id_parent_address_same_as_student').change(function () { 978 set_parent_billing() 979 }); 980 $('#id_student_mailing_street, #id_student_mailing_city, #id_student_mailing_state, #id_student_mailing_postal_code, #id_student_mailing_country').blur(function(){ 981 set_parent_billing(true); 982 }); 983 984 // Fill Secondary Parent Billing if "Same as Primary Parent" checkbox is checked 985 function set_secondary_parent_billing(checked_only) { 986 if ($('#id_secodary_parent_address_same_as_parent').is(':checked')) { 987 $('#id_secondary_parent_mailing_street').val($('#id_parent_mailing_street').val()).prop('readonly', true); 988 $('#id_secondary_parent_mailing_city').val($('#id_parent_mailing_city').val()).prop('readonly', true);; 989 $('#id_secondary_parent_mailing_state').val($('#id_parent_mailing_state').val()).prop('readonly', true);; 990 $('#id_secondary_parent_mailing_postal_code').val($('#id_parent_mailing_postal_code').val()).prop('readonly', true); 991 $('#id_secondary_parent_mailing_country').val( $('#id_parent_mailing_country option:selected').val() ).attr('readonly', true).css('pointer-events','none'); 992 } else if ( !checked_only) { 993 //Clear on uncheck 994 $('#id_secondary_parent_mailing_street').val('').prop('readonly', false); 995 $('#id_secondary_parent_mailing_city').val('').prop('readonly', false); 996 $('#id_secondary_parent_mailing_state').val('').prop('readonly', false); 997 $('#id_secondary_parent_mailing_postal_code').val('').prop('readonly', false); 998 $('#id_secondary_parent_mailing_country').val('').attr('readonly', false).css('pointer-events','auto'); 999 } 1000 } 1001 $('#id_secodary_parent_address_same_as_parent').change(function () { 1002 set_secondary_parent_billing() 1003 }); 1004 $('#id_parent_mailing_street, #id_parent_mailing_city, #id_parent_mailing_state, #id_parent_mailing_postal_code, #id_parent_mailing_country').blur(function(){ 1005 set_secondary_parent_billing(true); 1006 }); 1007 1008 // Set email field with contact info the same as user email address, make readonly 1009 function set_student_email() { 1010 if ( $('#id_email').val() != '' ) { 1011 $('#id_student_email').val($('#id_email').val()).prop('readonly', true); 1012 } 1013 } 1014 $('#id_email').blur(function(){ 1015 if ( $('#content-container').hasClass('enroll-page') ) { 1016 set_student_email() 1017 } 1018 }); 1019 1020 /** 1021 * Set our Enroll form Fields before submitting 1022 */ 1023 function set_enroll_fields() { 1024 if ( $('#content-container').hasClass('enroll-page') ) { 1025 set_parent_billing(true); 1026 set_student_email(); 1027 } 1028 } 1029 // Call immediately ... just in case 1030 set_enroll_fields(); 1031 1032 /** 1033 * Display our loading spinner while the form is submitting. 1034 */ 1035 $('form').submit(function(){ 1036 set_enroll_fields(); 1037 1038 // Skip if there is a loading message already initiated 1039 if ( $('#loading-message').is(":visible") ) { 1040 return; 1041 } else { 1042 loadingStart('Processing your form, please wait...'); 1043 return true; 1044 } 1045 }); 1046 1047 /** 1048 * Save Progress - saves a student's work without form validation 1049 */ 1050 $('p.save-progress button').click(function(e){ 1051 1052 // It's possible to paste in an essay greater than the 3000 char max, which without validation will save as an empty string 1053 var exceeds_max = false;
1054 $('form.wizard-form textarea').each(function(){ 1055 if ($(this).val().length > 3000) { 1056 exceeds_max = true; 1057 alert('A text box on this page exceeds the maximum 3000 character limit. Please check and save again.'); 1058 return false; 1059 } 1060 }); 1061 1062 if ( !exceeds_max ){ 1063 loadingStart('Saving'); 1064 $('#id_save_skip_validation').val('true'); 1065 $('form.wizard-form').attr('novalidate','novalidate'); // Prevent HTML5 validation 1066 } 1067 1068 $('form.wizard-form button[type="submit"]').click(); // Have to use click. .submit() doesn't work if a submit button is present 1069 }); 1070 1071 /** 1072 * Portal Nav - Check if we are mobile size and turn the sidebar into mobile menu 1073 */ 1074 function toggleDashMobileNav() { 1075 if( $(window).width() <= 767 ) { 1076 $('#toggle-dash-nav').attr('aria-expanded', 'false'); 1077 $('#account-menu').addClass('nav collapse'); 1078 } else { 1079 $('#toggle-dash-nav').attr('aria-expanded', 'true'); 1080 $('#account-menu').removeClass('nav collapse'); 1081 } 1082 } 1083 // Call our resizing function on window resize 1084 $(window).resize(function() { 1085 toggleDashMobileNav() 1086 }); 1087 1088 // Call this function immediately, in-case we are already mobile size. 1089 toggleDashMobileNav(); 1090 1091 1092 //---------------------------------------------------------------------------// 1093 // These functions below handle submitting our scholarship code forms, // 1094 // this is to be done with JS because we cannot nest an HTML form in a form. // 1095 //---------------------------------------------------------------------------// 1096 1097 /** 1098 * Submit the top scholarship code form with enter key press. 1099 */ 1100 $("#scholarship-code-top").on("keypress", function(e) { 1101 // Was the "enter" key pressed (keycode 13)? 1102 if(e.which == 13) { 1103 // Prevent default here, so the outer form will not high-jack this action. 1104 e.preventDefault(); 1105 // Delegate to generic function 1106 submitScholarshipForm("top"); 1107 } 1108 }); 1109 1110 /** 1111 * Submit the bottom scholarship code form with button click 1112 */ 1113 $("#scholarship-form-top-btn-submit").on("click", function(e){ 1114 e.preventDefault(); 1115 // Delegate to generic function 1116 submitScholarshipForm("top"); 1117 }); 1118 1119 /** 1120 * Submit the bottom scholarship code form with enter key press. 1121 */ 1122 $("#scholarship-code-bottom").on("keypress", function(e) { 1123 // Was the "enter" key pressed (keycode 13)? 1124 if(e.which == 13) { 1125 // Prevent default here, so the outer form will not high-jack this action. 1126 e.preventDefault(); 1127 // Delegate to generic function 1128 submitScholarshipForm("bottom"); 1129 } 1130 }); 1131 1132 /** 1133 * Submit the bottom scholarship code form with button click 1134 */ 1135 $("#scholarship-form-bottom-btn-submit").on("click", function(e){ 1136 e.preventDefault(); 1137 // Delegate to generic function 1138 submitScholarshipForm("bottom"); 1139 }); 1140 1141 /** 1142 * Submits a scholarship form on the enrollment page. 1143 * 1144 * @param whichForm - Which scholarship form on the page we are submitting, 1145 * can be one of: ["top", "bottom"] 1146 */ 1147 function submitScholarshipForm(whichForm) { 1148 loadingStart('Processing your form, please wait...'); 1149 1150 var formEle = ""; 1151 var scode = ""; 1152 1153 var formTopEle = $("#scholarship-form-top"); 1154 var formBottomEle = $("#scholarship-form-bottom"); 1155 1156 var codeTopEle = $("#scholarship-code-top"); 1157 var codeBottomEle = $("#scholarship-code-bottom"); 1158 1159 1160 // Check which form was submitted. 1161 if (whichForm === "bottom") { 1162 formEle = formBottomEle; 1163 scode = codeBottomEle.val(); 1164 1165 // Clear the input field 1166 codeBottomEle.val(""); 1167 } else { 1168 formEle = formTopEle; 1169 scode = codeTopEle.val(); 1170 // Clear the input field 1171 codeTopEle.val(""); 1172 } 1173 1174 // Clear both forms success and errors 1175 clearScholarshipFormErrorAndSuccess(formTopEle); 1176 clearScholarshipFormErrorAndSuccess(formBottomEle); 1177
1178 // Submit our scholarship code (the server side code will save the code in the session if this is successful) 1179 $.ajax({ 1180 url: (typeof MY_SCHOLARSHIP_TOKEN_URL !== "undefined" ? MY_SCHOLARSHIP_TOKEN_URL : ""), 1181 method: "POST", 1182 dataType: "json", 1183 data: { 1184 scode: scode, 1185 csrfmiddlewaretoken: (typeof MY_CSRF_TOKEN !== "undefined" ? MY_CSRF_TOKEN : "") 1186 }, 1187 success: function(serverData) { 1188 console.log(serverData); 1189 if (serverData.error) { 1190 displayScholarshipFormError(formEle, serverData.error); 1191 } else if (serverData.success) { 1192 // First remove a previous scholarship (if we had one) 1193 removeScholarshipFromForm(); 1194 // Now apply the scholarship to the application. 1195 // Applying the scholarship to the form really only updates the display of prices 1196 // The actual discount is applied when the form is submitted and is done server side. 1197 applyScholarshipToForm(serverData.data); 1198 displayScholarshipFormSuccess(formEle, serverData.success); 1199 } 1200 }, 1201 error: function(jqXhr, textStatus, errorThrown) { 1202 console.error("Error submitting scholarship code: " + errorThrown + " (" + textStatus + ")"); 1203 displayScholarshipFormError(formEle, "Unexpected error, please try again. If this error persists please contact us."); 1204 }, 1205 complete: function() { 1206 loadingStop(); 1207 } 1208 }) 1209 } 1210 1211 function clearScholarshipFormErrorAndSuccess(formEle) { 1212 var formGroupEle = formEle.children(".form-group").first(); 1213 // Remove error styling 1214 formGroupEle.removeClass("has-error"); 1215 1216 // Remove error and success message 1217 formGroupEle.children(".form-messages-block").remove(); 1218 } 1219 1220 function displayScholarshipFormError(formEle, errorMessage) { 1221 var formGroupEle = formEle.children(".form-group").first(); 1222 1223 // Add error styling 1224 formGroupEle.addClass("has-error"); 1225 1226 // Display error message 1227 var errorMessageEle = $("<div class='form-messages-block alert alert-danger'></div>"); 1228 errorMessageEle.text(errorMessage); 1229 formGroupEle.append(errorMessageEle); 1230 } 1231 1232 function displayScholarshipFormSuccess(formEle, successMessage) { 1233 var formGroupEle = formEle.children(".form-group").first(); 1234 1235 // Display error message 1236 var successMessageEle = $("<div class='form-messages-block alert alert-success'></div>"); 1237 successMessageEle.text(successMessage); 1238 formGroupEle.append(successMessageEle); 1239 } 1240 1241 function hideScholarshipAppliedFields() { 1242 $(".scholarship-applied-to-program").hide(); 1243 $(".scholarship-applied").hide(); 1244 } 1245 1246 function showScholarshipAppliedFields() { 1247 $(".scholarship-applied-to-program").show(); 1248 $(".scholarship-applied").show(); 1249 } 1250 1251 function applyScholarshipToForm(sprod) { 1252 1253 var hasUnitPrice = sprod.unit_price && sprod.unit_price != "$0.00"; 1254 1255 if (sprod && hasUnitPrice) { 1256 var scholarshipUnitPriceUnformatted = sprod.unit_price_unformatted; 1257 var scholarshipUnitPriceFormatted = sprod.unit_price; 1258 1259 console.log('discount precent? ', sprod.discount_percent); 1260 if (sprod.discount_percent) { 1261 applicationDiscountPercent = parseFloat(sprod.discount_percent); 1262 console.log('discount precent? ', applicationDiscountPercent); 1263 // Set Unit Price based on discount percent and total tuition 1264 var totalTuition = 0; 1265 $.each(selected_products, function() { 1266 var prod_id = this; 1267 var $trip = $('#product-' + prod_id); 1268 totalTuition += parseFloat($trip.data('tuition') || 0); 1269 }); 1270 scholarshipUnitPriceUnformatted = (-1 * totalTuition * (applicationDiscountPercent / 100)).toFixed(2); 1271 var scholarshipDisplayAmount = parseFloat(scholarshipUnitPriceUnformatted || 0); 1272 if (scholarshipDisplayAmount > 0) { 1273 scholarshipDisplayAmount = -scholarshipDisplayAmount; 1274 } 1275 scholarshipUnitPriceFormatted = formatScholarshipMoney(scholarshipDisplayAmount); 1276 } 1277 1278 var discountAppliesToDueNow = sprod.is_due_now_scholarship; 1279 1280 // Set the visible discount on the product rows. 1281 $(".scholarship-applied-to-program-value").text(scholarshipUnitPriceFormatted); 1282 1283 var scholarshipLineItemParentEle = $(".scholarship-applied-lineitems").first(); 1284 1285 // Set the visible "Scholarship" line item with the discount on step 4 1286 var lineItem = $('<tr class="scholarship-line-item scholarship"></tr>');
1287 lineItem.append($('<td class="scholarship-summary"></td>').text(sprod.name)); 1288 lineItem.append($('<td class="scholarship-total"></td>').text(scholarshipUnitPriceFormatted)); 1289 lineItem.append($('<td class="scholarship-duenow"></td>').text((discountAppliesToDueNow ? sprod.due_upon_application : "$0.00"))); 1290 lineItem.append($('<td class="action"></td>')); 1291 scholarshipLineItemParentEle.append(lineItem); 1292 1293 // Set our globals for the discount as updateTotals() will use them. 1294 applicationScodeUnformattedPrice = scholarshipUnitPriceUnformatted; 1295 applicationScodeFormattedPrice = scholarshipUnitPriceFormatted; 1296 applicationDiscountAppliesToDueNow = discountAppliesToDueNow; 1297 applicationScodeUnformattedDueNowPrice = sprod.due_upon_application_unformatted; 1298 applicationScodeFormattedDueNowPrice = sprod.due_upon_application; 1299 1300 1301 console.log('scholarship unitprice unformat? ', applicationScodeUnformattedPrice); 1302 console.log('scholarship unitprice format? ', applicationScodeFormattedPrice); 1303 1304 // Applies the scholarship to the totals (if at least one product has been selected) 1305 if (selected_products.length) { 1306 updateTotals(); 1307 } 1308 showScholarshipAppliedFields(); 1309 } 1310 } 1311 1312 function removeScholarshipFromForm() { 1313 // Hide the UI elements we previously showed. 1314 hideScholarshipAppliedFields(); 1315 1316 // Remove/Reset the UI elements that were added earlier 1317 $(".scholarship-applied-to-program-value").text("$0.00"); 1318 $(".scholarship-applied-lineitems").children(".scholarship-line-item").remove(); 1319 1320 // Reset the globals so their values are cleared 1321 applicationScodeUnformattedPrice = ""; 1322 applicationScodeFormattedPrice = ""; 1323 applicationDiscountAppliesToDueNow = false; 1324 applicationScodeUnformattedDueNowPrice = ""; 1325 applicationScodeFormattedDueNowPrice = ""; 1326 applicationDiscountPercent = 0; 1327 1328 // Finally update the prices to be correct 1329 if (selected_products.length) { 1330 updateTotals(); 1331 } 1332 } 1333 1334 function addTableToDestination(destinationSlug, yearData, targetYear) { 1335 // Predict both possible IDs the Django template might have generated 1336 var perfectSuffix = destinationSlug + '-' + targetYear; 1337 var fallbackSuffix = destinationSlug + '-'; 1338 1339 // Try to find the exact year match first (e.g. for future years) 1340 var suffix = perfectSuffix; 1341 var $menu = $('#menu-' + suffix); 1342 1343 // If it doesn't exist, it must be a primary year card with a blank year! 1344 if ($menu.length === 0) { 1345 suffix = fallbackSuffix; 1346 $menu = $('#menu-' + suffix); 1347 } 1348 1349 var table = $menu.find('#destination-table-' + suffix); 1350 1351 if (!table[0] && $menu.length > 0) { 1352 // Call the newly exposed buildTable function! 1353 DestMenu.buildTable($menu, destinationSlug, yearData, suffix); 1354 } 1355 } 1356 1357 /** Show/Hide credit card fees in balance table **/ 1358 $('.cc-more-btn').on('click', function() { 1359 $('.table-cc-fee').show(); 1360 $('.cc-more-btn').hide(); 1361 $('.cc-less-btn').show(); 1362 }); 1363 $('.cc-less-btn').on('click', function() { 1364 $('.table-cc-fee').hide(); 1365 $('.cc-more-btn').show(); 1366 $('.cc-less-btn').hide(); 1367 }); 1368 1369 // Insurance delete form 1370 $('#insurance-delete-form').on('submit', function(event) { 1371 if (!confirm("Are you sure you want to remove this item?")) { 1372 event.preventDefault(); 1373 loadingStop(); 1374 } 1375 }); 1376 }); 1377})(jQuery);
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.