1(function ($) { 2 'use strict'; 3 4 $(document).ready(function () { 5 const applyFadeGradient = () => { 6 /** 7 * Match Fade gradient Shadow color 8 * Used it for the fade gradient shadow on the Read More button 9 * @type {string} 10 */ 11 let bgColor = window 12 .getComputedStyle(document.querySelector('.doc_documentation_area'), null) 13 .getPropertyValue('background-color'), 14 bgColorRGBA = bgColor.replace(')', ', 0)').replace('rgb', 'rgba'); 15 16 if (bgColor) { 17 $('.fadeGradient').css( 18 'background', 19 '-webkit-linear-gradient(bottom, ' + 20 bgColor + 21 ' 15%, ' + 22 bgColorRGBA + 23 ' 100%)' 24 ); 25 } 26 } 27 28 // Apply fade gradient initially 29 applyFadeGradient(); 30 31 /** 32 * Watch for body background-color changes and reapply fade gradient 33 * This handles dark/light mode switches and any other background color changes 34 */ 35 (function () { 36 let lastBgColor = window.getComputedStyle(document.body, null).getPropertyValue('background-color'); 37 38 // Create a MutationObserver to watch for changes on the body element 39 const observer = new MutationObserver(function (mutations) { 40 // Get the current background color 41 const currentBgColor = window.getComputedStyle(document.body, null).getPropertyValue('background-color'); 42 43 // Only call applyFadeGradient if the background color actually changed 44 if (currentBgColor !== lastBgColor) { 45 lastBgColor = currentBgColor; 46 applyFadeGradient(); 47 } 48 }); 49 50 // Start observing the body element for attribute and style changes 51 observer.observe(document.body, { 52 attributes: true, // Watch for attribute changes (like class changes) 53 attributeFilter: ['class', 'style'], // Only watch class and style attributes 54 childList: false, // Don't watch for child additions/removals
55 subtree: false // Don't watch descendants 56 }); 57 })(); 58 59 /** 60 * Social Share options 61 * Copy the current page link to clipboard 62 */ 63 if ($('.share-this-doc').length) { 64 $('.share-this-doc').on('click', function (e) { 65 e.preventDefault(); 66 let success_message = $(this).data('success-message'); 67 let $temp = $('<input>'); 68 $('body').append($temp); 69 $temp.val($(location).attr('href')).select(); 70 document.execCommand('copy'); 71 $temp.remove(); 72 73 setTimeout(function () { 74 $('.ezd-link-copied-wrap') 75 .text(success_message) 76 .addClass('copied'); 77 }, 500); 78 79 setTimeout(function () { 80 $('.ezd-link-copied-wrap').removeClass('copied'); 81 }, 3500); 82 }); 83 } 84 85 $('.ezd-link-copied-wrap').click(function () { 86 $(this).removeClass('copied'); 87 }); 88 89 $.fn.ezd_social_popup = function ( 90 e, 91 intWidth, 92 intHeight, 93 strResize, 94 blnResize 95 ) { 96 // Prevent default anchor event 97 e.preventDefault(); 98 99 // Set values for window 100 intWidth = intWidth || '500'; 101 intHeight = intHeight || '400'; 102 strResize = blnResize ? 'yes' : 'no'; 103 104 // Set title and open popup with focus on it 105 var strTitle = 106 typeof this.attr('title') !== 'undefined' 107 ? this.attr('title') 108 : 'Social Share', 109 strParam = 110 'width=' + 111 intWidth + 112 ',height=' + 113 intHeight + 114 ',resizable=' + 115 strResize, 116 objWindow = window 117 .open(this.attr('href'), strTitle, strParam) 118 .focus(); 119 }; 120 $('.social-links a:not(:first)').on('click', function (e) { 121 $(this).ezd_social_popup(e); 122 }); 123 124 // Add scroll spy attributes to body 125 $('body').attr({ 126 // 'data-bs-spy': 'scroll', 127 'data-bs-target': '#eazydocs-toc', 128 }); 129 130 // Sanitize URL hash on page load 131 if (window.location.hash) { 132 try { 133 // Decode the percent-encoded hash 134 const decoded = decodeURIComponent(window.location.hash.substring(1)); 135 const safe = decoded.normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^\w\-]/g, ''); 136 137 // If sanitized differs, rewrite it before any plugin runs 138 if (safe && safe !== window.location.hash.substring(1)) { 139 // Update the URL fragment instantly (no reload) 140 history.replaceState(null, '', '#' + safe); 141 } 142 } catch (e) { 143 console.warn('Invalid hash encoding:', e); 144 } 145 } 146 147 /** 148 * Make the Titles clickable with anchor js 149 * If no selector is provided, it falls back to a default selector of: 150 * 'h2, h3, h4, h5, h6' 151 */ 152 const ezdHeadingSelector = '.doc-scrollable h2, .doc-scrollable h3, .doc-scrollable h4'; 153 function ezdSetupHeadingAnchors() { 154 const $headings = $(ezdHeadingSelector); 155 $headings.each(function () { 156 const text = $(this).text(); 157 const clean = text.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9\s\-_\/]/g, '').trim().replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-+|-+$/g, ''); 158 if (clean) { 159 $(this).attr('id', clean); 160 } 161 }); 162 $headings.find('.anchorjs-link').remove(); 163 anchors.add(ezdHeadingSelector); 164 } 165 166 window.ezd_heading_anchors = ezdSetupHeadingAnchors; 167 ezdSetupHeadingAnchors(); 168 169 // Anchor JS scroll â use getElementById so IDs with / or _ don't break jQuery CSS selectors 170 var urlHash = window.location.href.split('#')[1]; 171 if (urlHash) { 172 var hashTarget = document.getElementById(decodeURIComponent(urlHash)); 173 if (hashTarget) { 174 $('html,body').animate({ 175 scrollTop: $(hashTarget).offset().top 176 }, 30); 177 } 178 } 179 180 /** 181 * Feedback Contact Form Ajax Handler 182 */ 183 $('form#edocs-contact-form').on('submit', function (e) { 184 e.preventDefault(); 185 let that = $(this), 186 url = that.attr('action'), 187 type = that.attr('method'); 188 let name = $('#name').val(); 189 let email = $('#email').val(); 190 let subject = $('#subject').val(); 191 let doc_id = $('#doc_id').val(); 192 let message = $('#massage').val(); 193 194 if (nameDisallowedCharacters(name)) { 195 $('.form-name-field').append('<span class="ezd-input-warning-text">Special characters not allowed</span>'); 196 $('.ezd-input-warning-text:not(:last)').remove(); 197 // remove the error message after 3 seconds 198 setTimeout(function () { 199 $('.ezd-input-warning-text').remove(); 200 }, 3000); 201 202 } else if (emailDisallowedCharacters(email)) { 203 $('.form-email-field').append('<span class="ezd-input-warning-text">Invalid email format.</span>'); 204 $('.ezd-input-warning-text:not(:last)').remove(); 205 // remove the error message after 3 seconds 206 setTimeout(function () { 207 $('.ezd-input-warning-text').remove(); 208 }, 3000); 209 210 } else { 211 212 $.ajax({ 213 url: eazydocs_local_object.ajaxurl, 214 type: 'post', 215 dataType: 'json', 216 data: { 217 action: 'eazydocs_feedback_email', 218 name: name, 219 email: email, 220 subject: subject, 221 doc_id: doc_id, 222 message: message, 223 security: eazydocs_local_object.nonce, 224 }, 225 beforeSend: function () { 226 $('.eazydocs-form-result').html( 227 '<div class="spinner-border spinner-border-sm" role="status">\n' + 228 '<span class="visually-hidden">Loading...</span>\n' + 229 '</div>' 230 ); 231 }, 232 success: function (response) { 233 if (response.success) { 234 $('.eazydocs-form-result').html(response.data); 235 } else { 236 $('.eazydocs-form-result').html('Something went wrong.'); 237 } 238 setTimeout(function () { 239 $('.eazydocs-form-result').html(''); 240 }, 3000); 241 }, 242 243 error: function () { 244 $('.eazydocs-form-result').html( 245 'Oops! Something wrong, try again!' 246 ); 247 }, 248 }); 249 $('form#edocs-contact-form')[0].reset(); 250 } 251 252 // Check for disallowed characters in the name field 253 function nameDisallowedCharacters(str) { 254 // Regular expression to check for disallowed characters 255 var pattern = /[#$%^&*()+={}\[\];:'",<>\/?@]/; 256 return pattern.test(str); 257 } 258 259 // Check for disallowed characters in the email field 260 function emailDisallowedCharacters(email) { 261 // Regular expression to check for disallowed characters 262 var mailPattern = /[#$%^&*()+={}\[\];:'",<>\/?]/; 263 // Check if there is more than one "@" symbol 264 var atSymbolCount = (email.match(/@/g) || []).length; 265 return atSymbolCount !== 1 || mailPattern.test(email); 266 } 267 }); 268 269 /** 270 * Feedback voting Handler 271 */ 272 $(document).on('click', '.vote-link-wrap a.h_btn', function (e) { 273 e.preventDefault(); 274 let self = $(this); 275 $.ajax({ 276 url: eazydocs_local_object.ajaxurl, 277 method: 'post', 278 data: { 279 action: 'eazydocs_handle_feedback', 280 post_id: self.data('id'), 281 type: self.data('type'), 282 security: eazydocs_local_object.nonce, 283 }, 284 beforeSend: function () { 285 $('.eazydocs-feedback-wrap .vote-link-wrap').html( 286 '<div class="spinner-border spinner-border-sm" role="status">\n' + 287 ' <span class="visually-hidden">Loading...</span>\n' + 288 '</div>' 289 ); 290 }, 291 success: function (response) { 292 $('.eazydocs-feedback-wrap').html(response.data); 293 }, 294 error: function () {
295 console.log('Oops! Something wrong, try again!'); 296 }, 297 }); 298 }); 299 300 /** 301 * Expand and collapse the sidebar menu on clicking on the arrow icon 302 */ 303 if ($('.nav-sidebar > li').hasClass('active')) { 304 $('.nav-sidebar > li.active').find('ul').slideDown(700); 305 } 306 307 // Handle click event for all .icon elements inside .nav-sidebar 308 $('.nav-sidebar').on('click', '.doc-link .icon', function (e) { 309 // Prevent default action 310 e.preventDefault(); 311 312 // Find the closest parent li element and toggle its active class 313 let parentLi = $(this).closest('li.nav-item'); 314 parentLi.toggleClass('active'); 315 316 // Toggle the display of the immediate child ul element 317 parentLi.children('ul.dropdown_nav').toggle(300); 318 319 // Hide sibling submenus 320 parentLi.siblings().find('ul.dropdown_nav').hide(300); 321 parentLi.siblings().removeClass('active'); 322 }); 323 324 /** 325 * Print doc 326 * 327 * Builds a clean, self-contained printout via printThis: sets a proper 328 * document title (so "Save as PDF" yields a meaningful filename), prepends 329 * a source header (title + URL + date), shows a busy state on the button, 330 * and guards against repeat clicks spawning multiple print dialogs. 331 */ 332 let ezdIsPrinting = false; 333 334 // HTML-escape helper to keep injected title/URL safe. 335 function ezdEscapeHtml(value) { 336 return $('<div>').text(value == null ? '' : value).html(); 337 } 338 339 $('.pageSideSection .print').on('click', function (e) { 340 e.preventDefault(); 341 342 if (ezdIsPrinting) { 343 return; 344 } 345 346 const $btn = $(this); 347 const $content = $( 348 '.doc-middle-content .doc-post-content, body.single-onepage-docs .documentation_info' 349 ); 350 351 if (!$content.length) { 352 return; 353 } 354 355 // Prefer the doc heading for the title; fall back to the page title. 356 const docTitle = ($content.find('h1').first().text() || document.title || '').trim(); 357 const printDate = new Date().toLocaleDateString(undefined, { 358 year: 'numeric', 359 month: 'long', 360 day: 'numeric', 361 }); 362 363 const printHeader = 364 '<div class="ezd-print-header">' + 365 '<span class="ezd-print-header__title">' + ezdEscapeHtml(docTitle) + '</span>' + 366 '<span class="ezd-print-header__meta">' + 367 '<span class="ezd-print-header__url">' + ezdEscapeHtml(window.location.href) + '</span>' + 368 '<span class="ezd-print-header__date">' + ezdEscapeHtml(printDate) + '</span>' + 369 '</span>' + 370 '</div>'; 371 372 ezdIsPrinting = true; 373 $btn.addClass('is-printing').attr('aria-busy', 'true'); 374 375 function ezdRestorePrintButton() { 376 ezdIsPrinting = false; 377 $btn.removeClass('is-printing').removeAttr('aria-busy'); 378 } 379 380 // Append the plugin version so the print stylesheet busts cache on release. 381 const printCssVer = eazydocs_local_object.EZD_VERSION 382 ? '?ver=' + encodeURIComponent(eazydocs_local_object.EZD_VERSION) 383 : ''; 384 385 $content.printThis({ 386 loadCSS: eazydocs_local_object.EZD_STYLES + 'print.css' + printCssVer, 387 pageTitle: docTitle, 388 header: printHeader, 389 afterPrint: ezdRestorePrintButton, 390 }); 391 392 // Safety net if afterPrint never fires (e.g. dialog dismissed instantly). 393 setTimeout(ezdRestorePrintButton, 5000); 394 }); 395 396 /** 397 * ============================ 398 * Navbar Show / Hide Script 399 * ============================ 400 * This script handles dynamic adjustments of the navbar's top margin based on user interactions: 401 * - Clicking internal page anchor links (.doc_menu and .ezd-note-indicator) 402 * - Clicking outside the anchor links 403 * - Scrolling the page 404 * ============================ 405 */ 406 407 // Function to handle navbar margin adjustments 408 function ezdSetNavbarMarginTop(active) { 409 let height = $('.navbar.navbar_fixed').outerHeight(); // Get navbar height 410 let adminBarOffset = $('body').hasClass('admin-bar') ? 32 : 0; // Check for admin bar 411 let marginTop = active ? `-${height}px` : `${adminBarOffset}px`; 412 413 // Apply the appropriate margin and toggle active class 414 $('.navbar.navbar_fixed').toggleClass('doc_menu_active', active).css('margin-top', marginTop); 415 } 416 417 /** 418 * Handle link click for internal page navigation 419 * Applies to both .doc_menu and .ezd-note-indicator mean footnote elements. 420 */ 421 function normalize(str) { 422 return str 423 .replace(/â/g, '-') // EN DASH â dash 424 .normalize('NFD') // normalize accents 425 .replace(/[\u0300-\u036f]/g, '') // remove accents 426 .replace(/[^a-z0-9\s-]/gi, '') // clean symbols
427 .replace(/\s+/g, ' ') // normalize spaces 428 .trim() 429 .toLowerCase(); 430 } 431 432 /** 433 * Build a map from normalized heading TEXT â real ID 434 */ 435 const headingTextMap = {}; 436 document.querySelectorAll('h1[id], h2[id], h3[id]').forEach(h => { 437 headingTextMap[normalize(h.textContent)] = h.id; 438 }); 439 440 /** 441 * Docs menu click handler 442 */ 443 $('.doc_menu a[href^="#"]:not([href="#"]), .ezd-note-indicator').on('click', function (event) { 444 event.preventDefault(); 445 ezdSetNavbarMarginTop(true); 446 447 const linkText = normalize($(this).text()); 448 449 // Find matching heading by text 450 const realId = Object.keys(headingTextMap).find(key => 451 key.includes(linkText) || linkText.includes(key) 452 ); 453 454 if (!realId) { 455 // Parent menu or grouping item â do nothing 456 return; 457 } 458 459 const $el = $('#' + CSS.escape(headingTextMap[realId])); 460 if (!$el.length) return; 461 462 $('html, body').stop().animate({ 463 scrollTop: $el.offset().top 464 }, 900); 465 }); 466 467 /** 468 * Handle scroll events 469 * Reset navbar margin when scrolled near the top. 470 */ 471 $(window).on('scroll', function () { 472 if ($(this).scrollTop() <= $('.navbar.navbar_fixed').outerHeight()) { 473 ezdSetNavbarMarginTop(false); 474 } 475 }); 476 477 /** 478 * ============================ 479 * End of Navbar Show / Hide Script 480 * ============================ 481 */ 482 483 // Ensure sidebars are sticky and toggle logic works properly 484 if ($('.doc_documentation_area').length > 0) { 485 var leftOpen = false; 486 var rightOpen = false; 487 488 // Right sidebar toggle 489 $(document).on('click', '#mobile-right-toggle, .doc_rightsidebar.opened .doc_menu a', function (e) { 490 e.preventDefault(); 491 e.stopPropagation(); 492 if (leftOpen) closeLeftSidebar(); 493 494 if (!rightOpen) { 495 $('.doc_documentation_area').addClass('overlay'); 496 $('.doc_rightsidebar').addClass('opened').animate({ right: '0px' }, 100); 497 rightOpen = true; 498 } else { 499 closeRightSidebar(); 500 } 501 }); 502 503 // Left sidebar toggle 504 $(document).on('click', '#mobile-left-toggle', function (e) { 505 e.preventDefault(); 506 e.stopPropagation(); 507 if (rightOpen) closeRightSidebar(); 508 509 if (!leftOpen) { 510 $('.doc_documentation_area').addClass('overlay'); 511 $('.left-column .doc_left_sidebarlist') 512 .addClass('opened') 513 .animate({ left: '0px' }, 300); 514 leftOpen = true; 515 } else { 516 closeLeftSidebar(); 517 } 518 }); 519 520 // Close sidebars when clicking outside 521 $(document).on('click', function (e) { 522 if ( 523 !$(e.target).closest('.doc_rightsidebar, .doc_rightsidebar.opened .doc_menu a, .left-column, #mobile-right-toggle, #mobile-left-toggle').length 524 ) { 525 if (leftOpen) closeLeftSidebar(); 526 if (rightOpen) closeRightSidebar(); 527 } 528 }); 529 530 function closeLeftSidebar() { 531 var screenWidth = $(window).width(); // Get current window width 532 var animationDistance = (screenWidth < 360) ? '-280px' : '-330px'; // Set distance based on screen width 533 534 $('.doc_documentation_area').removeClass('overlay'); 535 $('.left-column .doc_left_sidebarlist') 536 .removeClass('opened') 537 .animate({ left: animationDistance }, 300); 538 539 leftOpen = false; 540 } 541 542 function closeRightSidebar() { 543 $('.doc_documentation_area').removeClass('overlay'); 544 $('.doc_rightsidebar').removeClass('opened').animate({ right: '-290px' }, 100); 545 rightOpen = false; 546 } 547 } 548 549 // Mobile menu on the Doc single page 550 $(document).on('click', '.single-docs .mobile_menu_btn', function () { 551 $('body').removeClass('menu-is-closed').addClass('menu-is-opened'); 552 }); 553 554 $(document).on('click', '.single-docs .close_nav', function (e) { 555 if ($('.side_menu').hasClass('menu-opened')) { 556 $('.side_menu').removeClass('menu-opened'); 557 $('body').removeClass('menu-is-opened'); 558 } else { 559 $('.side_menu').addClass('menu-opened'); 560 } 561 }); 562 563 /** 564 * Filter doc menu items on the left sidebar 565 **/ 566 if ($('#doc_filter').length) { 567 $('#doc_filter').keyup(function () { 568 var value = $(this).val().toLowerCase(); 569 $('.nav-sidebar .page_item').each(function () { 570 var lcval = $(this).text().toLowerCase(); 571 if (lcval.indexOf(value) > -1) { 572 $(this).show(500); 573 } else { 574 $(this).hide(500); 575 } 576 if (value.length > 0) { 577 $('.left-sidebar-results') 578 .find('li') 579 .addClass('active'); 580 $('.left-sidebar-results').find('li > ul').show(500); 581 } else { 582 $('.left-sidebar-results') 583 .find('li') 584 .removeClass('active'); 585 $('.left-sidebar-results').find('li > ul').hide(500); 586 } 587 }); 588 }); 589 590 document 591 .getElementById('doc_filter') 592 .addEventListener('search', function (event) { 593 $('.nav-sidebar .page_item').show(300); 594 $('.left-sidebar-results').find('li').removeClass('active'); 595 $('.left-sidebar-results').find('li > ul').hide(500); 596 }); 597 } 598 599 /** 600 * Collapse left sidebar 601 **/ 602 function docLeftSidebarToggle() { 603 let left_column = $('.doc_mobile_menu'); 604 let middle_column = $('.doc-middle-content'); 605 $(document).on( 606 'click', 607 '.left-sidebar-toggle .left-arrow', 608 function () { 609 $('.doc_mobile_menu').hide(500); 610 611 if (middle_column.hasClass('ezd-xl-col-7')) { 612 $('.doc-middle-content') 613 .removeClass('ezd-xl-col-7') 614 .addClass('ezd-xl-col-10 ezd-col-extended'); 615 } else if (middle_column.hasClass('ezd-xl-col-8')) { 616 $('.doc-middle-content') 617 .removeClass('ezd-xl-col-8') 618 .addClass('ezd-xl-col-10'); 619 } 620 621 $('.left-sidebar-toggle .left-arrow').hide(500); 622 $('.left-sidebar-toggle .right-arrow').show(500); 623 } 624 ); 625 626 $(document).on( 627 'click', 628 '.left-sidebar-toggle .right-arrow', 629 function () { 630 $('.doc_mobile_menu').show(500); 631 632 if (middle_column.hasClass('ezd-xl-col-10')) { 633 $('.doc-middle-content') 634 .removeClass('ezd-xl-col-10 ezd-col-extended') 635 .addClass('ezd-xl-col-7'); 636 } else if (middle_column.hasClass('ezd-xl-col-8')) { 637 $('.doc-middle-content') 638 .removeClass('ezd-xl-col-10 ezd-col-extended') 639 .addClass('ezd-xl-col-8'); 640 } 641 642 $('.left-sidebar-toggle .left-arrow').show(500); 643 $('.left-sidebar-toggle .right-arrow').hide(500); 644 } 645 ); 646 } 647 648 docLeftSidebarToggle(); 649 650 // page scroll 651 function bodyFixed() { 652 let windowWidth = $(window).width(); 653 let middle_column = $('.doc-middle-content'); 654 655 if ($('#sticky_doc').length) { 656 if (windowWidth > 576) { 657 let tops = $('#sticky_doc'); 658 let leftOffset = tops.offset().top; 659 660 $(window).on('scroll', function () { 661 let scroll = $(window).scrollTop(); 662 if (scroll >= leftOffset) { 663 tops.addClass('body_fixed'); 664 } else { 665 tops.removeClass('body_fixed'); 666 } 667 }); 668 } 669 } 670 } 671
672 bodyFixed(); 673 674 // TOC area 675 function bodyFixed2() { 676 var windowWidth = $(window).width(); 677 678 if ($('#sticky_doc2').length) { 679 if (windowWidth > 576) { 680 var tops = $('#sticky_doc2'); 681 var topOffset = tops.offset().top; 682 var blogForm = $('.blog_comment_box'); 683 var blogFormTop = blogForm.offset().top - 300; 684 685 $(window).on('scroll', function () { 686 var scrolls = $(window).scrollTop(); 687 if (scrolls >= topOffset) { 688 tops.addClass('stick'); 689 } else { 690 tops.removeClass('stick'); 691 } 692 }); 693 694 var $hackers = $('#hackers'); 695 var hackersPaddingActive = false; 696 697 $('a[href="#hackers"]').click(function () { 698 $hackers.css('padding-top', '100px'); 699 hackersPaddingActive = true; 700 }); 701 702 $(window).on('scroll', function () { 703 if (!hackersPaddingActive) { 704 return; 705 } 706 var hackersOffset = $hackers.offset().top; 707 var scrolls = $(window).scrollTop(); 708 if (scrolls < hackersOffset) { 709 $hackers.css('padding-top', '0px'); 710 hackersPaddingActive = false; 711 } 712 }); 713 } 714 } 715 } 716 717 bodyFixed2(); 718 719 function tocSidebarScrollHeight() { 720 var leftSidebarScrollElement = $('.doc_left_sidebarlist .ezd-scroll'); 721 if (leftSidebarScrollElement.length) { 722 var leftSidebarScrollOffset = leftSidebarScrollElement.position().top; 723 var maxHeightLeftSidebar = `calc(100vh - ${leftSidebarScrollOffset}px)`; 724 leftSidebarScrollElement.css('max-height', maxHeightLeftSidebar); 725 } 726 727 var rightSidebarScrollElement = $('.single-docs .doc_rightsidebar .toc_right'); 728 if (rightSidebarScrollElement.length) { 729 var rightSidebarScrollOffset = rightSidebarScrollElement.position().top; 730 var maxHeightRightSidebar = `calc(100vh - ${rightSidebarScrollOffset + 70}px)`; 731 rightSidebarScrollElement.css('max-height', maxHeightRightSidebar); 732 } 733 } 734 tocSidebarScrollHeight(); 735 736 /* Menu Click js */ 737 if ($('.submenu').length) { 738 $('.submenu > .dropdown-toggle').click(function () { 739 var location = $(this).attr('href'); 740 window.location.href = location; 741 return false; 742 }); 743 } 744 745 /*------------ Cookie functions and color js ------------*/ 746 function createCookie(name, value, days) { 747 var expires = ''; 748 if (days) { 749 var date = new Date(); 750 date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); 751 expires = '; expires=' + date.toUTCString(); 752 } 753 document.cookie = name + '=' + value + expires + '; path=/'; 754 } 755 756 function readCookie(name) { 757 var nameEQ = name + '='; 758 var ca = document.cookie.split(';'); 759 for (var i = 0; i < ca.length; i++) { 760 var c = ca[i]; 761 while (c.charAt(0) == ' ') c = c.substring(1, c.length); 762 if (c.indexOf(nameEQ) == 0) 763 return c.substring(nameEQ.length, c.length); 764 } 765 return null; 766 } 767 768 function eraseCookie(name) { 769 createCookie(name, '', -1); 770 } 771 772 /** 773 * Dark mode switcher. 774 * 775 * The server already paints the correct theme before this runs (the 776 * `body_dark` class is added server-side from the cookie / admin default, 777 * and the first-visit "follow system" case is handled by an inline script 778 * on wp_body_open). So here we only mirror that state onto the toggle UI, 779 * persist the visitor's choice on change, and â while no explicit choice 780 * exists and the default is "follow system" â track live OS changes. 781 * Keeping the body class as the single source of truth removes the old 782 * flicker and the competing active-state logic. 783 */ 784 if (eazydocs_local_object.ezd_dark_switcher === '1') { 785 var $darkSwitch = $('#ezd_dark_switch'); 786 787 function syncDarkUI(isDark) { 788 $darkSwitch.prop('checked', isDark); 789 $('.light-mode').toggleClass('active', !isDark); 790 $('.dark-mode').toggleClass('active', isDark); 791 } 792 793 function setDarkMode(isDark) { 794 $('body').toggleClass('body_dark', isDark); 795 syncDarkUI(isDark); 796 } 797 798 // Reflect whatever the server decided. 799 syncDarkUI($('body').hasClass('body_dark')); 800 801 $darkSwitch.on('change', function () { 802 var isDark = $(this).is(':checked'); 803 setDarkMode(isDark); 804 createCookie('body_dark', isDark ? 'true' : 'false', 999); 805 }); 806 807 // Live-follow the OS preference until the visitor makes a choice. 808 if ( 809 window.matchMedia && 810 readCookie('body_dark') === null && 811 eazydocs_local_object.ezd_dark_default === 'system' 812 ) { 813 var darkScheme = window.matchMedia('(prefers-color-scheme: dark)'); 814 var onSchemeChange = function (e) { 815 if (readCookie('body_dark') !== null) return; // a choice was made meanwhile 816 setDarkMode(e.matches); 817 }; 818 if (darkScheme.addEventListener) { 819 darkScheme.addEventListener('change', onSchemeChange); 820 } else if (darkScheme.addListener) { 821 darkScheme.addListener(onSchemeChange); 822 } 823 } 824 } 825 826 // CONTRIBUTOR SEARCH 827 $('#ezd-contributor-search').on('input', function () { 828 let value = $(this).val().toLowerCase(); 829 830 if (value === '') { 831 // Show all users when input is cleared 832 $('.users_wrap_item').show(); 833 } else { 834 // Filter the users based on input 835 $('.users_wrap_item').filter(function () { 836 $(this).toggle( 837 $(this).text().toLowerCase().indexOf(value) > -1 838 ); 839 }); 840 } 841 }); 842 843 /* 844 * Font size switcher 845 * Check if there are buttons for font size switcher 846 */ 847 848 if ($('#rvfs-controllers button').length) { 849 var $speech = $('#post p, #post ul li:not(.process_tab_shortcode ul li), #post h1, #post h2, #post h3, #post h4, #post h5, #post h6, #post ol li, #post table:not(.basic_table_info,.table-dark), #post table tr td, #post .tab-content'); 850 var originalSizes = {}; 851 852 // Function to check if cookie exists and apply font size 853 function checkFontSize() { 854 var cookieFontSize = readCookie("fontSize"); 855 if (cookieFontSize) { 856 var sizeFactor = parseFloat(cookieFontSize); 857 $speech.each(function () { 858 var tagName = $(this).prop('tagName').toLowerCase(); 859 var originalSize = originalSizes[tagName]; 860 $(this).css('fontSize', originalSize * sizeFactor + 'px'); 861 }); 862 } 863 } 864 // Store the original font sizes 865 $speech.each(function () { 866 var tagName = $(this).prop('tagName').toLowerCase(); 867 if (!originalSizes[tagName]) { 868 originalSizes[tagName] = parseFloat($(this).css('fontSize')); 869 } 870 }); 871 872 // Apply font size when page loads 873 checkFontSize(); 874 875 // Event handler for font size buttons 876 $(document).on('click', '#rvfs-controllers button', function () { 877 var sizeFactor; 878 var currentFactor = parseFloat(readCookie("fontSize")) || 1; 879
880 switch (this.id) { 881 case 'switcher-large': 882 sizeFactor = currentFactor * 1.1; 883 break; 884 case 'switcher-small': 885 sizeFactor = currentFactor / 1.1; 886 break; 887 case 'switcher-default': 888 sizeFactor = 1; 889 break; 890 default: 891 sizeFactor = 1; 892 } 893 894 if (sizeFactor === 1) { 895 $speech.each(function () { 896 var tagName = $(this).prop('tagName').toLowerCase(); 897 $(this).css('fontSize', originalSizes[tagName] + 'px'); 898 }); 899 eraseCookie("fontSize"); 900 } else { 901 $speech.each(function () { 902 var tagName = $(this).prop('tagName').toLowerCase(); 903 var originalSize = originalSizes[tagName]; 904 $(this).css('fontSize', originalSize * sizeFactor + 'px'); 905 }); 906 createCookie("fontSize", sizeFactor, 30); 907 } 908 }); 909 } 910 911 /** 912 * Enables sticky behavior for the sidebar when it reaches the top of the viewport. 913 * This applies to both the left sidebar and mobile right sidebar in smaller screens. 914 */ 915 function ezd_sidebar_enable_sticky() { 916 // One-page layouts manage sidebar stickiness via CSS (.ezd-sticky-lg-top) 917 // and the off-canvas drawer. The JS `.sticky` toggle below applies 918 // `position: fixed`, which detaches the one-page drawer/aside from its 919 // column â so restrict this to the single-doc layout only. 920 var $stickyElements = $('.left-column .doc_left_sidebarlist, .doc_right_mobile_menu .doc_rightsidebar') 921 .filter(function () { 922 return $(this).closest('.onepage_doc_area').length === 0; 923 }); // Select single-doc sidebar elements 924 925 if ($stickyElements.length === 0) return; // Exit if no target elements found 926 927 var stickyOffset = $stickyElements.offset().top; // Get initial top position 928 929 $(window).on('scroll', function () { 930 var scrollTop = $(window).scrollTop(); // Current scroll position 931 932 if (scrollTop >= stickyOffset) { 933 $stickyElements.addClass('sticky'); 934 } else { 935 $stickyElements.removeClass('sticky'); 936 } 937 }); 938 } 939 940 // Initialize the sticky sidebar function 941 ezd_sidebar_enable_sticky(); 942 943 // Google Login script 944 $(".ezd-google-login-btn").on("click", function (e) { 945 e.preventDefault(); 946 var $btn = $(this); 947 var googleUrl = $btn.data("href"); 948 949 // Open popup 950 var width = 600; 951 var height = 600; 952 var left = (screen.width / 2) - (width / 2); 953 var top = (screen.height / 2) - (height / 2); 954 var popup = window.open(googleUrl, "GoogleLogin", "width=" + width + ",height=" + height + ",top=" + top + ",left=" + left); 955 956 // Loading feedback 957 var originalText = $btn.find("span").text(); 958 $btn.find("span").text("Connecting..."); 959 $btn.css("opacity", "0.7"); 960 961 // Reset after 10s 962 setTimeout(function () { 963 $btn.find("span").text(originalText); 964 $btn.css("opacity", "1"); 965 }, 10000); 966 }); 967 968 /** 969 * EazyDocs Accordion Functionality 970 * Sync with updated markup: .ezd-doc-attached-files-accordion 971 * Maintains ARIA states and toggle icons. 972 */ 973 function ezdDocAttachedFile() { 974 const $accordion = $('.ezd-doc-attached-files-accordion'); 975 976 // Set initial collapsed state 977 $accordion.find('.accordion__content').addClass('is-collapsed').hide(); 978 $accordion.find('.accordion__header').attr('aria-expanded', 'false'); 979 $accordion.find('.accordion__toggle i') 980 .removeClass('arrow_carrot-up') 981 .addClass('arrow_carrot-down'); 982 983 // Click on header or toggle button 984 $accordion.on('click', '.accordion__header, .accordion__toggle', function (e) { 985 e.preventDefault(); 986 const $wrapper = $(this).closest('.ezd-doc-attached-files-accordion'); 987 const $content = $wrapper.find('.accordion__content'); 988 const $header = $wrapper.find('.accordion__header'); 989 const $icon = $wrapper.find('.accordion__toggle i'); 990 const expanded = $header.attr('aria-expanded') === 'true'; 991 992 if (expanded) { 993 $content.slideUp(250).addClass('is-collapsed'); 994 $header.attr('aria-expanded', 'false'); 995 $icon.removeClass('arrow_carrot-up').addClass('arrow_carrot-down'); 996 } else { 997 $content.slideDown(250).removeClass('is-collapsed'); 998 $header.attr('aria-expanded', 'true'); 999 $icon.removeClass('arrow_carrot-down').addClass('arrow_carrot-up'); 1000 } 1001 }); 1002 } 1003 // Initialize accordion functionality 1004 ezdDocAttachedFile(); 1005 }); 1006})(jQuery); 1007 1008// Listen for messages from the Google login popup window. 1009// Hardened against cross-origin open redirect / javascript: URL injection: 1010// only same-origin senders and same-origin http(s) redirect targets are honored. 1011window.addEventListener("message", function (event) { 1012 // Reject messages from any origin other than this site. 1013 if (event.origin !== window.location.origin) { 1014 return; 1015 } 1016 1017 var data = event.data; 1018 if (!data || data.type !== "google_login_success" || !data.redirect) { 1019 return; 1020 } 1021 1022 // Resolve and validate the redirect target before navigating. 1023 var target; 1024 try { 1025 target = new URL(data.redirect, window.location.origin); 1026 } catch (err) { 1027 return; 1028 } 1029 1030 if ( 1031 (target.protocol === "http:" || target.protocol === "https:") && 1032 target.origin === window.location.origin 1033 ) { 1034 window.location.href = target.href; 1035 } 1036});
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.