1// Encodes string ID to a compact base36 hash. 2function encryptId(id) { 3 let hash = 0; 4 for (let i = 0; i < id.length; i++) { 5 const char = id.charCodeAt(i); 6 hash = ((hash << 5) - hash) + char; 7 hash = hash & hash; // Ensure 32-bit integer. 8 } 9 hash = Math.abs(hash); // Ensure positive. 10 return hash.toString(36); // Convert to base36. 11} 12 13// Copies text to clipboard and updates button status. 14function copyToClipboard(text, button) { 15 // Use hidden textarea for copying. 16 const textarea = document.createElement('textarea'); 17 textarea.value = text; 18 textarea.setAttribute('readonly', ''); 19 textarea.style.position = 'absolute'; 20 textarea.style.left = '-9999px'; 21 document.body.appendChild(textarea); 22 23 textarea.select(); // Select text. 24 try { 25 const successful = document.execCommand('copy'); // Attempt copy. 26 button.textContent = successful ? 'Copied!' : 'Failed to copy'; // Update button text. 27 } catch (err) { 28 console.error('Clipboard copy failed:', err); 29 button.textContent = 'Failed to copy'; // Update on error. 30 } finally { 31 document.body.removeChild(textarea); // Remove textarea. 32 // Reset button text after 5s. 33 setTimeout(() => { 34 if (button && (button.textContent === 'Copied!' || button.textContent === 'Failed to copy')) { 35 button.textContent = 'Copy Link'; 36 } 37 }, 5000); 38 } 39} 40 41// Scrolls the page smoothly to the target element. 42function scrollToElement($element) { 43 if ($element && $element.length > 0 && $element.is(':visible')) { 44 const offsetTop = $element.offset().top; 45 $('html, body').animate({ 46 scrollTop: offsetTop 47 }, 500); // 500ms animation duration 48 } 49} 50 51// Checks the current URL hash and scrolls to the corresponding element if found. 52function checkHashAndScroll() { 53 const hash = window.location.hash; 54 // Check if hash exists and looks like our generated IDs (starts with #) 55 if (hash && hash.length > 1) { 56 try { 57 // Find the element by ID (hash minus the #) 58 const $targetElement = $('#' + hash.substring(1)); 59 scrollToElement($targetElement); 60 } catch (e) { 61 // Handle potential errors with invalid selectors if hash is weird 62 console.warn("Could not scroll to hash:", hash, e); 63 } 64 } 65} 66 67// Generates unique anchor links and sets up copy buttons for widgets. 68function generateWidgetAnchorLinks() { 69 // Group widgets by type found via 'data-name'. 70 const widgetGroups = {}; 71 $('.widget-anchor').each(function () { 72 const widgetType = $(this).attr('data-name'); 73 if (!widgetType) return; 74 if (!widgetGroups[widgetType]) widgetGroups[widgetType] = []; 75 widgetGroups[widgetType].push($(this)); 76 }); 77 78 // Process each widget group. 79 $.each(widgetGroups, function (widgetType, widgets) { 80 // Process each widget. 81 $.each(widgets, function (index, $widget) { 82 if ($widget.attr('data-anchor-processed') === 'true') return; // Skip if done. 83 84 // Create unique ID (e.g., "widgetType-1"). 85 const uniqueId = widgetType + '-' + (index + 1); 86 87 // Encode the ID. 88 const encryptedId = encryptId(uniqueId); 89 90 // Set widget ID. 91 $widget.attr('id', encryptedId); 92 93 // Find copy button. 94 const $copyLinkButton = $widget.find('.copy-link-button'); 95 96 // If button exists, configure it. 97 if ($copyLinkButton.length) { 98 // Build full URL with anchor. 99 const fullUrl = (window.customAppSettings?.currentPageUrl || window.location.origin + window.location.pathname) + '#' + encryptedId; 100 101 // Set button click handler. 102 $copyLinkButton.off('click').on('click', function () { 103 copyToClipboard(fullUrl, this); 104 }); 105 106 // Show button and mark as processed. 107 $copyLinkButton.removeClass('hidden'); 108 $widget.attr('data-anchor-processed', 'true'); 109 } 110 }); 111 });
112} 113 114// Run script when the page is ready. 115$(document).ready(function () { 116 generateWidgetAnchorLinks(); 117 checkHashAndScroll(); 118 119 // Run after full page load with delay. 120 $(window).on('load', function () { 121 setTimeout(generateWidgetAnchorLinks, 1000); 122 setTimeout(checkHashAndScroll, 1100); 123 124 setTimeout(generateWidgetAnchorLinks, 5000); 125 setTimeout(generateWidgetAnchorLinks, 10000); 126 }); 127 128 // Run on Kentico page builder load event. 129 $(document).on('pageBuilderLoaded', function () { 130 generateWidgetAnchorLinks(); 131 setTimeout(checkHashAndScroll, 100); 132 }); 133 134 // IMPORTANT: Listen for hash changes after initial load 135 $(window).on('hashchange', function () { 136 checkHashAndScroll(); 137 }); 138});
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.