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