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