PageSourceSearch

https://www.aucklandnz.com/js/scripts/widget-anchor-link-generator.js?v=20260806.1

js aucklandnz.com collected 2026-09-24 18:49:35 UTC 5,113 bytes, 138 lines download raw bytes

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.