1/** 2 * Main. 3 * 4 * @package SimpleShareButtonsAdder 5 */ 6 7/* exported Main */ 8var Main = ( function( $, FB ) { 9 'use strict'; 10 11 // These open a chat prefilled with a prompt in a new tab, not a share popup. 12 var aiSites = [ 'chatgpt', 'claude', 'grok', 'perplexity' ]; 13 14 // These take no prompt in the URL, so it is copied to the clipboard instead. 15 var aiCopySites = [ 'copilot', 'gemini' ]; 16 17 // Shown when the prompt travels over the clipboard, and how long to hold the tab. 18 var AI_COPY_MESSAGE = 'Prompt copied â paste it into the chat'; 19 var AI_COPY_DELAY = 600; 20 21 // How long the copy notice sits above the button, its fade, and its screen gutter. 22 var COPY_NOTICE_HOLD = 1800; 23 var COPY_NOTICE_FADE = 400; 24 var COPY_NOTICE_MARGIN = 8; 25 26 return { 27 /** 28 * Holds data. 29 */ 30 data: {}, 31 32 /** 33 * Boot plugin. 34 * 35 * @param data 36 */ 37 boot: function( data ) { 38 this.data = data; 39 40 $( document ).ready( 41 function() { 42 this.init(); 43 }.bind( this ) 44 ); 45 }, 46 47 /** 48 * Initialize plugin. 49 */ 50 init: function() { 51 this.listen(); 52 this.removeP(); 53 }, 54 55 /** 56 * Listener event. 57 */ 58 listen: function() { 59 var self = this; 60 61 // Upon clicking a share button. 62 $( 'body' ).on( 63 'click', 64 '.ssbp-wrap a', 65 function( event ) { 66 67 // Don't go the the href yet. 68 event.preventDefault(); 69 self.engageShareButton( this ); 70 } 71 ); 72 }, 73 74 /** 75 * Open an AI assistant in a new tab rather than a popup. 76 * 77 * @param url 78 */ 79 openAiTab: function( url ) { 80 var tab = window.open( url, '_blank' ); 81 82 if ( tab ) { 83 tab.opener = null; 84 } 85 }, 86 87 /** 88 * Copy text to the clipboard. 89 * 90 * Fire and forget, so the caller stays inside the click gesture. 91 * 92 * @param text 93 */ 94 copyToClipboard: function( text ) { 95 if ( ! text ) { 96 return; 97 } 98 99 if ( navigator.clipboard && navigator.clipboard.writeText ) { 100 navigator.clipboard.writeText( text ); 101 return; 102 } 103 104 // Fallback for browsers without the async clipboard API. 105 var field = document.createElement( 'textarea' ); 106 field.value = text; 107 field.setAttribute( 'readonly', '' ); 108 field.style.position = 'fixed'; 109 field.style.left = '-9999px'; 110 document.body.appendChild( field ); 111 field.select(); 112 113 try { 114 document.execCommand( 'copy' ); 115 } catch ( error ) { 116 } 117 118 field.parentNode.removeChild( field ); 119 }, 120 121 /** 122 * Show a transient message above a share button. 123 * 124 * The notice is anchored to the top edge of the button so it never covers 125 * the icon, and is nudged back inside the viewport when it would spill out. 126 * 127 * @param event 128 * @param message 129 */ 130 showCopyNotice: function( event, message ) { 131 var button = $( event ).get( 0 ); 132 133 if ( ! button ) { 134 return; 135 } 136 137 // The notice is positioned against the button, so the button has to be 138 // a containing block. Themes leave these links statically positioned. 139 if ( 'static' === window.getComputedStyle( button ).position ) { 140 button.style.position = 'relative'; 141 } 142 143 var notice = document.createElement( 'span' ); 144 notice.innerText = message; 145 notice.className = 'copy-notify ssba-copy-notify'; 146 button.appendChild( notice ); 147 148 // Pull the notice back on screen if centring pushed it past an edge. 149 var bounds = notice.getBoundingClientRect(); 150 var overflowLeft = COPY_NOTICE_MARGIN - bounds.left; 151 var overflowRight = bounds.right - ( document.documentElement.clientWidth - COPY_NOTICE_MARGIN ); 152 153 if ( 0 < overflowLeft ) { 154 notice.style.marginLeft = Math.round( overflowLeft ) + 'px'; 155 } else if ( 0 < overflowRight ) { 156 notice.style.marginLeft = '-' + Math.round( overflowRight ) + 'px'; 157 } 158 159 window.setTimeout( 160 function() { 161 notice.className += ' ssba-copy-notify--hiding'; 162 163 window.setTimeout( 164 function() { 165 if ( notice.parentNode ) { 166 notice.parentNode.removeChild( notice ); 167 } 168 }, 169 COPY_NOTICE_FADE 170 ); 171 }, 172 COPY_NOTICE_HOLD 173 ); 174 }, 175 176 /** 177 * Share button popup 178 * 179 * @param event 180 */ 181 engageShareButton: function( event ) { 182 183 // If it's facebook mobile. 184 if ( 'mobile' === $( event ).data( 'facebook' ) ) { 185 FB.ui( 186 { 187 method: 'share', 188 mobile_iframe: true, 189 href: $( event ).data( 'href' ) 190 }, 191 function( response ) {} 192 ); 193 } else if ( -1 !== aiSites.indexOf( $( event ).data( 'site' ) ) ) { 194 195 // AI assistants open a chat in a new tab rather than a popup. 196 this.openAiTab( $( event ).attr( 'href' ) ); 197 } else if ( -1 !== aiCopySites.indexOf( $( event ).data( 'site' ) ) ) { 198 199 // These take no prompt in the URL, so hand it over the clipboard. 200 this.copyToClipboard( $( event ).data( 'prompt' ) || '' ); 201 this.showCopyNotice( event, AI_COPY_MESSAGE ); 202 203 // Let the notice land before the new tab takes focus. The delay
203stays 204 // well inside the user-activation window, so the tab is not blocked. 205 setTimeout( this.openAiTab.bind( this, $( event ).attr( 'href' ) ), AI_COPY_DELAY ); 206 } else { 207 // These share options don't need to have a popup. 208 if ( 'copy' === $( event ).data( 'site' ) || 'email' === $( event ).data( 'site' ) || 'print' === $( event ).data( 'site' ) || 'pinterest' === $( event ).data( 'site' ) ) { 209 if ( 'copy' === $( event ).data( 'site' ) ) { 210 this.copyToClipboard( $( event ).attr( 'href' ) ); 211 this.showCopyNotice( event, 'URL Copied!' ); 212 } else { 213 // Just redirect. 214 window.location.href = $( event ).attr( 'href' ); 215 } 216 } else { 217 218 // Prepare popup window. 219 var width = 575, 220 height = 520, 221 left = ( $( window ).width() - width ) / 2, 222 top = ( $( window ).height() - height ) / 2, 223 opts = 'status=1' + 224 ',width=' + width + 225 ',height=' + height + 226 ',top=' + top + 227 ',left=' + left; 228 229 // Open the share url in a smaller window. 230 window.open( $( event ).attr( 'href' ), 'share', opts ); 231 } 232 } 233 }, 234 235 /** 236 * Remove generated p tag from facebook save button. 237 */ 238 removeP: function() { 239 } 240 }; 241} )( window.jQuery, window.FB );
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.