1/* eslint-disable no-var */ 2jQuery(function ($) { 3 var hash = window.location.hash; 4 const expandableSidebarHeightPadding = 50; 5 6 learndashFocusModeSidebarAutoScroll(); 7 8 initLoginModal(); 9 if ('#login' == hash) { 10 openLoginModal(); 11 } 12 13 if ('undefined' !== typeof ldGetUrlVars().login) { 14 var loginStatus = ldGetUrlVars().login; 15 16 if ('failed' == loginStatus) { 17 openLoginModal(); 18 } 19 } 20 21 if ('undefined' !== typeof ldGetUrlVars()['ld-topic-page']) { 22 var topicPage = ldGetUrlVars()['ld-topic-page']; 23 var topicIds = topicPage.split('-'); 24 var topicId = Object.values(topicIds)[0]; 25 26 var lesson = $('#ld-expand-' + topicId); 27 var button = $(lesson).find('.ld-expand-button'); 28 29 ldToggleExpandableElement(button, true); 30 31 $('html, body').animate( 32 { 33 scrollTop: $(lesson).offset().top, 34 }, 35 500 36 ); 37 } 38 39 // a[href="#login"] is for backwards compatibility with potentially outdated templates. 40 $('body').on( 41 'click', 42 'a[href="#login"], button[data-ld-login-modal-trigger]', 43 function (e) { 44 e.preventDefault(); 45 openLoginModal(); 46 } 47 ); 48 49 // By default, the element is not interactable with keyboard. We make the keypress event for space and enter key to close the login modal for accessibility. 50 $('body').on('keypress', '.ld-modal-closer', function (e) { 51 if (13 === e.keyCode || 32 === e.keyCode) { 52 e.preventDefault(); 53 closeLoginModal(); 54 } 55 }); 56 57 $('body').on('click', '.ld-modal-closer', function (e) { 58 e.preventDefault(); 59 closeLoginModal(); 60 }); 61 62 $('body').on('click', '#ld-comments-post-button', function (e) { 63 $(this).addClass('ld-open'); 64 $('#ld-comments-form').removeClass('ld-collapsed'); 65 $('textarea#comment').focus(); 66 }); 67 68 // Close modal if clicking away 69 /* 70 $('body').on('click', function(e) { 71 if ($('.learndash-wrapper').hasClass('ld-modal-open')) { 72 if ( ! $(e.target).parents('.ld-modal').length && (! $(e.target).is('a'))) { 73 closeLoginModal(); 74 } 75 } 76 }); 77 */ 78 79 // Close modal on Esc key 80 $(document).on('keyup', function (e) { 81 if (27 === e.keyCode) { 82 closeLoginModal(); 83 } 84 }); 85 86 $('.learndash-wrapper').on( 87 'click', 88 'a.user_statistic', 89 learndash_ld30_show_user_statistic 90 ); 91 92 focusMobileCheck(); 93 focusMobileResizeCheck(); 94 95 disableFocusTrap(); 96 97 $('body').on('click', '.ld-focus-sidebar-trigger', function (e) { 98 if ($('.ld-focus').hasClass('ld-focus-sidebar-collapsed')) { 99 openFocusSidebar(); 100 } else { 101 closeFocusSidebar(); 102 } 103 }); 104 105 $('body').on('click', '.ld-trigger-mobile-nav', function (e) { 106 e.preventDefault(); 107 if ($('.ld-focus').hasClass('ld-focus-sidebar-collapsed')) { 108 openFocusSidebar(); 109 } else { 110 closeFocusSidebar(); 111 } 112 }); 113 114 $('.ld-js-register-account').on('click', function (e) { 115 e.preventDefault(); 116 117 $('.ld-login-modal-register .ld-modal-text').slideUp('slow'); 118 $('.ld-login-modal-register .ld-alert').slideUp('slow'); 119 $(this).slideUp('slow', function () { 120 $('#ld-user-register').slideDown('slow'); 121 }); 122 }); 123 124 // If registration login link filter not defined, allow to replace the register form with login form 125 ldRegistrationLinkInit(); 126 ldRegistrationLinkInitModern(); 127 128 var windowWidth = $(window).width(); 129 130 // Ensure that tooltips are positioned properly after screen size changes. 131 $(window).on('resize orientationchange', function () { 132 const resizeTimer = setTimeout(() => { 133 const newWidth = $(window).width(); 134 135 if (newWidth === windowWidth) { 136 return; 137 } 138 139 windowWidth = newWidth; 140 141 // Wait one more animation frame after debounce to let layout settle. 142 window.requestAnimationFrame(() => { 143 initTooltips(); 144 focusMobileResizeCheck(); 145 clearTimeout(resizeTimer); 146 }); 147 }, 150); 148 }); 149 150 if ($('.ld-course-status-content').length) { 151 var tallest = 0; 152 153 $('.ld-course-status-content').each(function () { 154 if ($(this).height() > tallest) { 155 tallest = $(this).height(); 156 } 157 }); 158 159 $('.ld-course-status-content').height(tallest); 160 } 161 162 function focusMobileCheck() { 163 if (1024 > $(window).width()) { 164 closeFocusSidebarPageLoad(); 165 } 166 } 167 168 /** 169 * Toggles the focus sidebar based on the window width. 170 * 171 * @since 3.2.0 172 * @since 4.21.5 Now also toggles aria-modal attribute. 173 * 174 * @return {void} 175 */ 176 function focusMobileResizeCheck() { 177 if ($(window).width() < 1024) { 178 $('#ld-focus-sidebar').attr('aria-modal', 'true'); 179 180 if (!$('.ld-focus').hasClass('ld-focus-sidebar-collapsed')) { 181 closeFocusSidebar(); 182 } 183 } else { 184 $('#ld-focus-sidebar').attr('aria-modal', 'false'); 185 186 if ($('.ld-focus').hasClass('ld-focus-sidebar-filtered')) { 187 closeFocusSidebar(); 188 } else if ( 189 !$('.ld-focus').hasClass('ld-focus-sidebar-filtered') && 190 $('.ld-focus').hasClass('ld-focus-sidebar-collapsed') 191 ) { 192 openFocusSidebar(); 193 } 194 } 195 } 196 197 function focusMobileHandleOrientationChange(e) { 198 if (e.matches) { 199 if ( 200 1024 <= $(window).width() && 201 !$('.ld-focus').hasClass('ld-focus-sidebar-filtered') && 202 $('.ld-focus').hasClass('ld-focus-sidebar-collapsed') 203 ) { 204 openFocusSidebar(); 205 } 206 } 207 } 208 window 209 .matchMedia('(orientation: landscape)') 210 .addListener(focusMobileHandleOrientationChange); 211 212 function closeFocusSidebarPageLoad() { 213 $('.ld-focus').addClass('ld-focus-sidebar-collapsed'); 214 $('.ld-focus').removeClass('ld-focus-initial-transition'); 215 $('.ld-mobile-nav').removeClass('expanded'); 216 $('[aria-controls="ld-focus-sidebar"]').attr('aria-expanded', 'false'); 217 positionTooltips(); 218 219 const sidebar = document.querySelector('.ld-focus-sidebar'); 220 221 if (sidebar) { 222 sidebar.dispatchEvent(new CustomEvent('ld-focus-sidebar-closed')); 223 } 224 dispatchSidebarEvent(false); 225 } 226 227 /** 228 * Dispatches a custom event on to notify other scripts that the focus sidebar has been opened or closed. 229 * 230 * @since 4.23.2 231 * 232 * @param {boolean} isOpen Whether the sidebar is open. 233 * 234 * @return {void} 235 */ 236 function dispatchSidebarEvent(isOpen) { 237 const sidebar = document.querySelector('.ld-focus-sidebar'); 238 239 if (!sidebar) { 240 return; 241 } 242 243 let eventName = 'ld-focus-sidebar-closed'; 244 245 if (isOpen) { 246 eventName = 'ld-focus-sidebar-opened'; 247 } 248 249 sidebar.dispatchEvent(new CustomEvent(eventName)); 250 } 251 252 /** 253 * Closes the focus sidebar. 254 * 255 * @since 3.0.0 256 * 257 * @return {void} 258 */ 259 function closeFocusSidebar() { 260 // Hide the wrapper to avoid issues with focus trap. 261 $('.ld-focus-sidebar-wrapper').hide(); 262 263 $('.ld-focus').addClass('ld-focus-sidebar-collapsed'); 264 $('.ld-mobile-nav').removeClass('expanded'); 265 $('[aria-controls="ld-focus-sidebar"]').attr('aria-expanded', 'false'); 266 267 disableFocusTrap(); 268 269 // If the mobile trigger is visible, move focus to it.
270 const mobileTrigger = $('.ld-trigger-mobile-nav'); 271 272 if (mobileTrigger.is(':visible')) { 273 mobileTrigger.focus(); 274 } 275 276 positionTooltips(); 277 278 const sidebar = document.querySelector('.ld-focus-sidebar'); 279 280 if (sidebar) { 281 sidebar.dispatchEvent(new CustomEvent('ld-focus-sidebar-closed')); 282 } 283 dispatchSidebarEvent(false); 284 } 285 /** 286 * Handles tab key press in the focus sidebar to trap focus within the sidebar. 287 * When the last focusable element is reached, focus is redirected to the first element. 288 * 289 * @since 4.21.3 290 * @param {Event} e The keyboard event object. 291 */ 292 function handleTabTrap(e) { 293 if (e.key === 'Tab') { 294 e.preventDefault(); 295 $('#ld-focus-sidebar-toggle').focus(); 296 } 297 } 298 299 /** 300 * Enables focus trap for the sidebar to improve accessibility. 301 * Makes sidebar elements tabbable, focuses the sidebar toggle button, 302 * and adds event listener to trap focus within the sidebar. 303 * 304 * @since 4.21.3 305 */ 306 function enableFocusTrap() { 307 // Focus sidebar for accessibility when opened, allowing keyboard navigation easier. 308 $('#ld-focus-sidebar-toggle').focus(); 309 310 // Make the course heading tabbable. 311 $('#ld-focus-mode-course-heading').attr('tabindex', '0'); 312 313 // Get list of focusable elements, and when last one is focused, focus on the first element to trap focus. 314 const focusableElements = $('.ld-lesson-items a'); 315 const lastFocusableElement = 316 focusableElements[focusableElements.length - 1]; 317 318 lastFocusableElement.addEventListener('keydown', handleTabTrap); 319 } 320 321 /** 322 * Disables focus trap for the sidebar when it's closed. 323 * Makes sidebar elements non-tabbable and removes the event listener. 324 * 325 * @since 4.21.3 326 */ 327 function disableFocusTrap() { 328 // Make the course heading non-tabbable when the sidebar is closed. 329 $('#ld-focus-mode-course-heading').attr('tabindex', '-1'); 330 331 if ($('.ld-focus-sidebar-trigger').attr('aria-expanded') === 'true') { 332 return; 333 } 334 335 // Remove focus trap when sidebar is closed. 336 const focusableElements = $('.ld-lesson-items a'); 337 const lastFocusableElement = 338 focusableElements[focusableElements.length - 1]; 339 340 if (lastFocusableElement) { 341 lastFocusableElement.removeEventListener('keydown', handleTabTrap); 342 } 343 } 344 345 /** 346 * Opens the focus sidebar and enables focus trap for accessibility. 347 * Handles mobile checks, class toggling, and icon changes. 348 * 349 * @since 3.0.0 350 */ 351 function openFocusSidebar() { 352 focusMobileCheck(); 353 354 // Show the wrapper 355 $('.ld-focus-sidebar-wrapper').show(); 356 357 // Recalculate max-heights for expanded lesson items whose height was measured while 358 // the sidebar was hidden (display: none). When hidden, outerHeight() returns 0 and 359 // data-height is set to 0. A value > 0 means it was already measured correctly. 360 $( '.ld-focus-sidebar .ld-lesson-item-expanded.ld-expanded' ).each( function () { 361 const $lessonContainer = $( this ); 362 if ( parseInt( $lessonContainer.attr( 'data-height' ), 10 ) > 0 ) { 363 return; 364 } 365 let recalcHeight = 0; 366 $lessonContainer.children().each( function () { 367 recalcHeight += $( this ).outerHeight(); 368 } ); 369 const newHeight = recalcHeight > 0 ? recalcHeight + expandableSidebarHeightPadding : 0; 370 $lessonContainer.attr( 'data-height', newHeight ).css( 'max-height', newHeight ); 371 } ); 372 373 // Flip classes to open the focus sidebar. 374 $('.ld-focus').removeClass('ld-focus-sidebar-collapsed'); 375 $('.ld-mobile-nav').addClass('expanded'); 376 377 // We need to wait for the sidebar to be opened before we can enable the focus trap. 378 enableFocusTrap(); 379 380 $('[aria-controls="ld-focus-sidebar"]').attr('aria-expanded', 'true'); 381 382 const sidebar = document.querySelector('.ld-focus-sidebar'); 383 384 if (sidebar) { 385 sidebar.dispatchEvent(new CustomEvent('ld-focus-sidebar-opened')); 386 } 387 dispatchSidebarEvent(true); 388 389 positionTooltips(); 390 } 391 392 $('.ld-file-input').each(function () { 393 var $input = $(this), 394 $label = $input.next('label'), 395 labelVal = $label.html(); 396 397 $input.on('change', function (e) { 398 var fileName = ''; 399 if (this.files && 1 < this.files.length) { 400 fileName = ( 401 this.getAttribute('data-multiple-caption') || '' 402 ).replace('{count}', this.files.length); 403 } else if (e.target.value) { 404 fileName = e.target.value.split('\\').pop(); 405 } 406 if (fileName) { 407 $label.find('span').html(fileName); 408 $label.addClass('ld-file-selected'); 409 $('#uploadfile_btn').attr('disabled', false); 410 } else { 411 $label.html(labelVal); 412 $label.removeClass('ld-file-selected'); 413 $('#uploadfile_btn').attr('disabled', true); 414 } 415 }); 416 417 $('#uploadfile_form').on('submit', function () { 418 $label.removeClass('ld-file-selected'); 419 $('#uploadfile_btn').attr('disabled', true); 420 }); 421 422 // Firefox bug fix 423 $input 424 .on('focus', function () { 425 $input.addClass('has-focus'); 426 }) 427 .on('blur', function () { 428 $input.removeClass('has-focus'); 429 }); 430 }); 431 432 $('body').on( 433 'click', 434 '.ld-expand-button, [data-ld-expand-button]', 435 function (e) { 436 e.preventDefault(); 437 438 ldToggleExpandableElement($(this)); 439 440 positionTooltips(); 441 } 442 ); 443 444 /** 445 * Initialize expanded items to be expanded and collapsed elements to have [hidden="hidden"]. 446 * Expanded elements allow "Expand All" items to expand all their children by default. 447 * Collapsed elements need [hidden="hidden"] to be set via JavaScript, so we cannot pre-load this on the server. 448 *
449 * @see https://designsystem.digital.gov/components/accordion/ 450 * 451 * @since 4.21.0 452 */ 453 function initializeExpandableElements() { 454 $('.ld-expand-button, [data-ld-expand-button], .ld-search-prompt').each( 455 function (index, buttonElement) { 456 ldToggleExpandableElement( 457 $(buttonElement), 458 $(buttonElement).attr('aria-expanded') === 'true' 459 ); 460 } 461 ); 462 } 463 464 /** 465 * Focuses on the first alert on the page with a role "alert". 466 * This is required for accessibility. 467 * 468 * @since 4.21.3 469 * @since 4.21.5 Switched to a focus-based approach for better compatibility with different screen readers. 470 * 471 * @return {void} 472 */ 473 function initializeAlerts() { 474 /** 475 * We need to give it a tabindex of -1 to allow it to be programmatically focused so it will be 476 * read out by screen readers. Unfortunately, we cannot remove it after focusing. 477 * If we do, it won't be read at all. 478 * 479 * By using -1 rather than 0, it won't show up in the tab order when navigating by keyboard. 480 * 481 * We can only target the first alert because we have no way to know when the screen reader has finished 482 * reading each alert to focus on the next one. 483 * 484 * The setTimeout() is used to force this to happen at the end of the event queue and improve compatibility. 485 */ 486 setTimeout(function () { 487 $('.ld-alert[role="alert"]:visible') 488 .first() 489 .attr('tabindex', '-1') 490 .focus(); 491 }, 500); 492 } 493 494 $(document).on( 495 'ldAccordionPaginationComplete', 496 '.ld-accordion', 497 initializeExpandableElements 498 ); 499 500 // On page load. 501 initializeExpandableElements(); 502 initializeAlerts(); 503 504 $('body').on('click', '.ld-search-prompt', function (e) { 505 e.preventDefault(); 506 507 $('#course_name_field').focus(); 508 509 ldToggleExpandableElement($(this)); 510 511 const $controls = $('#' + $(this).attr('aria-controls')); 512 513 if ($controls.find('.ld-closer').length > 0) { 514 $controls 515 .find('.ld-closer') 516 .attr('aria-expanded', $(this).attr('aria-expanded')); 517 } 518 }); 519 520 /** 521 * Handles expanding and collapsing elements on button click. 522 * 523 * @since 4.20.2 524 * 525 * @param {jQuery} $button jQuery Element for the button that was clicked. 526 * @param {boolean} expand Whether to expand the associated element or not. Defaults to a value based on the button's aria-expanded attribute. 527 * 528 * @return {void} 529 */ 530 function ldToggleExpandableElement($button, expand) { 531 if ('undefined' === typeof expand) { 532 // Checking !== true to handle undefined and false. 533 expand = $button.attr('aria-expanded') !== 'true'; 534 } 535 536 const containerID = $button.attr('aria-controls'); 537 538 if ( 539 typeof containerID !== 'undefined' && 540 containerID.indexOf(' ') > -1 && 541 !$button.data('ld-expanding-all') 542 ) { 543 // We're toggling multiple elements at once via an "Expand All"-type button. 544 545 containerID.split(' ').forEach(function (id) { 546 const $element = $('[aria-controls="' + id + '"]'); 547 548 ldToggleExpandableElement($element, expand); 549 }); 550 551 // Temporarily set the "ld-expanding-all" data to allow us to toggle the state of this specific button. 552 553 $button.data('ld-expanding-all', true); 554 555 ldToggleExpandableElement($button, expand); 556 557 $button.data('ld-expanding-all', false); 558 } else { 559 // Toggle a specific button. 560 561 // Account for edge cases where an Expand All button controls only one expandable area. 562 $button = $('[aria-controls="' + containerID + '"]'); 563 564 const $container = $('#' + containerID); 565 566 if (expand && $container.length > 0) { 567 // Unhide right away. 568 $container.attr('hidden', false); 569 } 570 571 $button.each(function (index, element) { 572 /** 573 * Pull the initial text from a cached data attribute, 574 * that way as the button text changes we always know what it was initially. 575 */ 576 const dataInitialText = 577 $(element).data('ld-initial-text') || 578 $(element) 579 .find('.ld-text, [data-ld-expand-button-text-element]') 580 .html(); 581 582 $(element).data('ld-initial-text', dataInitialText); 583 584 const dataExpandText = 585 $(element).data('ld-expand-text') || dataInitialText; 586 const dataCollapseText = 587 $(element).data('ld-collapse-text') || dataInitialText; 588 589 $(element) 590 .attr('aria-expanded', expand) 591 .toggleClass('ld-expanded', expand); 592 593 if (expand && dataCollapseText) { 594 $(element) 595 .find('.ld-text, [data-ld-expand-button-text-element]') 596 .html(dataCollapseText); 597 } else if (!expand && dataExpandText) { 598 $(element) 599 .find('.ld-text, [data-ld-expand-button-text-element]') 600 .html(dataExpandText); 601 } 602 }); 603 604 if ($container.length <= 0) { 605 /** 606 * Expand All buttons with more than one controlled element won't have a valid Container, 607 * so we shouldn't proceed. 608 */ 609 return; 610 } 611 612 let totalHeight = 0; 613 $container.children().each(function () { 614 totalHeight += $(this).outerHeight(); 615 }); 616 617 const finalHeightWithPadding = totalHeight > 0 ? totalHeight + expandableSidebarHeightPadding : 0; 618 619 // Writing to the attribute to make debugging easier. 620 $container.attr('data-height', finalHeightWithPadding); 621 622 $container.css({ 623 'max-height': expand ? finalHeightWithPadding : 0, 624 }); 625 626 if (!expand) { 627 // If we're collapsing, we should remove this class immediately. 628 $container.toggleClass('ld-expanded', expand); 629 630 const waitForCollapsed = setInterval(function () { 631 if ($container.outerHeight() === 0) { 632 clearInterval(waitForCollapsed); 633 $container.attr('hidden', true); 634 } 635 }); 636 } else { 637 // If we're expanding, we only want to add the .ld-expanded class once fully expanded. 638 const waitForExpanded = setInterval(function () { 639 if ($container.outerHeight() === totalHeight) { 640 clearInterval(waitForExpanded); 641 $container.toggleClass('ld-expanded', expand); 642 } 643 }); 644 } 645 } 646 647 positionTooltips(); 648 } 649 650 /** 651 * Initialize registration link in the classic registration page. 652 * 653 * @since 4.16.0 654 * 655 * @return {void} 656 */ 657 function ldRegistrationLinkInit() { 658 const $loginLink = $('.registration-login-link'); 659 660 if ($loginLink.length === 0) { 661 return; 662 } 663 664 if ('' !== $loginLink.attr('href')) { 665 return; 666 } 667 668 $loginLink.on('click', function (e) { 669 e.preventDefault(); 670 $('#learndash_registerform, .registration-login').hide(); 671 $( 672 '.registration-login-form, .show-register-form, .show-password-reset-link' 673 ).show(); 674 }); 675 676 $('.show-register-form').on('click', function (e) { 677 e.preventDefault(); 678 $( 679 '.registration-login-form, .show-register-form, .show-password-reset-link' 680 ).hide(); 681 $('#learndash_registerform, .registration-login').show(); 682 }); 683 } 684 685 /** 686 * Initialize registration link in the modern registration page. 687 * 688 * @since 4.16.0 689 * 690 * @return {void} 691 */ 692 function ldRegistrationLinkInitModern() { 693 const $loginLink = $('.ld-registration__login-link'); 694 695 if ($loginLink.length === 0) { 696 return; 697 } 698 699 if ('' !== $loginLink.attr('href')) { 700 return; 701 } 702 703 $(document).on('click', '.ld-registration__login-link', function (e) { 704 e.preventDefault(); 705 let $wrapper = $(this).closest('.ld-registration__wrapper'); 706 $wrapper.addClass('ld-registration__wrapper--login'); 707 $wrapper.removeClass('ld-registration__wrapper--register'); 708 }); 709 710 $(document).on( 711 'click', 712 '.ld-registration__register-link', 713 function (e) { 714 e.preventDefault(); 715 let $wrapper = $(this).closest('.ld-registration__wrapper'); 716 $wrapper.removeClass('ld-registration__wrapper--login'); 717 $wrapper.addClass('ld-registration__wrapper--register'); 718 } 719 ); 720 } 721 722 $('body').on('click', '.ld-closer', function (e) { 723 ldToggleExpandableElement($('.ld-search-prompt'), false); 724 $(this).attr('aria-expanded', false); 725 }); 726 727 $('body').on('touch click', '.ld-tabs-navigation .ld-tab', function () { 728 const $tabContent = $('#' + $(this).attr('aria-controls')); 729 if (!$tabContent.length) { 730 return; 731 } 732 733 // Set other Tabs as inactive. 734 $('.ld-tabs-navigation .ld-tab.ld-active') 735 .removeClass('ld-active') 736 .attr('aria-selected', 'false') 737 .attr('tabindex', '-1'); 738 739 // Set current Tab as active. 740 $(this) 741 .addClass('ld-active') 742 .attr('aria-selected', 'true') 743 .removeAttr('tabindex'); 744 745 // Make other Tab Content panels invisible. 746 $('.ld-tabs-content .ld-tab-content.ld-visible').removeClass( 747 'ld-visible' 748 ); 749 750 $tabContent.addClass('ld-visible'); 751 752 positionTooltips(); 753 }); 754 755 $('body').on('keydown', '.ld-tabs-navigation .ld-tab', function (event) { 756 // If the key is not a Left/Right arrow key, Home, or End, do nothing. 757 if ( 758 ['ArrowLeft', 'ArrowRight', 'Home', 'End'].indexOf(event.key) === -1 759 ) { 760 return; 761 } 762 763 const target = event.currentTarget; 764 const $firstTab = $(target) 765 .closest('[role="tablist"]') 766 .find('[role="tab"]') 767 .first(); 768 const $lastTab = $(target) 769 .closest('[role="tablist"]') 770 .find('[role="tab"]') 771 .last(); 772 773 event.stopPropagation(); 774 event.preventDefault(); 775 776 switch (event.key) { 777 case 'ArrowLeft': 778 if (target === $firstTab[0]) { 779 $lastTab.focus(); 780 } else { 781 $(this).prev().focus(); 782 } 783 break; 784 case 'ArrowRight': 785 if (target === $lastTab[0]) { 786 $firstTab.focus(); 787 } else { 788 $(target).next().focus(); 789 } 790 break; 791 case 'Home': 792 $firstTab.focus(); 793 break; 794 case 'End': 795 $lastTab.focus(); 796 break; 797 default: 798 break; 799 } 800 }); 801 802 /** 803 * Initialize tooltips. 804 * - If JS isn't enabled, the tooltips will be visible at all times. 805 * - This adds the `ld-tooltip--initialized` class to all tooltips to signify that JS is enabled. 806 * - The `ld-tooltip--hidden` class is removed when the mouse is over a tooltip, the tooltip is focused, 807 * or the tooltip is focused-within. 808 * - The `ld-tooltip--hidden` class is added if a tooltip is visible and the Escape key is pressed. 809 * 810 * @since 4.21.3 811 * 812 * @return {void} 813 */ 814 function initTooltips() { 815 $('.ld-tooltip').each(function () { 816 const $tooltip = $(this).find('[role="tooltip"]'); 817 818 if (!$tooltip.length || typeof $tooltip[0] === 'undefined') { 819 return; 820 } 821 822 if (!$(this).hasClass('ld-tooltip--initialized')) { 823 $(this) 824 .addClass('ld-tooltip--initialized') 825 .addClass('ld-tooltip--hidden'); 826 } 827 828 /** 829 * We need to temporarily remove the `ld-tooltip--hidden` class 830 * as hidden tooltips are hidden off to the left edge of the screen. 831 * 832 * We need to know where it would normally be positioned when visible 833 * to know if we need to move it to the right. 834 */ 835 const isHidden = $(this).hasClass('ld-tooltip--hidden'); 836 837 $(this).removeClass('ld-tooltip--hidden'); 838 $(this).removeClass('ld-tooltip--position-right'); 839 840 const tooltipRect = $tooltip[0].getBoundingClientRect(); 841 842 let containerWidth = windowWidth; 843 844 /** 845 * If the tooltip is within the focus sidebar, use the sidebar width. 846 * 847 * We have to do this because when an element is vertically scrollable, a browser will simply not 848 * allow you to have items spill out the left or right sides even when the appropriate CSS for 849 * overflow-x is set to visible as it will be forced to auto implicitly. 850 * 851 * See https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-x#syntax 852 */ 853 if ($(this).closest('.ld-focus-sidebar').length) { 854 containerWidth = $(this).closest('.ld-focus-sidebar').width(); 855 } 856 857 /** 858 * If the tooltip would overflow the right edge of the window, 859 * add the `ld-tooltip--position-right` class. 860 */ 861 $(this).toggleClass( 862 'ld-tooltip--position-right', 863 tooltipRect.right > containerWidth 864 ); 865 866 // Add the .ld-tooltip--hidden class back if it was hidden. 867 $(this).toggleClass('ld-tooltip--hidden', isHidden); 868 }); 869 870 $(document).on('keydown', function (event) { 871 /** 872 * We need to check if the Escape key is pressed and if any tooltips 873 * are currently visible before dismissing them. 874 */ 875 if ( 876 event.key === 'Escape' && 877 ($('.ld-tooltip:hover').length || 878 $('.ld-tooltip:focus').length || 879 $('.ld-tooltip:focus-within').length) 880 ) { 881 $( 882 '.ld-tooltip:hover, .ld-tooltip:focus, .ld-tooltip:focus-within' 883 ).addClass('ld-tooltip--hidden'); 884 885 // Remove focus to prevent automatically scrolling the page. 886 $('.ld-tooltip:focus, .ld-tooltip :focus').blur(); 887 } 888 }); 889 890 /** 891 * For the next two event listeners: 892 * 893 * - mouseenter/mouseleave are used for hover on desktop. 894 *
895 * - focusin/focusout are used for keyboard focus on desktop. 896 * - These are used instead of focus/blur because the focused element is likely _within_ the tooltip container. 897 * - If the focused element is the tooltip container, these event names still work. 898 * 899 * - touchstart is used for touch devices. 900 */ 901 902 $(document).on( 903 'mouseenter focusin touchstart', 904 '.ld-tooltip--hidden', 905 function () { 906 // Show the tooltip. 907 $(this).removeClass('ld-tooltip--hidden'); 908 } 909 ); 910 911 $(document).on( 912 'mouseleave focusout touchstart', 913 '.ld-tooltip:not(.ld-tooltip--hidden)', 914 function () { 915 // Hide the tooltip when the mouse when the user moves away from the tooltip. 916 $(this).addClass('ld-tooltip--hidden'); 917 } 918 ); 919 } 920 921 /** 922 * In order to account for container breakpoints modifying the layout, 923 * we need to initialize tooltips twice on page load. 924 * 925 * TODO: Find a better way to do this. 926 */ 927 initTooltips(); 928 initTooltips(); 929 930 var $tooltips = $('*[data-ld-tooltip]'); 931 932 initLegacyTooltips(); 933 934 /** 935 * Initialize legacy LD30 Classic tooltips. 936 * Replaced with a new style of tooltips in 4.21.3 for accessibility reasons. 937 * Kept for backwards compatibility. 938 * 939 * @since 3.0.0 940 * @since 4.21.3 Renamed from initTooltips to initLegacyTooltips. 941 * 942 * @return {void} 943 */ 944 function initLegacyTooltips() { 945 // Clear out old tooltips 946 947 if ($('#learndash-tooltips').length) { 948 $('#learndash-tooltips').remove(); 949 $tooltips = $('*[data-ld-tooltip]'); 950 } 951 952 if ($tooltips.length) { 953 $('body').prepend('<div id="learndash-tooltips"></div>'); 954 var $ctr = 1; 955 $tooltips.each(function () { 956 var anchor = $(this); 957 if (anchor.hasClass('ld-item-list-item')) { 958 anchor = anchor.find('.ld-item-title'); 959 } 960 961 /** 962 * Prevent calendar icon from being clickable. 963 */ 964 if ( 965 'undefined' !== typeof anchor && 966 $(anchor).hasClass('ld-status-waiting') 967 ) { 968 $(anchor).on('click', function (e) { 969 e.preventDefault(); 970 return false; 971 }); 972 973 // Also prevent parent <a> from being clickable. 974 var parent_anchor = $(anchor).parents('a'); 975 if ('undefined' !== typeof parent_anchor) { 976 $(parent_anchor).on('click', function (e) { 977 e.preventDefault(); 978 return false; 979 }); 980 } 981 } 982 983 var elementOffsets = { 984 top: anchor.offset().top, 985 left: anchor.offset().left + anchor.outerWidth() / 2, 986 }; 987 var $content = $(this).attr('data-ld-tooltip'); 988 var $rel_id = Math.floor(Math.random() * 99999); 989 990 //var $tooltip = '<span id="ld-tooltip-' + $rel_id + '" class="ld-tooltip" style="top:' + elementOffsets.top + 'px; left:' + elementOffsets.left + 'px;">' + $content + '</span>'; 991 var $tooltip = 992 '<span id="ld-tooltip-' + 993 $rel_id + 994 '" class="ld-tooltip">' + 995 $content + 996 '</span>'; 997 $(this).attr('data-ld-tooltip-id', $rel_id); 998 $('#learndash-tooltips').append($tooltip); 999 $ctr++; 1000 var $tooltip = $('#ld-tooltip-' + $rel_id); 1001 $(this) 1002 .on('mouseenter', function () { 1003 $tooltip.addClass('ld-visible'); 1004 }) 1005 .on('mouseleave', function () { 1006 $tooltip.removeClass('ld-visible'); 1007 }); 1008 }); 1009 1010 $(window).on('resize', function () { 1011 // Reposition tooltips after resizing 1012 positionTooltips(); 1013 }); 1014 1015 $(window) 1016 .add('.ld-focus-sidebar-wrapper') 1017 .on('scroll', function () { 1018 // Hide tooltips so they don't persist while scrolling 1019 $('.ld-visible.ld-tooltip').removeClass('ld-visible'); 1020 1021 // Reposition tooltips after scrolling 1022 positionTooltips(); 1023 }); 1024 1025 positionTooltips(); 1026 } 1027 } 1028 1029 function initLoginModal() { 1030 var modal_wrapper = $('.learndash-wrapper-login-modal'); 1031 if ('undefined' !== typeof modal_wrapper && modal_wrapper.length) { 1032 // Move the model to be first element of the body. See LEARNDASH-3503 1033 $(modal_wrapper).prependTo('body'); 1034 } 1035 } 1036 1037 function openLoginModal() { 1038 var modal_wrapper = $('.learndash-wrapper-login-modal'); 1039 if ('undefined' !== typeof modal_wrapper && modal_wrapper.length) { 1040 $(modal_wrapper).addClass('ld-modal-open'); 1041 $(modal_wrapper).removeClass('ld-modal-closed'); 1042 1043 // Removed LEARNDASH-3867 #4 1044 $('html, body').animate( 1045 { 1046 scrollTop: $('.ld-modal', modal_wrapper).offset().top, 1047 }, 1048 50 1049 ); 1050 1051 $('.ld-modal', modal_wrapper).focus(); 1052 } 1053 } 1054 1055 function closeLoginModal() { 1056 var modal_wrapper = $('.learndash-wrapper-login-modal'); 1057 if ('undefined' !== typeof modal_wrapper && modal_wrapper.length) { 1058 $(modal_wrapper).removeClass('ld-modal-open'); 1059 $(modal_wrapper).addClass('ld-modal-closed'); 1060 1061 // Return the focus to the login link that triggers the modal. 1062 $('[data-ld-login-modal-trigger]').focus(); 1063 } 1064 } 1065
1066 /** 1067 * Position legacy LD30 Classic tooltips. 1068 * This is not used by the new tooltips added in 4.21.3. 1069 * Kept for backwards compatibility. 1070 * 1071 * @since 3.0.0 1072 * 1073 * @return {void} 1074 */ 1075 function positionTooltips() { 1076 if ('undefined' !== typeof $tooltips) { 1077 setTimeout(function () { 1078 $tooltips.each(function () { 1079 var anchor = $(this); 1080 var $rel_id = anchor.attr('data-ld-tooltip-id'); 1081 $tooltip = $('#ld-tooltip-' + $rel_id); 1082 1083 if (anchor.hasClass('ld-item-list-item')) { 1084 //anchor = anchor.find('.ld-item-title'); 1085 anchor = anchor.find('.ld-status-icon'); 1086 } 1087 1088 var parent_focus = 1089 jQuery(anchor).parents('.ld-focus-sidebar'); 1090 var left_post = 1091 anchor.offset().left + (anchor.outerWidth() + 10); 1092 if (parent_focus.length) { 1093 left_post = 1094 anchor.offset().left + (anchor.outerWidth() - 18); 1095 } 1096 1097 // Get the main content height 1098 var focusModeMainContentHeight = 1099 $('.ld-focus-main').height(); 1100 1101 // Current tooltip height 1102 var focusModeCurrentTooltipHeight = 1103 anchor.offset().top + -3; 1104 1105 // Position tooltip depending on focus mode or not 1106 if (!focusModeMainContentHeight) { 1107 var anchorTop = anchor.offset().top + -3; 1108 var anchorLeft = anchor.offset().left; 1109 } else { 1110 anchorTop = 1111 focusModeCurrentTooltipHeight < 1112 focusModeMainContentHeight 1113 ? focusModeCurrentTooltipHeight 1114 : focusModeMainContentHeight; 1115 anchorLeft = left_post; 1116 } 1117 1118 $tooltip 1119 .css({ 1120 top: anchorTop, 1121 1122 //'left' : anchor.offset().left + (anchor.outerWidth() / 2), 1123 //'left': left_post, //anchor.offset().left + (anchor.outerWidth() +10), 1124 left: anchorLeft, //anchor.offset().left + (anchor.outerWidth() +10), 1125 'margin-left': 0, 1126 'margin-right': 0, 1127 }) 1128 .removeClass('ld-shifted-left ld-shifted-right'); 1129 if ($tooltip.offset().left <= 0) { 1130 $tooltip 1131 .css({ 1132 'margin-left': Math.abs($tooltip.offset().left), 1133 }) 1134 .addClass('ld-shifted-left'); 1135 } 1136 var $tooltipRight = 1137 $(window).width() - 1138 ($tooltip.offset().left + $tooltip.outerWidth()); 1139 if (0 >= $tooltipRight && 360 < $(window).width()) { 1140 $tooltip 1141 .css({ 'margin-right': Math.abs($tooltipRight) }) 1142 .addClass('ld-shifted-right'); 1143 } 1144 }); 1145 }, 500); 1146 } 1147 } 1148 1149 $('body').on('click', '#ld-profile .ld-reset-button', function (e) { 1150 e.preventDefault(); 1151 1152 $('#ld-profile #course_name_field').val(''); 1153 1154 var searchVars = { 1155 shortcode_instance: $('#ld-profile').data('shortcode_instance'), 1156 }; 1157 1158 searchVars['ld-profile-search'] = $(this) 1159 .parents('.ld-item-search-wrapper') 1160 .find('#course_name_field') 1161 .val(); 1162 searchVars['ld-profile-search-nonce'] = $(this) 1163 .parents('.ld-item-search-wrapper') 1164 .find('form.ld-item-search-fields') 1165 .data('nonce'); 1166 1167 $('#ld-profile #ld-main-course-list').addClass('ld-loading'); 1168 1169 $.ajax({ 1170 type: 'GET', 1171 url: ldVars.ajaxurl + '?action=ld30_ajax_profile_search', 1172 data: searchVars, 1173 success(response) { 1174 if ('undefined' !== typeof response.data.markup) { 1175 $('#ld-profile').html(response.data.markup); 1176 ldToggleExpandableElement( 1177 '#ld-profile .ld-search-prompt', 1178 true 1179 ); 1180 } 1181 }, 1182 }); 1183 }); 1184 1185 $('body').on('submit', '.ld-item-search-fields', function (e) { 1186 e.preventDefault(); 1187 1188 var searchVars = { 1189 shortcode_instance: $('#ld-profile').data('shortcode_instance'), 1190 }; 1191 1192 searchVars['ld-profile-search'] = $(this) 1193 .parents('.ld-item-search-wrapper') 1194 .find('#course_name_field') 1195 .val(); 1196 searchVars['ld-profile-search-nonce'] = $(this) 1197 .parents('.ld-item-search-wrapper') 1198 .find('form.ld-item-search-fields') 1199 .data('nonce'); 1200 1201 $('#ld-profile #ld-main-course-list').addClass('ld-loading'); 1202 1203 $.ajax({ 1204 type: 'GET', 1205 url: ldVars.ajaxurl + '?action=ld30_ajax_profile_search', 1206 data: searchVars, 1207 success(response) { 1208 if ('undefined' !== typeof response.data.markup) { 1209 $('#ld-profile').html(response.data.markup); 1210 ldToggleExpandableElement( 1211 '#ld-profile .ld-search-prompt', 1212 true 1213 ); 1214 } 1215 }, 1216 }); 1217 }); 1218 1219 $('body').on('click', '.ld-pagination a', function (e) { 1220 e.preventDefault(); 1221 1222 var linkVars = {}; 1223 var parentVars = {}; 1224 1225 $(this) 1226 .attr('href') 1227 .replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { 1228 linkVars[key] = value; 1229 }); 1230 1231 linkVars.pager_nonce = $(this) 1232 .parents('.ld-pagination') 1233 .data('pager-nonce'); 1234 1235 linkVars.pager_results = $(this) 1236 .parents('.ld-pagination') 1237 .data('pager-results'); 1238 1239 linkVars.context = $(this).data('context'); 1240 1241 parentVars.currentTarget = e.currentTarget; 1242 1243 if ('profile' != linkVars.context) { 1244 linkVars.lesson_id = $(this).data('lesson_id'); 1245 linkVars.course_id = $(this).data('course_id'); 1246 1247 if ($('.ld-course-nav-' + linkVars.course_id).length) { 1248 linkVars.widget_instance = $( 1249 '.ld-course-nav-' + linkVars.course_id 1250 ).data('widget_instance'); 1251 } 1252 } 1253 1254 if ('course_topics' == linkVars.context) { 1255 $('#ld-topic-list-' + linkVars.lesson_id).addClass('ld-loading'); 1256 $('#ld-nav-content-list-' + linkVars.lesson_id).addClass( 1257 'ld-loading' 1258 ); 1259 } 1260 1261 if ('course_content_shortcode' == linkVars.context) { 1262 parentVars.parent_container = $(parentVars.currentTarget).closest( 1263 '.ld-course-content-' + linkVars.course_id 1264 ); 1265 if ( 1266 'undefined' !== typeof parentVars.parent_container && 1267 parentVars.parent_container.length 1268 ) { 1269 $(parentVars.parent_container).addClass('ld-loading'); 1270 linkVars.shortcode_instance = $( 1271 parentVars.parent_container 1272 ).data('shortcode_instance'); 1273 } else { 1274 $('.ld-course-content-' + linkVars.course_id).addClass( 1275 'ld-loading' 1276 ); 1277 linkVars.shortcode_instance = $( 1278 '.ld-course-content-' + linkVars.course_id 1279 ).data('shortcode_instance'); 1280 } 1281 } else if ('course_lessons' == linkVars.context) { 1282 var parent_container; 1283 1284 // Check if we are within the Course Navigation Widget. 1285 if ( 1286 'undefined' === typeof parentVars.parent_container || 1287 !parentVars.parent_container.length 1288 ) { 1289 parent_container = $(parentVars.currentTarget).parents( 1290 '.ld-lesson-navigation' 1291 ); 1292 if ( 1293 'undefined' !== typeof parent_container && 1294 parent_container.length 1295 ) { 1296 parentVars.context_sub = 'course_navigation_widget'; 1297 parentVars.parent_container = $( 1298 parentVars.currentTarget 1299 ).parents('#ld-lesson-list-' + linkVars.course_id); 1300 } 1301 } 1302 1303 // Check if we are within the Focus Mode Sidebar. 1304 if ( 1305 'undefined' === typeof parentVars.parent_container || 1306 !parentVars.parent_container.length 1307 ) { 1308 parent_container = $(parentVars.currentTarget).parents( 1309 '.ld-focus-sidebar-wrapper' 1310 ); 1311 if ( 1312 'undefined' !== typeof parent_container && 1313 parent_container.length 1314 ) { 1315 parentVars.context_sub = 'focus_mode_sidebar'; 1316 parentVars.parent_container = $( 1317 parentVars.currentTarget 1318 ).parents('#ld-lesson-list-' + linkVars.course_id); 1319 } 1320 } 1321 1322 if ( 1323 'undefined' === typeof parentVars.parent_container || 1324 !parentVars.parent_container.length 1325 ) { 1326 parentVars.parent_container = $( 1327 parentVars.currentTarget 1328 ).closest( 1329 '#ld-item-list-' + linkVars.course_id, 1330 '#ld-lesson-list-' + linkVars.course_id 1331 ); 1332 } 1333 if ( 1334 'undefined' !== typeof parentVars.parent_container && 1335 parentVars.parent_container.length 1336 ) { 1337 $(parentVars.parent_container).addClass('ld-loading'); 1338 } else { 1339 // Fallback solution. 1340 $('#ld-item-list-' + linkVars.course_id).addClass('ld-loading'); 1341 $('#ld-lesson-list-' + linkVars.course_id).addClass( 1342 'ld-loading' 1343 ); 1344 } 1345 } 1346 1347 if ('profile' == linkVars.context) { 1348 $('#ld-profile #ld-main-course-list').addClass('ld-loading'); 1349 linkVars.shortcode_instance = 1350 $('#ld-profile').data('shortcode_instance'); 1351 } 1352 1353 if ('profile_quizzes' == linkVars.context) { 1354 $( 1355 '#ld-course-list-item-' + 1356 linkVars.pager_results.quiz_course_id + 1357 ' .ld-item-contents' 1358 ).addClass('ld-loading'); 1359 } 1360 1361 if ('course_info_courses' == linkVars.context) { 1362 $('.ld-user-status').addClass('ld-loading'); 1363 linkVars.shortcode_instance = 1364 $('.ld-user-status').data('shortcode-atts'); 1365 } 1366 1367 if ('group_courses' == linkVars.context) { 1368 linkVars.group_id = $(this).data('group_id'); 1369 if ('undefined' !== typeof linkVars.group_id) { 1370 parent_container = $(parentVars.currentTarget).parents( 1371 '.ld-group-courses-' + linkVars.group_id 1372 ); 1373 if ( 1374 'undefined' !== typeof parent_container && 1375 parent_container.length 1376 ) {
1377 $(parent_container).addClass('ld-loading'); 1378 parentVars.parent_container = parent_container; 1379 } 1380 } 1381 } 1382 1383 $.ajax({ 1384 type: 'GET', 1385 url: ldVars.ajaxurl + '?action=ld30_ajax_pager', 1386 data: linkVars, 1387 success(response) { 1388 // If we have a course listing, update 1389 1390 if ('course_topics' == linkVars.context) { 1391 if ($('#ld-topic-list-' + linkVars.lesson_id).length) { 1392 if ('undefined' !== typeof response.data.topics) { 1393 $('#ld-topic-list-' + linkVars.lesson_id).html( 1394 response.data.topics 1395 ); 1396 } 1397 1398 if ('undefined' !== typeof response.data.pager) { 1399 $('#ld-expand-' + linkVars.lesson_id) 1400 .find('.ld-table-list-footer') 1401 .html(response.data.pager); 1402 } 1403 1404 learndashSetMaxHeight( 1405 $('.ld-lesson-item-' + linkVars.lesson_id).find( 1406 '.ld-item-list-item-expanded' 1407 ) 1408 ); 1409 1410 $('#ld-topic-list-' + linkVars.lesson_id).removeClass( 1411 'ld-loading' 1412 ); 1413 } 1414 1415 if ( 1416 $('#ld-nav-content-list-' + linkVars.lesson_id).length 1417 ) { 1418 if ('undefined' !== typeof response.data.nav_topics) { 1419 $('#ld-nav-content-list-' + linkVars.lesson_id) 1420 .find('.ld-table-list-items') 1421 .html(response.data.topics); 1422 } 1423 1424 if ('undefined' !== typeof response.data.pager) { 1425 $('#ld-nav-content-list-' + linkVars.lesson_id) 1426 .find('.ld-table-list-footer') 1427 .html(response.data.pager); 1428 } 1429 1430 $( 1431 '#ld-nav-content-list-' + linkVars.lesson_id 1432 ).removeClass('ld-loading'); 1433 } 1434 } 1435 1436 if ('course_content_shortcode' == linkVars.context) { 1437 if ('undefined' !== typeof response.data.markup) { 1438 if ( 1439 'undefined' !== 1440 typeof parentVars.parent_container && 1441 parentVars.parent_container.length 1442 ) { 1443 $(parentVars.parent_container).replaceWith( 1444 response.data.markup 1445 ); 1446 } else { 1447 $( 1448 '#learndash_post_' + linkVars.course_id 1449 ).replaceWith(response.data.markup); 1450 } 1451 } 1452 } else if ('course_lessons' == linkVars.context) { 1453 if ( 1454 'undefined' !== typeof parentVars.parent_container && 1455 parentVars.parent_container.length 1456 ) { 1457 if ( 1458 'course_navigation_widget' == parentVars.context_sub 1459 ) { 1460 if ( 1461 'undefined' !== typeof response.data.nav_lessons 1462 ) { 1463 $(parentVars.parent_container) 1464 .html(response.data.nav_lessons) 1465 .removeClass('ld-loading'); 1466 } 1467 } else if ( 1468 'focus_mode_sidebar' == parentVars.context_sub 1469 ) { 1470 if ( 1471 'undefined' !== typeof response.data.nav_lessons 1472 ) { 1473 $(parentVars.parent_container) 1474 .html(response.data.nav_lessons) 1475 .removeClass('ld-loading'); 1476 } 1477 } else if ( 1478 'undefined' !== typeof response.data.lessons 1479 ) { 1480 $(parentVars.parent_container) 1481 .html(response.data.lessons) 1482 .removeClass('ld-loading'); 1483 } 1484 } else { 1485 if ($('#ld-item-list-' + linkVars.course_id).length) { 1486 if ('undefined' !== typeof response.data.lessons) { 1487 $('#ld-item-list-' + linkVars.course_id) 1488 .html(response.data.lessons) 1489 .removeClass('ld-loading'); 1490 } 1491 } 1492 1493 if ($('#ld-lesson-list-' + linkVars.course_id).length) { 1494 if ( 1495 'undefined' !== typeof response.data.nav_lessons 1496 ) { 1497 $('#ld-lesson-list-' + linkVars.course_id) 1498 .html(response.data.nav_lessons) 1499 .removeClass('ld-loading'); 1500 } 1501 } 1502 } 1503 } 1504 1505 if ('group_courses' == linkVars.context) { 1506 if ( 1507 'undefined' !== typeof parentVars.parent_container && 1508 parentVars.parent_container.length 1509 ) { 1510 if ('undefined' !== typeof response.data.markup) { 1511 $(parentVars.parent_container) 1512 .html(response.data.markup) 1513 .removeClass('ld-loading'); 1514 } 1515 } 1516 } 1517 1518 if ('profile' == linkVars.context) { 1519 if ('undefined' !== typeof response.data.markup) { 1520 $('#ld-profile').html(response.data.markup); 1521 } 1522 } 1523 1524 if ('profile_quizzes' == linkVars.context) { 1525 if ('undefined' !== typeof response.data.markup) { 1526 $( 1527 '#ld-course-list-item-' + 1528 linkVars.pager_results.quiz_course_id + 1529 ' .ld-item-list-item-expanded .ld-item-contents' 1530 ).replaceWith(response.data.markup); 1531 $( 1532 '#ld-course-list-item-' + 1533 linkVars.pager_results.quiz_course_id 1534 ) 1535 .get(0) 1536 .scrollIntoView({ behavior: 'smooth' }); 1537 } 1538 } 1539
1540 if ('course_info_courses' == linkVars.context) { 1541 if ('undefined' !== typeof response.data.markup) { 1542 $('.ld-user-status').replaceWith(response.data.markup); 1543 } 1544 } 1545 1546 $('body').trigger('ld_has_paginated'); 1547 1548 initTooltips(); 1549 }, 1550 }); 1551 }); 1552 1553 if ($('#learndash_timer').length) { 1554 var timer_el = jQuery('#learndash_timer'); 1555 var timer_seconds = timer_el.attr('data-timer-seconds'); 1556 var timer_button_el = jQuery(timer_el.attr('data-button')); 1557 1558 var cookie_key = timer_el.attr('data-cookie-key'); 1559 1560 if ('undefined' !== typeof cookie_key) { 1561 var cookie_name = 'learndash_timer_cookie_' + cookie_key; 1562 } else { 1563 var cookie_name = 'learndash_timer_cookie'; 1564 } 1565 1566 cookie_timer_seconds = jQuery.cookie(cookie_name); 1567 1568 if ('undefined' !== typeof cookie_timer_seconds) { 1569 timer_seconds = parseInt(cookie_timer_seconds); 1570 } 1571 1572 if (0 == timer_seconds) { 1573 $(timer_el).hide(); 1574 } 1575 1576 $(timer_button_el).on('learndash-time-finished', function () { 1577 $(timer_el).hide(); 1578 }); 1579 } 1580 1581 $(document).on('learndash_video_disable_assets', function (event, status) { 1582 if ('undefined' === typeof learndash_video_data) { 1583 return false; 1584 } 1585 1586 if ('BEFORE' == learndash_video_data.videos_shown) { 1587 if (true == status) { 1588 $('.ld-lesson-topic-list').hide(); 1589 $('.ld-lesson-navigation') 1590 .find('#ld-nav-content-list-' + ldVars.postID) 1591 .addClass('user_has_no_access'); 1592 $('.ld-quiz-list').hide(); 1593 } else { 1594 $('.ld-lesson-topic-list').slideDown(); 1595 $('.ld-quiz-list').slideDown(); 1596 $('.ld-lesson-navigation') 1597 .find('#ld-nav-content-list-' + ldVars.postID) 1598 .removeClass('user_has_no_access'); 1599 } 1600 } 1601 }); 1602 1603 $('.learndash-wrapper').on( 1604 'click', 1605 '.wpProQuiz_questionListItem input[type="radio"]', 1606 function (e) { 1607 $(this) 1608 .parents('.wpProQuiz_questionList') 1609 .find('label') 1610 .removeClass('is-selected'); 1611 $(this).parents('label').addClass('is-selected'); 1612 } 1613 ); 1614 1615 $('.learndash-wrapper').on( 1616 'click', 1617 '.wpProQuiz_questionListItem input[type="checkbox"]', 1618 function (e) { 1619 if (jQuery(e.currentTarget).is(':checked')) { 1620 $(this).parents('label').addClass('is-selected'); 1621 } else { 1622 $(this).parents('label').removeClass('is-selected'); 1623 } 1624 } 1625 ); 1626 1627 function learndash_ld30_show_user_statistic(e) { 1628 e.preventDefault(); 1629 1630 var refId = jQuery(this).data('ref-id'); 1631 var quizId = jQuery(this).data('quiz-id'); 1632 var userId = jQuery(this).data('user-id'); 1633 var statistic_nonce = jQuery(this).data('statistic-nonce'); 1634 var post_data = { 1635 action: 'wp_pro_quiz_admin_ajax_statistic_load_user', 1636 func: 'statisticLoadUser', 1637 data: { 1638 quizId, 1639 userId, 1640 refId, 1641 statistic_nonce, 1642 avg: 0, 1643 }, 1644 }; 1645 1646 jQuery('#wpProQuiz_user_overlay, #wpProQuiz_loadUserData').show(); 1647 var content = jQuery('#wpProQuiz_user_content').hide(); 1648 1649 jQuery.ajax({ 1650 type: 'POST', 1651 url: ldVars.ajaxurl, 1652 dataType: 'json', 1653 cache: false, 1654 data: post_data, 1655 error(jqXHR, textStatus, errorThrown) {}, 1656 success(reply_data) { 1657 if ('undefined' !== typeof reply_data.html) { 1658 content.html(reply_data.html); 1659 jQuery('#wpProQuiz_user_content').show(); 1660 1661 jQuery('body').trigger( 1662 'learndash-statistics-contentchanged' 1663 ); 1664 1665 jQuery('#wpProQuiz_loadUserData').hide(); 1666 1667 content.find('.statistic_data').on('click', function () { 1668 jQuery(this).parents('tr').next().toggle('fast'); 1669 1670 return false; 1671 }); 1672 } 1673 }, 1674 }); 1675 1676 jQuery('#wpProQuiz_overlay_close').on('click', function () { 1677 jQuery('#wpProQuiz_user_overlay').hide(); 1678 }); 1679 } 1680 1681 function learndashSetMaxHeight(elm) { 1682 var totalHeight = 0; 1683 1684 elm.find('> *').each(function () { 1685 totalHeight += $(this).outerHeight(); 1686 }); 1687 1688 elm.attr('data-height', '' + (totalHeight + 50) + ''); 1689 1690 elm.css({ 1691 'max-height': totalHeight + 50, 1692 }); 1693 } 1694 1695 /** 1696 * Will scroll the position of the Focus Mode sidebar 1697 * to the active step. 1698 */ 1699 function learndashFocusModeSidebarAutoScroll() { 1700 if (jQuery('.learndash-wrapper .ld-focus').length) { 1701 var sidebar_wrapper = jQuery( 1702 '.learndash-wrapper .ld-focus .ld-focus-sidebar-wrapper' 1703 ); 1704 1705 var sidebar_current_topic = jQuery( 1706 '.learndash-wrapper .ld-focus .ld-focus-sidebar-wrapper .ld-is-current-item' 1707 ); 1708 if ( 1709 'undefined' !== typeof sidebar_current_topic && 1710 sidebar_current_topic.length 1711 ) { 1712 var sidebar_scrollTo = sidebar_current_topic; 1713 } else { 1714 var sidebar_current_lesson = jQuery( 1715 '.learndash-wrapper .ld-focus .ld-focus-sidebar-wrapper .ld-is-current-lesson' 1716 ); 1717 if ( 1718 'undefined' !== typeof sidebar_current_lesson && 1719 sidebar_current_lesson.length 1720 ) { 1721 var sidebar_scrollTo = sidebar_current_lesson; 1722 } 1723 } 1724 1725 if ( 1726 'undefined' !== typeof sidebar_scrollTo && 1727 sidebar_scrollTo.length 1728 ) { 1729 var offset_top = 0; 1730 if ( 1731 jQuery('.learndash-wrapper .ld-focus .ld-focus-header') 1732 .length 1733 ) { 1734 var logo_height = jQuery( 1735 '.learndash-wrapper .ld-focus .ld-focus-header' 1736 ).height(); 1737 offset_top += logo_height; 1738 } 1739 if ( 1740 jQuery( 1741 '.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-course-navigation-heading' 1742 ).length 1743 ) { 1744 var heading_height = jQuery( 1745 '.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-course-navigation-heading' 1746 ).height(); 1747 offset_top += heading_height; 1748 } 1749 if ( 1750 jQuery( 1751 '.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-focus-sidebar-wrapper' 1752 ).length 1753 ) { 1754 var container_height = jQuery( 1755 '.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-focus-sidebar-wrapper' 1756 ).height(); 1757 offset_top += container_height; 1758 } 1759 1760 var current_item_height = jQuery(sidebar_scrollTo).height(); 1761 offset_top -= current_item_height; 1762 1763 sidebar_wrapper.animate( 1764 { 1765 scrollTop: sidebar_scrollTo.offset().top - offset_top, 1766 }, 1767 1000 1768 ); 1769 } 1770 } 1771 } 1772 1773 // Coupon processing. 1774 1775 function update_payment_forms(data) { 1776 $('#total-row').attr('data-total', data.total.value); 1777 1778 // Update PayPal form amount. 1779 $('form[name="buynow"] input[name="amount"]').val(data.total.value); 1780 1781 // Update Stripe form amount. 1782 $('form.learndash-stripe-checkout input[name="stripe_price"]').val( 1783 data.total.stripe_value 1784 ); 1785 1786 // Remove Stripe Connect session to respect the new amount.
1787 const stripe_course_id = $( 1788 '.learndash-stripe-checkout input[name="stripe_course_id"]' 1789 ).val(); 1790 1791 if (stripe_course_id) { 1792 LD_Cookies.remove('ld_stripe_session_id_' + stripe_course_id); // Stripe Plugin (Checkout). 1793 LD_Cookies.remove( 1794 'ld_stripe_connect_session_id_' + stripe_course_id 1795 ); // Stripe Connect in core. 1796 } 1797 1798 // Re-init Stripe Plugin (Legacy) to respect the new amount. 1799 if (typeof ld_init_stripe_legacy === 'function') { 1800 ld_init_stripe_legacy(); 1801 } 1802 } 1803 1804 $('.btn-join').on('click', function (e) { 1805 if ($(this).hasClass('btn-disabled')) { 1806 e.preventDefault(); 1807 return false; 1808 } 1809 1810 const supportsCoupon = $('#total-row').attr('data-supports-coupon'); 1811 1812 if (!supportsCoupon) { 1813 return; 1814 } 1815 1816 const total = parseFloat($('#total-row').attr('data-total')); 1817 1818 if (0 === total) { 1819 $.ajax({ 1820 type: 'POST', 1821 url: ldVars.ajaxurl, 1822 dataType: 'json', 1823 cache: false, 1824 data: { 1825 action: 'learndash_enroll_with_zero_price', 1826 nonce: $('#apply-coupon-form').data('nonce'), 1827 post_id: $('#apply-coupon-form').data('post-id'), 1828 }, 1829 success(response) { 1830 if (response.success) { 1831 window.location.replace(response.data.redirect_url); 1832 } else { 1833 alert(response.data.message); 1834 } 1835 }, 1836 }); 1837 1838 e.preventDefault(); 1839 return false; 1840 } 1841 }); 1842 1843 $('#apply-coupon-form').on('submit', function (e) { 1844 e.preventDefault(); 1845 const $el = $(this); 1846 const $wrapper = $el.closest('.ld-registration-order__items'); 1847 1848 $.ajax({ 1849 type: 'POST', 1850 url: ldVars.ajaxurl, 1851 dataType: 'json', 1852 cache: false, 1853 data: { 1854 action: 'learndash_apply_coupon', 1855 nonce: $(this).data('nonce'), 1856 coupon_code: $(this).find('#coupon-field').val(), 1857 post_id: $(this).data('post-id'), 1858 }, 1859 success(response) { 1860 const isModernRegistration = $( 1861 '.ld-form__field-coupon_field' 1862 ).length; 1863 1864 if (isModernRegistration) { 1865 $('#coupon-alerts .coupon-alert').hide(); 1866 1867 const $alert = $('#coupon-alerts').find( 1868 response.success 1869 ? '.coupon-alert-success' 1870 : '.coupon-alert-warning' 1871 ); 1872 1873 if (response.success) { 1874 $wrapper 1875 .find('.ld-coupon__label-text') 1876 .html(response.data.coupon_code); // Set coupon code in totals. 1877 1878 $wrapper 1879 .find('.ld-coupon__value') 1880 .html('(' + response.data.discount + ')'); // Set discount value in totals. 1881 1882 $wrapper 1883 .find('.ld-registration-order__item-price-value') 1884 .html(response.data.total.formatted); // Update course/group price. 1885 1886 $wrapper 1887 .find('.ld-registration-order__total-price') 1888 .html(response.data.total.formatted); // Update Total. 1889 1890 $wrapper.addClass( 1891 'ld-registration-order__items--with-coupon' 1892 ); 1893 1894 update_payment_forms(response.data); 1895 } 1896 1897 $alert 1898 .find('.ld-alert-messages') 1899 .html(response.data.message); 1900 $alert.fadeIn(); 1901 } else { 1902 $('#coupon-alerts .coupon-alert').hide(); 1903 1904 const $alert = $('#coupon-alerts').find( 1905 response.success 1906 ? '.coupon-alert-success' 1907 : '.coupon-alert-warning' 1908 ); 1909 1910 const $coupon_row = $('#coupon-row'); 1911 1912 if (response.success) { 1913 $coupon_row 1914 .find('.purchase-label > span') 1915 .html(response.data.coupon_code); // Set coupon code in totals. 1916 $coupon_row 1917 .find('.purchase-value span') 1918 .html(response.data.discount); // Set discount value in totals. 1919 $coupon_row.css('display', 'flex').hide().fadeIn(); // Show a coupon row in totals. 1920 $('#total-row .purchase-value').html( 1921 response.data.total.formatted 1922 ); // Update Total. 1923 $('#totals').show(); 1924 1925 update_payment_forms(response.data); 1926 } 1927 1928 $alert 1929 .find('.ld-alert-messages') 1930 .html(response.data.message); 1931 $alert.fadeIn(); 1932 } 1933 }, 1934 }); 1935 }); 1936 1937 $('#remove-coupon-form').on('submit', function (e) { 1938 e.preventDefault(); 1939 const $el = $(this); 1940 const $wrapper = $el.closest('.ld-registration-order__items'); 1941 1942 $.ajax({ 1943 type: 'POST', 1944 url: ldVars.ajaxurl, 1945 dataType: 'json', 1946 cache: false, 1947 data: { 1948 action: 'learndash_remove_coupon', 1949 nonce: $(this).data('nonce'), 1950 post_id: $(this).data('post-id'), 1951 }, 1952 success(response) { 1953 const isModernRegistration = $( 1954 '.ld-form__field-coupon_field' 1955 ).length; 1956 1957 if (isModernRegistration) { 1958 $('#coupon-alerts .coupon-alert').hide(); 1959 1960 const $alert = $('#coupon-alerts').find( 1961 response.success 1962 ? '.coupon-alert-success' 1963 : '.coupon-alert-warning' 1964 ); 1965 1966 if (response.success) { 1967 $wrapper.removeClass( 1968 'ld-registration-order__items--with-coupon' 1969 ); 1970 $wrapper.find('.ld-form__field-coupon_field').val(''); // Set coupon field empty. 1971 $wrapper 1972 .find('.ld-registration-order__item-price-value') 1973 .html(response.data.total.formatted); // Update course/group price. 1974 $wrapper 1975 .find('.ld-registration-order__total-price') 1976 .html(response.data.total.formatted); // Update Total. 1977 1978 update_payment_forms(response.data); 1979 } 1980 1981 $alert 1982 .find('.ld-alert-messages') 1983 .html(response.data.message); 1984 $alert.fadeIn(); 1985 } else { 1986 $('#coupon-alerts .coupon-alert').hide(); 1987 1988 const $alert = $('#coupon-alerts').find( 1989 response.success 1990 ? '.coupon-alert-success' 1991 : '.coupon-alert-warning' 1992 ); 1993 1994 if (response.success) { 1995 $('#coupon-row').hide(); // Hide a coupon row in totals. 1996 $('#coupon-field').val(''); // Set coupon field empty. 1997 $('#price-row .purchase-value').html( 1998 response.data.total.formatted 1999 ); // Update Price. 2000 $('#subtotal-row .purchase-value').html( 2001 response.data.total.formatted 2002 ); // Update Subtotal. 2003 $('#total-row .purchase-value').html( 2004 response.data.total.formatted 2005 ); // Update Total. 2006 $('#totals').hide(); 2007 2008 update_payment_forms(response.data); 2009 } 2010 2011 $alert 2012 .find('.ld-alert-messages') 2013 .html(response.data.message); 2014 $alert.fadeIn(); 2015 } 2016 }, 2017 }); 2018 }); 2019}); 2020 2021function ldGetUrlVars() { 2022 var vars = {}; 2023 var parts = window.location.href.replace( 2024 /[?&]+([^=&]+)=([^&]*)/gi, 2025 function (m, key, value) { 2026 vars[key] = value; 2027 } 2028 ); 2029 2030 return vars; 2031}
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.