PageSourceSearch

https://www.tobaccodocuments.org/wp-content/plugins/ai-share-summarize/assets/js/ai-share-summarize.js

js tobaccodocuments.org collected 2026-10-02 05:04:50 UTC 21,751 bytes, 557 lines download raw bytes

1/**
2 * AI Share & Summarize Plugin - JavaScript
3 * Advanced functionality for tooltips, hover effects, responsive behavior, click tracking
4 * and the visitor-facing AI summary generation button (v2.0.0).
5 * Version: 2.0.1
6 */
7
8/**
9 * Optional dark-background detection.
10 *
11 * Active only when the admin selects the "auto" dark-mode adaptation mode.
12 * Walks up from each share-buttons container looking for the first opaque
13 * background, computes its WCAG relative luminance, and toggles the
14 * .is-on-dark class accordingly. Runs once on DOMContentLoaded and again
15 * when the OS-level prefers-color-scheme flips (themes that wire dark mode
16 * to that media query change their background at that moment, so we need
17 * to re-measure). No MutationObserver is installed to keep runtime cost
18 * negligible.
19 */
20(function() {
21    if ( ! window.ayudawpAissL10n || 'auto' !== window.ayudawpAissL10n.darkAdaptMode ) {
22        return;
23    }
24
25    function detect() {
26        var nodes = document.querySelectorAll(
27            '.ayudawp-share-buttons:not(.dark):not(.custom), .ayudawp-aiss-summary'
28        );
29        for ( var i = 0; i < nodes.length; i++ ) {
30            var node = nodes[ i ].parentElement;
31            var bg   = '';
32            while ( node && node !== document.documentElement ) {
33                var computed = window.getComputedStyle( node ).backgroundColor;
34                if ( computed && 'rgba(0, 0, 0, 0)' !== computed && 'transparent' !== computed ) {
35                    bg = computed;
36                    break;
37                }
38                node = node.parentElement;
39            }
40            if ( ! bg ) {
41                continue;
42            }
43            var match = bg.match( /\d+(\.\d+)?/g );
44            if ( ! match || match.length < 3 ) {
45                continue;
46            }
47            var r = parseFloat( match[ 0 ] ) / 255;
48            var g = parseFloat( match[ 1 ] ) / 255;
49            var b = parseFloat( match[ 2 ] ) / 255;
50            var lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
51            nodes[ i ].classList.toggle( 'is-on-dark', lum < 0.5 );
52        }
53    }
54
55    if ( 'loading' === document.readyState ) {
56        document.addEventListener( 'DOMContentLoaded', detect );
57    } else {
58        detect();
59    }
60
61    if ( window.matchMedia ) {
62        var mq = window.matchMedia( '(prefers-color-scheme: dark)' );
63        if ( mq.addEventListener ) {
64            mq.addEventListener( 'change', detect );
65        } else if ( mq.addListener ) {
66            mq.addListener( detect );
67        }
68    }
69})();
70
71jQuery(document).ready(function($) {
72
73    /**
74     * Platforms that require copy-to-clipboard behavior
75     * These platforms don't support direct URL parameters for prompts
76     */
77    var copyPromptPlatforms = ['gemini', 'deepseek', 'copilot', 'qwen', 'meta_ai'];
78
79    /**
80     * Platform base URLs for copy-to-clipboard platforms
81     */
82    var platformBaseUrls = {
83        'gemini': 'https://gemini.google.com/app',
84        'deepseek': 'https://chat.deepseek.com/',
85        'copilot': 'https://copilot.microsoft.com/',
86        'qwen': 'https://chat.qwen.ai/',
87        'meta_ai': 'https://www.meta.ai/'
88    };
89
90    /**
91     * Universal tooltip system for all buttons
92     */
93    function attachUniversalTooltips() {
94        $('.ayudawp-tooltip').remove();
95
96        $('.ayudawp-share-btn').each(function() {
97            var $button = $(this);
98            var platform = detectPlatform($button);
99            var tooltipText = '';
100
101            if (copyPromptPlatforms.indexOf(platform) !== -1) {
102                var isIconsOnly = $button.closest('.ayudawp-share-buttons').hasClass('icons-only');
103                var platformName = '';
104
105                if (platform === 'gemini') {
106                    platformName = 'Gemini';
107                } else if (platform === 'deepseek') {
108                    platformName = 'DeepSeek';
109                } else if (platform === 'copilot') {
110                    platformName = 'Copilot';
111                } else if (platform === 'qwen') {
112                    platformName = 'Qwen';
113                } else if (platform === 'meta_ai' || platform === 'meta-ai') {
114                    platformName = 'Meta AI';
115                }
116
117                if (isIconsOnly) {
118                    tooltipText = (window.ayudawpAissL10n && window.ayudawpAissL10n[platform + 'TooltipShort']) ||
119                                  (window.ayudawpAissL10n && window.ayudawpAissL10n.copyPromptShort) ||
120                                  'Copy prompt & open';
121                } else {
122                    tooltipText = (window.ayudawpAissL10n && window.ayudawpAissL10n[platform + 'TooltipLong']) ||
123                                  (window.ayudawpAissL10n && window.ayudawpAissL10n.copyPromptLong) ||
124                                  ('Copy prompt and open ' + platformName);
125                }
126            } else if (platform && window.ayudawpAissL10n && window.ayudawpAissL10n.platformNames) {
127                tooltipText = window.ayudawpAissL10n.platformNames[platform] || platform;
128            }
129
130            if (tooltipText) {
131                var $tooltip = $('<div class="ayudawp-tooltip">' + tooltipText + '</div>');
132                $('body').append($tooltip);
133
134                $button.on('mouseenter', function() {
135                    var offset = $button.offset();
136                    var buttonWidth = $button.outerWidth();
137                    var tooltipWidth = $tooltip.outerWidth();
138                    var tooltipHeight = $tooltip.outerHeight();
139
140                    $tooltip.css({
141                        top: offset.top - tooltipHeight - 8,
142                        left: offset.left + (buttonWidth / 2) - (tooltipWidth / 2)
143                    }).addClass('active');
144                });
145
146                $button.on('mouseleave', function() {
147                    $tooltip.removeClass('active');
148                });
149
150                $button.data('tooltip', $tooltip);
151            }
152        });
153    }
154
155    /**
156     * Detect platform from button data attribute or class
157     *
158     * @param {jQuery} $button Button element
159     * @return {string} Platform identifier
160     */
161    function detectPlatform($button) {
162        var platform = $button.attr('data-platform');
163
164        if (!platform) {
165            var classes = $button.attr('class').split(' ');
166            var platforms = ['twitter', 'linkedin', 'facebook', 'telegram', 'whatsapp', 'email', 'raindrop',
167                           'reddit', 'bluesky', 'line', 'mastodon', 'threads', 'pinterest',
168                           'claude', 'chatgpt', 'google-ai', 'gemini',
169                           'grok', 'perplexity', 'deepseek', 'mistral', 'copilot', 'qwen', 'meta-ai'];
170            for (var i = 0; i < platforms.length; i++) {
171                if (classes.indexOf(platforms[i]) !== -1) {
172                    platform = platforms[i];
173                    break;
174                }
175            }
176        }
177
178        return platform || '';
179    }
180
181    /**
182     * Enhanced click handling with proper platform detection and analytics tracking
183     */
184    function attachClickHandlers() {
185        $(document).off('click.ayudawp').on('click.ayudawp', '.ayudawp-share-btn', function(e) {
186            e.preventDefault();
187            e.stopPropagation();
188
189            var $button = $(this);
190            var url = $button.attr('data-url') || $button.attr('href');
191            var platform = detectPlatform($button);
192
193            if (!url) {
194                return false;
195            }
196
197            // Track the click (fire and forget).
198            trackClick(platform, $button);
199
200            // Special handling for platforms that require copy-to-clipboard.
201            if (copyPromptPlatforms.indexOf(platform) !== -1) {
202                handleCopyPromptClick(url, platform);
203                return false;
204            }
205
206            // Social media popups.
207            if (['twitter', 'linkedin', 'facebook', 'telegram', 'reddit', 'bluesky'].indexOf(platform) !== -1) {
208                var width = 600, height = 400;
209                if (platform === 'reddit') {
210                    width = 900;
211                    height = 600;
212                }
213                var left = (screen.width / 2) - (width / 2);
214                var top = (screen.height / 2) - (height / 2);
215
216                window.open(url, 'share', 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',scrollbars=yes,resizable=yes');
217                return false;
218            }
219
220            // Default behavior for all other buttons.
221            window.open(url, '_blank', 'noopener,noreferrer');
222            return false;
223        });
224    }
225
226    /**
227     * Track button click via REST API (fire and forget)
228     * No nonce needed: public non-destructive endpoint, compatible with page caching.
229     *
230     * @param {string} platform Platform identifier
231     * @param {jQuery} $button The clicked button element
232     */
233    function trackClick(platform, $button) {
234        if (!window.ayudawpAissL10n || !window.ayudawpAissL10n.trackUrl) {
235            return;
236        }
237
238        // Get post ID from the closest container or page body.
239        var postId = $button.closest('[data-post-id]').attr('data-post-id') ||
240                     $('article[id^="post-"]').first().attr('id');
241
242        if (postId && typeof postId === 'string') {
243            postId = postId.replace('post-', '');
244        }
245
246        // Normalize platform names (google-ai -> google_ai).
247        var normalizedPlatform = platform ? platform.replace(/-/g, '_') : '';
248
249        if (!normalizedPlatform) {
250            return;
251        }
252
253        // Fire and forget - don't block user interaction.
254        try {
255            var xhr = new XMLHttpRequest();
256            xhr.open('POST', window.ayudawpAissL10n.trackUrl, true);
257            xhr.setRequestHeader('Content-Type', 'application/json');
258            xhr.send(JSON.stringify({
259                platform: normalizedPlatform,
260                post_id: parseInt(postId, 10) || 0
261            }));
262        } catch (err) {
263            // Silently fail - tracking should never break user experience.
264        }
265    }
266
267    /**
268     * Handle click for platforms that require copy-to-clipboard behavior
269     * Works for Gemini, DeepSeek, and Copilot
270     *
271     * @param {string} url - The URL containing the prompt parameter
272     * @param {string} platform - The platform identifier
273     */
274    function handleCopyPromptClick(url, platform) {
275        try {
276            var urlParts = url.split('?');
277            var queryString = urlParts.length > 1 ? urlParts[1] : '';
278            var urlParams = new URLSearchParams(queryString);
279            var prompt = urlParams.get('prompt') || urlParams.get('q') || '';
280            var baseUrl = platformBaseUrls[platform] || urlParts[0];
281
282            if (prompt) {
283                copyToClipboard(decodeURIComponent(prompt)).then(function() {
284                    showNotification(window.ayudawpAissL10n.promptCopied || 'Prompt copied!', 'success');
285                    setTimeout(function() {
286                        window.open(baseUrl, '_blank', 'noopener,noreferrer');
287                    }, 500);
288                }).catch(function() {
289                    window.open(baseUrl, '_blank', 'noopener,noreferrer');
290                });
291            } else {
292                window.open(baseUrl, '_blank', 'noopener,noreferrer');
293            }
294        } catch (error) {
295            var fallbackUrl = platformBaseUrls[platform] || url.split('?')[0];
296            window.open(fallbackUrl, '_blank', 'noopener,noreferrer');
297        }
298    }
299
300    /**
301     * Copy to clipboard function
302     */
303    function copyToClipboard(text) {
304        if (navigator.clipboard && window.isSecureContext) {
305            return navigator.clipboard.writeText(text);
306        } else {
307            var textArea = document.createElement('textarea');
308            textArea.value = text;
309            textArea.style.position = 'fixed';
310            textArea.style.left = '-999999px';
311            textArea.style.top = '-999999px';
312            document.body.appendChild(textArea);
313            textArea.focus();
314            textArea.select();
315
316            return new Promise(function(resolve, reject) {
317                try {
318                    document.execCommand('copy') ? resolve() : reject();
319                } catch (err) {
320                    reject(err);
321                } finally {
322                    textArea.remove();
323                }
324            });
325        }
326    }
327
328    /**
329     * Show notification
330     */
331    function showNotification(message, type) {
332        if (document.querySelector('.ayudawp-notification')) return;
333
334        var notification = document.createElement('div');
335        notification.className = 'ayudawp-notification ayudawp-notification-' + type;
336        notification.textContent = message;
337        notification.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#333;color:white;padding:16px 24px;border-radius:8px;font-size:14px;font-weight:500;z-index:9999;box-shadow:0 8px 32px rgba(0,0,0,0.3);opacity:0;transition:all 0.4s ease;border:1px solid #555;';
338
339        document.body.appendChild(notification);
340
341        setTimeout(function() {
342            notification.style.opacity = '1';
343        }, 50);
344
345        setTimeout(function() {
346            notification.style.opacity = '0';
347            notification.style.transform = 'translate(-50%,-50%) scale(0.9)';
348            setTimeout(function() {
349                if (notification.parentNode) {
350                    notification.parentNode.removeChild(notification);
351                }
352            }, 400);
353        }, 2500);
354    }
355
356    /**
357     * Improve accessibility with keyboard navigation
358     */
359    function enhanceKeyboardNavigation() {
360        $(document).on('keydown', '.ayudawp-share-btn', function(e) {
361            if (e.keyCode === 13 || e.keyCode === 32) {
362                e.preventDefault();
363                $(this).click();
364            }
365        });
366    }
367
368    /**
369     * Enhanced responsive behavior for icons-only mode
370     */
371    function handleResponsiveIconsOnly() {
372        var $iconsOnlyContainers = $('.ayudawp-share-buttons.icons-only .ayudawp-buttons-container');
373
374        $iconsOnlyContainers.each(function() {
375            var $container = $(this);
376            var $buttons = $container.find('.ayudawp-share-btn');
377            var containerWidth = $container.width();
378            var buttonWidth = 44;
379            var gap = 10;
380            var buttonsPerRow = Math.floor((containerWidth + gap) / (buttonWidth + gap));
381
382            if ($(window).width() <= 480 && buttonsPerRow < $buttons.length) {
383                $container.css({
384                    'display': 'grid',
385                    'grid-template-columns': 'repeat(auto-fit, 44px)',
386                    'gap': '8px',
387                    'max-width': '300px',
388                    'margin': '0 auto'
389                });
390            } else {
391                $container.css({
392                    'display': 'flex',
393                    'grid-template-columns': '',
394                    'max-width': '',
395                    'margin': ''
396                });
397            }
398        });
399    }
400
401    /**
402     * Smooth hover animation for all buttons
403     */
404    function attachHoverEffects() {
405        $(document).on('mouseenter', '.ayudawp-share-btn', function() {
406            var $this = $(this);
407            var platform = $this.attr('data-platform');
408            if (copyPromptPlatforms.indexOf(platform) === -1) {
409                $this.css('transform', 'translateY(-2px)');
410            }
411        });
412
413        $(document).on('mouseleave', '.ayudawp-share-btn', function() {
414            var $this = $(this);
415            $this.css('transform', 'translateY(0)');
416        });
417    }
418
419    /**
420     * Initialize all features
421     */
422    function initializeAllFeatures() {
423        attachUniversalTooltips();
424        attachClickHandlers();
425        enhanceKeyboardNavigation();
426        handleResponsiveIconsOnly();
427        attachHoverEffects();
428    }
429
430    // Initialize on document ready.
431    initializeAllFeatures();
432
433    // Handle window resize for responsive behavior.
434    $(window).on('resize', function() {
435        handleResponsiveIconsOnly();
436    });
437
438    // Re-initialize when new content is added dynamically.
439    $(document).on('DOMNodeInserted', function(e) {
440        if ($(e.target).hasClass('ayudawp-share-buttons') || $(e.target).find('.ayudawp-share-buttons').length > 0) {
441            setTimeout(function() {
442                attachUniversalTooltips();
443                handleResponsiveIconsOnly();
444            }, 100);
445        }
446    });
447
448});
449
450/**
451 * AI Summary frontend generation button (v2.0.0)
452 *
453 * Vanilla JS — runs outside jQuery so the handler survives aggressive
454 * defer/async optimizations from caching/perf plugins (e.g. wpo-tweaks,
455 * WP Rocket, LiteSpeed). Event delegation on document so it picks up
456 * placeholders rendered by shortcodes, page builders or AJAX-loaded
457 * content. Uses fetch() with credentials so it works for logged-in and
458 * anonymous visitors alike.
459 *
460 * @since 2.0.0
461 */
462(function () {
463    'use strict';
464
465    function ayudawpAissInitSummaryButton() {
466        if (document.body && document.body.__aissSummaryBtnBound) {
467            return;
468        }
469        if (document.body) {
470            document.body.__aissSummaryBtnBound = true;
471        }
472
473        document.addEventListener('click', function (e) {
474            var btn = e.target && e.target.closest ? e.target.closest('.ayudawp-aiss-summary-generate-btn') : null;
475            if (!btn) {
476                return;
477            }
478            e.preventDefault();
479
480            var placeholder = btn.closest('.ayudawp-aiss-summary--placeholder');
481            if (!placeholder) {
482                return;
483            }
484            var postId = parseInt(placeholder.getAttribute('data-post-id'), 10);
485            if (!postId || btn.disabled) {
486                return;
487            }
488
489            var L = window.ayudawpAissL10n || {};
490            var labelEl = btn.querySelector('.ayudawp-aiss-summary-label');
491            var originalLabel = labelEl ? labelEl.textContent : '';
492
493            btn.disabled = true;
494            if (labelEl) {
495                labelEl.textContent = L.summaryGenerating || 'Generating…';
496            }
497
498            var endpoint = L.publicGenerateUrl || (window.location.origin + '/wp-json/aiss/v1/summary/generate');
499
500            fetch(endpoint, {
501                method:      'POST',
502                credentials: 'same-origin',
503                headers:     { 'Content-Type': 'application/json', 'Accept': 'application/json' },
504                body:        JSON.stringify({ post_id: postId })
505            }).then(function (response) {
506                return response.json().then(function (data) {
507                    return { ok: response.ok, data: data };
508                });
509            }).then(function (result) {
510                if (result.ok && result.data && result.data.html) {
511                    var wrapper = document.createElement('div');
512                    wrapper.innerHTML = result.data.html.trim();
513                    // The server response includes an inline <style> for the
514                    // critical CSS plus the actual <aside> summary block.
515                    // Picking firstChild would replace the placeholder with
516                    // the invisible <style>, making the box disappear. Pick
517                    // the real summary node instead (the .ayudawp-aiss-summary
518                    // that is NOT the placeholder we're replacing).
519                    var node = wrapper.querySelector('.ayudawp-aiss-summary:not(.ayudawp-aiss-summary--placeholder)');
520                    if (node && placeholder.parentNode) {
521                        placeholder.parentNode.replaceChild(node, placeholder);
522                        // A11y: open the freshly generated summary and move focus
523                        // to its toggle so screen readers announce the new content
524                        // that replaced the button the visitor just activated.
525                        var details = node.querySelector('details.ayudawp-aiss-summary-details');
526                        if (details) { details.open = true; }
527                        var toggle = node.querySelector('summary.ayudawp-aiss-summary-toggle') || node.querySelector('summary');
528                        if (toggle && typeof toggle.focus === 'function') {
529                            try { toggle.focus(); } catch (e) {}
530                        }
531                    }
532                    return;
533                }
534                var msg = (result.data && result.data.message) ? result.data.message : (L.summaryGenerateError || 'Generation failed. Try again.');
535                if (labelEl) { labelEl.textContent = msg; }
536                window.setTimeout(function () {
537                    btn.disabled = false;
538                    if (labelEl) { labelEl.textContent = originalLabel; }
539                }, 3000);
540            }).catch(function () {
541                if (labelEl) { labelEl.textContent = L.summaryGenerateError || 'Generation failed. Try again.'; }
542                window.setTimeout(function () {
543                    btn.disabled = false;
544                    if (labelEl) { labelEl.textContent = originalLabel; }
545                }, 3000);
546            });
547        }, false);
548    }
549
550    // Bind whenever the DOM is ready — works whether this script runs
551    // before or after DOMContentLoaded (covers defer/async loading).
552    if (document.readyState === 'loading') {
553        document.addEventListener('DOMContentLoaded', ayudawpAissInitSummaryButton);
554    } else {
555        ayudawpAissInitSummaryButton();
556    }
557})();

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.