PageSourceSearch

https://hapifhir.io/hapi-fhir/resources/cdr-web-resources/js/help-clipboard.js

js hapifhir.io collected 2026-10-02 09:50:08 UTC 4,905 bytes, 143 lines download raw bytes

1/**
2 * Shared clipboard functionality for documentation section links.
3 * Used by admin-web docs.js and cdr-site right-toc-handler.js files.
4 */
5window.HelpClipboard = (function() {
6    'use strict';
7
8    var DEFAULT_NAVBAR_HEIGHT = 90;
9
10    /**
11     * Copy text to clipboard with fallback for older browsers.
12     * @param {string} text - The text to copy
13     * @returns {Promise<boolean>} - Resolves to true if copy succeeded, false otherwise
14     */
15    function copyToClipboard(text) {
16        // Check for modern clipboard API (requires secure context)
17        if (navigator.clipboard && navigator.clipboard.writeText) {
18            return navigator.clipboard.writeText(text)
19                .then(function() { return true; })
20                .catch(function() { return false; });
21        }
22
23        // Fallback for older browsers or non-HTTPS contexts
24        // Note: document.execCommand('copy') is deprecated but needed for legacy browser support
25        return new Promise(function(resolve) {
26            try {
27                var textarea = document.createElement('textarea');
28                textarea.value = text;
29                textarea.style.position = 'fixed';
30                textarea.style.left = '-9999px';
31                textarea.style.top = '0';
32                document.body.appendChild(textarea);
33                textarea.focus();
34                textarea.select();
35                var success = document.execCommand('copy');
36                document.body.removeChild(textarea);
37                resolve(success);
38            } catch (err) {
39                resolve(false);
40            }
41        });
42    }
43
44    /**
45     * Show a floating notification near the clicked element.
46     * @param {HTMLElement} element - The element that was clicked
47     * @param {string} message - The message to display
48     * @param {boolean} isError - Whether this is an error notification
49     */
50    function showNotification(element, message, isError) {
51        var existing = document.querySelector('.helpSectionLink-copied');
52        if (existing) {
53            existing.remove();
54        }
55
56        var notification = document.createElement('div');
57        notification.className = 'helpSectionLink-copied';
58        notification.textContent = message;
59        if (isError) {
60            notification.style.backgroundColor = '#933';
61        }
62
63        var rect = element.getBoundingClientRect();
64
65        // For subsection links (helpSectionLinkTwo), position below; otherwise above
66        var isSubsectionLink = element.classList.contains('helpSectionLinkTwo');
67        var navBarHeight = DEFAULT_NAVBAR_HEIGHT;
68        var header = document.querySelector('.navbar-fixed-top, .fixed-top, header.fixed');
69        if (header) {
70            navBarHeight = header.offsetHeight + 10;
71        }
72
73        var top;
74        if (isSubsectionLink) {
75            top = rect.bottom + 5;
76        } else {
77            top = rect.top - 30;
78        }
79        // Ensure notification stays below nav bar
80        top = Math.max(top, navBarHeight);
81
82        notification.style.position = 'fixed';
83        notification.style.top = top + 'px';
84        notification.style.right = '20px';
85
86        document.body.appendChild(notification);
87
88        requestAnimationFrame(function() {
89            notification.classList.add('show');
90        });
91
92        setTimeout(function() {
93            notification.classList.remove('show');
94            setTimeout(function() {
95                notification.remove();
96            }, 300);
97        }, 1500);
98    }
99
100    /**
101     * Setup click handler for help section links to copy URL to clipboard.
102     * @param {HTMLElement|string} container - The container element or selector
103     */
104    function setupSectionLinkClipboard(container) {
105        var docsContent = typeof container === 'string'
106            ? document.querySelector(container)
107            : container;
108
109        if (!docsContent) {
110            return;
111        }
112
113        if (docsContent.hasAttribute('data-clipboard-initialized')) {
114            return;
115        }
116        docsContent.setAttribute('data-clipboard-initialized', 'true');
117
118        docsContent.addEventListener('click', function(event) {
119            var link = event.target.closest('.helpSectionLink, .helpSectionLinkTwo');
120            if (!link) {
121                return;
122            }
123
124            var url = link.href;
125            if (!url) {
126                return;
127            }
128            copyToClipboard(url).then(function(success) {
129                if (success) {
130                    showNotification(link, 'Link copied!', false);
131                } else {
132                    showNotification(link, 'Copy failed', true);
133                }
134            });
135        });
136    }
137
138    return {
139        copyToClipboard: copyToClipboard,
140        showNotification: showNotification,
141        setupSectionLinkClipboard: setupSectionLinkClipboard
142    };
143})();

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.