@@ -102,43 +102,88 @@ <h4>Understanding the Metrics:</h4>
102102<!-- JavaScript for sorting and copying -->
103103< script >
104104 function copyToClipboard ( text , element ) {
105- navigator . clipboard . writeText ( text ) . then ( function ( ) {
106- // Show visual feedback
107- if ( element ) {
108- // If using the SVG icon
109- const originalIcon = element . querySelector ( 'svg:first-child' ) ;
110- const checkmark = element . querySelector ( '.checkmark' ) ;
111-
105+ // Check if we're in a secure context (HTTPS)
106+ if ( navigator . clipboard && window . isSecureContext ) {
107+ // Secure context: use the modern Clipboard API
108+ navigator . clipboard . writeText ( text ) . then ( function ( ) {
109+ showCopySuccess ( element ) ;
110+ } ) . catch ( function ( err ) {
111+ console . error ( 'Failed to copy: ' , err ) ;
112+ fallbackCopy ( text , element ) ;
113+ } ) ;
114+ } else {
115+ // Non-secure context: use fallback method
116+ fallbackCopy ( text , element ) ;
117+ }
118+
119+ // Prevent default link behavior
120+ if ( event && event . preventDefault ) {
121+ event . preventDefault ( ) ;
122+ }
123+ return false ;
124+ }
125+
126+ function fallbackCopy ( text , element ) {
127+ // Create temporary input element
128+ const textArea = document . createElement ( "textarea" ) ;
129+ textArea . value = text ;
130+
131+ // Make the textarea out of viewport
132+ textArea . style . position = "fixed" ;
133+ textArea . style . left = "-999999px" ;
134+ textArea . style . top = "-999999px" ;
135+ document . body . appendChild ( textArea ) ;
136+
137+ // Preserve scroll position
138+ const scrollPos = window . pageYOffset || document . documentElement . scrollTop ;
139+
140+ // Select and copy
141+ textArea . focus ( ) ;
142+ textArea . select ( ) ;
143+
144+ let success = false ;
145+ try {
146+ success = document . execCommand ( 'copy' ) ;
147+ } catch ( err ) {
148+ console . error ( 'Fallback: Oops, unable to copy' , err ) ;
149+ }
150+
151+ // Clean up
152+ document . body . removeChild ( textArea ) ;
153+ window . scrollTo ( 0 , scrollPos ) ;
154+
155+ // Show success visual feedback
156+ if ( success ) {
157+ showCopySuccess ( element ) ;
158+ }
159+ }
160+
161+ function showCopySuccess ( element ) {
162+ if ( element ) {
163+ // If using the SVG icon
164+ const originalIcon = element . querySelector ( 'svg:first-child' ) ;
165+ const checkmark = element . querySelector ( '.checkmark' ) ;
166+
167+ if ( originalIcon && checkmark ) {
112168 // Hide original icon and show checkmark
113169 originalIcon . style . visibility = 'hidden' ;
114170 checkmark . style . visibility = 'visible' ;
115-
171+
116172 setTimeout ( function ( ) {
117173 // Restore original icon and hide checkmark
118174 originalIcon . style . visibility = 'visible' ;
119175 checkmark . style . visibility = 'hidden' ;
120176 } , 1500 ) ;
121177 } else {
122- // If using the button
123- const button = event . target ;
124- const originalText = button . textContent ;
125- button . textContent = "Copied!" ;
126- button . style . backgroundColor = "#4CAF50" ;
127-
178+ // Simple text-based fallback if SVG elements aren't found
179+ const originalText = element . textContent || 'Copy' ;
180+ element . textContent = "Copied!" ;
181+
128182 setTimeout ( function ( ) {
129- button . textContent = originalText ;
130- button . style . backgroundColor = "" ;
183+ element . textContent = originalText ;
131184 } , 1500 ) ;
132185 }
133- } , function ( ) {
134- console . error ( 'Failed to copy text to clipboard' ) ;
135- } ) ;
136-
137- // Prevent default link behavior
138- if ( event && event . preventDefault ) {
139- event . preventDefault ( ) ;
140186 }
141- return false ;
142187 }
143188
144189 // Sorting function from channels.html
0 commit comments