diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e33f698 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,45 @@ +# Pre-commit hooks configuration +# Documentation: https://pre-commit.com/ +# +# Installation: +# pip install pre-commit +# pre-commit install +# +# Usage: +# pre-commit run --all-files # Run on all files +# pre-commit run # Run specific hook +# +# For queries and documentation, visit: https://pre-commit.com/hooks.html + +repos: + # ---------------------------------- + # 1. Universal Git / file hygiene + # ---------------------------------- + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-merge-conflict + - id: check-added-large-files + - id: mixed-line-ending + args: ['--fix=lf'] + - id: check-yaml + - id: check-json + - id: check-toml + + # ---------------------------------- + # 3. Security (language-agnostic) + # ---------------------------------- + - repo: https://github.com/Yelp/detect-secrets + rev: v1.5.0 + hooks: + - id: detect-secrets + # Note: Maintainers should run `detect-secrets scan > .secrets.baseline` and uncomment the args below: + # args: ['--baseline', '.secrets.baseline'] + +# ============================================================================== +# IMPORTANT: +# Pre-commit runs locally every time you commit; only simple logic should be included here. +# Heavy operations should be handled by CI/CD pipelines (GitHub Actions, etc.) +# ============================================================================== diff --git a/src/social-share-button.js b/src/social-share-button.js index 5f05397..9353260 100644 --- a/src/social-share-button.js +++ b/src/social-share-button.js @@ -5,39 +5,35 @@ */ /** Analytics event schema version. Increment when the payload shape changes. */ -const ANALYTICS_SCHEMA_VERSION = "1.0"; +const ANALYTICS_SCHEMA_VERSION = '1.0'; class SocialShareButton { constructor(options = {}) { this.options = { - url: - options.url || - (typeof window !== "undefined" ? window.location.href : ""), - title: - options.title || - (typeof document !== "undefined" ? document.title : ""), - description: options.description || "", + url: options.url || (typeof window !== 'undefined' ? window.location.href : ''), + title: options.title || (typeof document !== 'undefined' ? document.title : ''), + description: options.description || '', hashtags: options.hashtags || [], - via: options.via || "", + via: options.via || '', platforms: options.platforms || [ - "whatsapp", - "facebook", - "twitter", - "linkedin", - "telegram", - "reddit", + 'whatsapp', + 'facebook', + 'twitter', + 'linkedin', + 'telegram', + 'reddit', ], - theme: options.theme || "dark", - buttonText: options.buttonText || "Share", - customClass: options.customClass || "", - buttonColor: options.buttonColor || "", - buttonHoverColor: options.buttonHoverColor || "", + theme: options.theme || 'dark', + buttonText: options.buttonText || 'Share', + customClass: options.customClass || '', + buttonColor: options.buttonColor || '', + buttonHoverColor: options.buttonHoverColor || '', onShare: options.onShare || null, onCopy: options.onCopy || null, container: options.container || null, showButton: options.showButton !== false, - buttonStyle: options.buttonStyle || "default", - modalPosition: options.modalPosition || "center", + buttonStyle: options.buttonStyle || 'default', + modalPosition: options.modalPosition || 'center', // Analytics — the library emits events but never collects or sends data itself. // Website owners wire up their own analytics tools via these options. analytics: options.analytics !== false, // set to false to disable all event emission @@ -55,14 +51,13 @@ class SocialShareButton { this.handleKeydown = null; this.listeners = []; // Central registry for all event listeners - this.openTimeout = null; // Track setTimeout for openModal animation + this.openTimeout = null; // Track setTimeout for openModal animation this.closeTimeout = null; // Track setTimeout for closeModal animation this.feedbackTimeout = null; // Track setTimeout for copy feedback reset this.ownsBodyLock = false; // Track if this instance owns the body overflow lock this.eventsAttached = false; // Guard against multiple attachEvents() calls this.isDestroyed = false; // Track if instance has been destroyed (prevents async callbacks) - if (this.options.container) { this.init(); } @@ -78,9 +73,9 @@ class SocialShareButton { } createButton() { - const button = document.createElement("button"); + const button = document.createElement('button'); button.className = `social-share-btn ${this.options.buttonStyle} ${this.options.customClass}`; - button.setAttribute("aria-label", "Share"); + button.setAttribute('aria-label', 'Share'); button.innerHTML = ` @@ -91,7 +86,7 @@ class SocialShareButton { this.button = button; if (this.options.container) { const container = - typeof this.options.container === "string" + typeof this.options.container === 'string' ? document.querySelector(this.options.container) : this.options.container; @@ -102,9 +97,9 @@ class SocialShareButton { } createModal() { - const modal = document.createElement("div"); + const modal = document.createElement('div'); modal.className = `social-share-modal-overlay ${this.options.theme}`; - modal.style.display = "none"; + modal.style.display = 'none'; modal.innerHTML = `
`; - const urlInputContainer = modal.querySelector(".social-share-link-input"); - const urlInput = document.createElement("input"); - urlInput.type = "text"; + const urlInputContainer = modal.querySelector('.social-share-link-input'); + const urlInput = document.createElement('input'); + urlInput.type = 'text'; urlInput.value = this.options.url; urlInput.readOnly = true; - urlInput.setAttribute("aria-label", "URL to share"); + urlInput.setAttribute('aria-label', 'URL to share'); urlInputContainer.appendChild(urlInput); this.modal = modal; @@ -137,38 +132,38 @@ class SocialShareButton { getPlatformsHTML() { const platforms = { whatsapp: { - name: "WhatsApp", - color: "#25D366", + name: 'WhatsApp', + color: '#25D366', icon: '', }, facebook: { - name: "Facebook", - color: "#1877F2", + name: 'Facebook', + color: '#1877F2', icon: '', }, twitter: { - name: "X", - color: "#000000", + name: 'X', + color: '#000000', icon: '', }, linkedin: { - name: "LinkedIn", - color: "#0A66C2", + name: 'LinkedIn', + color: '#0A66C2', icon: '', }, telegram: { - name: "Telegram", - color: "#0088cc", + name: 'Telegram', + color: '#0088cc', icon: '', }, reddit: { - name: "Reddit", - color: "#FF4500", + name: 'Reddit', + color: '#FF4500', icon: '', }, email: { - name: "Email", - color: "#7f7f7f", + name: 'Email', + color: '#7f7f7f', icon: '', }, }; @@ -186,7 +181,7 @@ class SocialShareButton { `; }) - .join(""); + .join(''); } getShareURL(platform) { @@ -194,33 +189,28 @@ class SocialShareButton { const encodedUrl = encodeURIComponent(url); const encodedTitle = encodeURIComponent(title); // const encodedDesc = encodeURIComponent(description); - const hashtagString = hashtags.length ? "#" + hashtags.join(" #") : ""; + const hashtagString = hashtags.length ? '#' + hashtags.join(' #') : ''; // Build platform-specific messages with customizable parameters - let whatsappMessage, - facebookMessage, - twitterMessage, - telegramMessage, - redditTitle, - emailBody; + let whatsappMessage, facebookMessage, twitterMessage, telegramMessage, redditTitle, emailBody; // WhatsApp: Casual with emoji - whatsappMessage = `\u{1F680} ${title}${description ? "\n\n" + description : ""}${hashtagString ? "\n\n" + hashtagString : ""}\n\nLive on the site \u{1F440}\nClean UI, smooth flow \u{2014} worth peeking\n\u{1F447}`; + whatsappMessage = `\u{1F680} ${title}${description ? '\n\n' + description : ''}${hashtagString ? '\n\n' + hashtagString : ''}\n\nLive on the site \u{1F440}\nClean UI, smooth flow \u{2014} worth peeking\n\u{1F447}`; // Facebook: Title + Description - facebookMessage = `${title}${description ? "\n\n" + description : ""}${hashtagString ? "\n\n" + hashtagString : ""}`; + facebookMessage = `${title}${description ? '\n\n' + description : ''}${hashtagString ? '\n\n' + hashtagString : ''}`; // Twitter: Title + Description + Hashtags + Via - twitterMessage = `${title}${description ? "\n\n" + description : ""}${hashtagString ? "\n" + hashtagString : ""}`; + twitterMessage = `${title}${description ? '\n\n' + description : ''}${hashtagString ? '\n' + hashtagString : ''}`; // Telegram: Casual with emoji - telegramMessage = `\u{1F517} ${title}${description ? "\n\n" + description : ""}${hashtagString ? "\n\n" + hashtagString : ""}\n\nLive + working\nClean stuff, take a look \u{1F447}`; + telegramMessage = `\u{1F517} ${title}${description ? '\n\n' + description : ''}${hashtagString ? '\n\n' + hashtagString : ''}\n\nLive + working\nClean stuff, take a look \u{1F447}`; // Reddit: Title + Description - redditTitle = `${title}${description ? " - " + description : ""}`; + redditTitle = `${title}${description ? ' - ' + description : ''}`; // Email: Friendly greeting - emailBody = `Hey \u{1F44B}\n\nSharing a clean project I came across:\n${title}${description ? "\n\n" + description : ""}\n\nLive, simple, and usable \u{2014} take a look \u{1F447}`; + emailBody = `Hey \u{1F44B}\n\nSharing a clean project I came across:\n${title}${description ? '\n\n' + description : ''}\n\nLive, simple, and usable \u{2014} take a look \u{1F447}`; const encodedWhatsapp = encodeURIComponent(whatsappMessage); const encodedFacebook = encodeURIComponent(facebookMessage); @@ -232,14 +222,14 @@ class SocialShareButton { const urls = { whatsapp: `https://wa.me/?text=${encodedWhatsapp}%20${encodedUrl}`, facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}"e=${encodedFacebook}`, - twitter: `https://twitter.com/intent/tweet?text=${encodedTwitter}&url=${encodedUrl}${via ? "&via=" + encodeURIComponent(via) : ""}`, + twitter: `https://twitter.com/intent/tweet?text=${encodedTwitter}&url=${encodedUrl}${via ? '&via=' + encodeURIComponent(via) : ''}`, linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`, telegram: `https://t.me/share/url?url=${encodedUrl}&text=${encodedTelegram}`, reddit: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedReddit}`, email: `mailto:?subject=${encodedTitle}&body=${encodedEmail}%20${encodedUrl}`, }; - return urls[platform] || ""; + return urls[platform] || ''; } addEventListener(element, type, handler, options = false) { @@ -265,7 +255,7 @@ class SocialShareButton { // Button click to open modal if (this.button) { const openModalHandler = () => this.openModal(); - this.addEventListener(this.button, "click", openModalHandler); + this.addEventListener(this.button, 'click', openModalHandler); } // Modal overlay click to close @@ -274,43 +264,41 @@ class SocialShareButton { this.closeModal(); } }; - this.addEventListener(this.modal, "click", modalClickHandler); + this.addEventListener(this.modal, 'click', modalClickHandler); // Close button - const closeBtn = this.modal.querySelector(".social-share-modal-close"); + const closeBtn = this.modal.querySelector('.social-share-modal-close'); const closeBtnHandler = () => this.closeModal(); - this.addEventListener(closeBtn, "click", closeBtnHandler); + this.addEventListener(closeBtn, 'click', closeBtnHandler); // Platform buttons - const platformBtns = this.modal.querySelectorAll( - ".social-share-platform-btn", - ); + const platformBtns = this.modal.querySelectorAll('.social-share-platform-btn'); platformBtns.forEach((btn) => { const platformHandler = () => { const platform = btn.dataset.platform; this.share(platform); }; - this.addEventListener(btn, "click", platformHandler); + this.addEventListener(btn, 'click', platformHandler); }); // Copy button - const copyBtn = this.modal.querySelector(".social-share-copy-btn"); + const copyBtn = this.modal.querySelector('.social-share-copy-btn'); const copyBtnHandler = () => this.copyLink(); - this.addEventListener(copyBtn, "click", copyBtnHandler); + this.addEventListener(copyBtn, 'click', copyBtnHandler); // Input click to select - const input = this.modal.querySelector(".social-share-link-input input"); + const input = this.modal.querySelector('.social-share-link-input input'); const inputSelectHandler = (e) => e.target.select(); - this.addEventListener(input, "click", inputSelectHandler); + this.addEventListener(input, 'click', inputSelectHandler); // ESC key to close this.handleKeydown = (e) => { - if (e.key === "Escape" && this.isModalOpen) { + if (e.key === 'Escape' && this.isModalOpen) { this.closeModal(); } }; - if (typeof document !== "undefined") { - document.addEventListener("keydown", this.handleKeydown); + if (typeof document !== 'undefined') { + document.addEventListener('keydown', this.handleKeydown); } this.eventsAttached = true; // Mark as attached @@ -320,11 +308,11 @@ class SocialShareButton { if (!this.modal) return; this.isModalOpen = true; - this.modal.style.display = "flex"; - this._emit("social_share_popup_open", "popup_open"); + this.modal.style.display = 'flex'; + this._emit('social_share_popup_open', 'popup_open'); // Shared body overflow management: only increment counter if this instance doesn't already own the lock - if (typeof document !== "undefined" && document.body) { + if (typeof document !== 'undefined' && document.body) { if (!this.ownsBodyLock) { // Only increment if this instance doesn't already own a lock if (SocialShareButton.openModalCount === 0) { @@ -334,7 +322,7 @@ class SocialShareButton { SocialShareButton.openModalCount++; this.ownsBodyLock = true; // Mark that this instance owns a lock } - document.body.style.overflow = "hidden"; + document.body.style.overflow = 'hidden'; } // Clear any pending animations (both open and close to prevent race conditions) @@ -349,8 +337,9 @@ class SocialShareButton { // Animate in this.openTimeout = setTimeout(() => { - if (this.modal) { // Safety check in case destroy() was called - this.modal.classList.add("active"); + if (this.modal) { + // Safety check in case destroy() was called + this.modal.classList.add('active'); } this.openTimeout = null; }, 10); @@ -359,8 +348,8 @@ class SocialShareButton { closeModal() { if (!this.modal) return; // Safety check - this.modal.classList.remove("active"); - this._emit("social_share_popup_close", "popup_close"); + this.modal.classList.remove('active'); + this._emit('social_share_popup_close', 'popup_close'); // Clear any pending animations (both open and close to prevent race conditions) if (this.openTimeout) { @@ -373,12 +362,13 @@ class SocialShareButton { } this.closeTimeout = setTimeout(() => { - if (this.modal) { // Safety check in case destroy() was called + if (this.modal) { + // Safety check in case destroy() was called this.isModalOpen = false; - this.modal.style.display = "none"; + this.modal.style.display = 'none'; // Shared body overflow management: only decrement if this instance owns the lock - if (this.ownsBodyLock && typeof document !== "undefined" && document.body) { + if (this.ownsBodyLock && typeof document !== 'undefined' && document.body) { // Decrement counter (guard against negative) if (SocialShareButton.openModalCount > 0) { SocialShareButton.openModalCount--; @@ -387,7 +377,7 @@ class SocialShareButton { // Restore original overflow only when all modals are closed if (SocialShareButton.openModalCount === 0) { - document.body.style.overflow = SocialShareButton.originalBodyOverflow || ""; + document.body.style.overflow = SocialShareButton.originalBodyOverflow || ''; SocialShareButton.originalBodyOverflow = null; } } @@ -400,25 +390,21 @@ class SocialShareButton { const shareUrl = this.getShareURL(platform); if (shareUrl) { - this._emit("social_share_click", "share", { platform }); + this._emit('social_share_click', 'share', { platform }); - if (platform === "email") { + if (platform === 'email') { window.location.href = shareUrl; } else { - window.open( - shareUrl, - "_blank", - "noopener,noreferrer,width=600,height=600", - ); + window.open(shareUrl, '_blank', 'noopener,noreferrer,width=600,height=600'); } - this._emit("social_share_success", "share", { platform }); + this._emit('social_share_success', 'share', { platform }); if (this.options.onShare) { this.options.onShare(platform, this.options.url); } } else { - this._emit("social_share_error", "error", { + this._emit('social_share_error', 'error', { platform, errorMessage: `No share URL configured for platform: ${platform}`, }); @@ -426,8 +412,8 @@ class SocialShareButton { } copyLink() { - const input = this.modal.querySelector(".social-share-link-input input"); - const copyBtn = this.modal.querySelector(".social-share-copy-btn"); + const input = this.modal.querySelector('.social-share-link-input input'); + const copyBtn = this.modal.querySelector('.social-share-copy-btn'); // Check if clipboard API is available if (navigator.clipboard && navigator.clipboard.writeText) { @@ -437,9 +423,9 @@ class SocialShareButton { // Guard against async callback after destroy if (this.isDestroyed) return; - copyBtn.textContent = "Copied!"; - copyBtn.classList.add("copied"); - this._emit("social_share_copy", "copy"); + copyBtn.textContent = 'Copied!'; + copyBtn.classList.add('copied'); + this._emit('social_share_copy', 'copy'); if (this.options.onCopy) { this.options.onCopy(this.options.url); @@ -453,13 +439,12 @@ class SocialShareButton { // Track feedback timeout to prevent callback after destroy this.feedbackTimeout = setTimeout(() => { if (this.isDestroyed || !copyBtn) return; // Safety check - copyBtn.textContent = "Copy"; - copyBtn.classList.remove("copied"); + copyBtn.textContent = 'Copy'; + copyBtn.classList.remove('copied'); this.feedbackTimeout = null; }, 2000); }) .catch(() => { - // Fallback to manual selection this.fallbackCopy(input, copyBtn); }); @@ -476,11 +461,11 @@ class SocialShareButton { try { input.select(); input.setSelectionRange(0, 99999); // For mobile devices - document.execCommand("copy"); + document.execCommand('copy'); - copyBtn.textContent = "Copied!"; - copyBtn.classList.add("copied"); - this._emit("social_share_copy", "copy"); + copyBtn.textContent = 'Copied!'; + copyBtn.classList.add('copied'); + this._emit('social_share_copy', 'copy'); if (this.options.onCopy) { this.options.onCopy(this.options.url); @@ -494,12 +479,12 @@ class SocialShareButton { // Track feedback timeout to prevent callback after destroy this.feedbackTimeout = setTimeout(() => { if (this.isDestroyed || !copyBtn) return; // Safety check - copyBtn.textContent = "Copy"; - copyBtn.classList.remove("copied"); + copyBtn.textContent = 'Copy'; + copyBtn.classList.remove('copied'); this.feedbackTimeout = null; }, 2000); } catch (_err) { - copyBtn.textContent = "Failed"; + copyBtn.textContent = 'Failed'; // Clear any existing feedback timeout if (this.feedbackTimeout) { @@ -509,7 +494,7 @@ class SocialShareButton { // Track feedback timeout to prevent callback after destroy this.feedbackTimeout = setTimeout(() => { if (this.isDestroyed || !copyBtn) return; // Safety check - copyBtn.textContent = "Copy"; + copyBtn.textContent = 'Copy'; this.feedbackTimeout = null; }, 2000); } @@ -517,8 +502,8 @@ class SocialShareButton { destroy() { if (this.handleKeydown) { - if (typeof document !== "undefined") { - document.removeEventListener("keydown", this.handleKeydown); + if (typeof document !== 'undefined') { + document.removeEventListener('keydown', this.handleKeydown); } this.handleKeydown = null; } @@ -544,17 +529,11 @@ class SocialShareButton { // Remove custom color handlers if (this.button && this.customColorMouseEnterHandler) { - this.button.removeEventListener( - "mouseenter", - this.customColorMouseEnterHandler, - ); + this.button.removeEventListener('mouseenter', this.customColorMouseEnterHandler); this.customColorMouseEnterHandler = null; } if (this.button && this.customColorMouseLeaveHandler) { - this.button.removeEventListener( - "mouseleave", - this.customColorMouseLeaveHandler, - ); + this.button.removeEventListener('mouseleave', this.customColorMouseLeaveHandler); this.customColorMouseLeaveHandler = null; } @@ -567,7 +546,7 @@ class SocialShareButton { } // Shared body overflow management: only decrement if this instance owns the lock - if (this.ownsBodyLock && typeof document !== "undefined" && document.body) { + if (this.ownsBodyLock && typeof document !== 'undefined' && document.body) { // Decrement counter (guard against negative) if (SocialShareButton.openModalCount > 0) { SocialShareButton.openModalCount--; @@ -576,7 +555,7 @@ class SocialShareButton { // Restore original overflow only when all modals are closed if (SocialShareButton.openModalCount === 0) { - document.body.style.overflow = SocialShareButton.originalBodyOverflow || ""; + document.body.style.overflow = SocialShareButton.originalBodyOverflow || ''; SocialShareButton.originalBodyOverflow = null; } } @@ -593,14 +572,14 @@ class SocialShareButton { // Update URL in modal if it exists if (this.modal) { - const input = this.modal.querySelector(".social-share-link-input input"); + const input = this.modal.querySelector('.social-share-link-input input'); if (input) { input.value = this.options.url; } } // Reapply custom colors if color option changed - if ("buttonColor" in options || "buttonHoverColor" in options) { + if ('buttonColor' in options || 'buttonHoverColor' in options) { this.applyCustomColors(); } } @@ -609,44 +588,38 @@ class SocialShareButton { if (!this.button) return; // Remove legacy global style tag to prevent cross-instance color bleed. - const styleTag = document.getElementById("social-share-custom-colors"); + const styleTag = document.getElementById('social-share-custom-colors'); if (styleTag && styleTag.parentNode) { styleTag.parentNode.removeChild(styleTag); } if (this.customColorMouseEnterHandler) { - this.button.removeEventListener( - "mouseenter", - this.customColorMouseEnterHandler, - ); + this.button.removeEventListener('mouseenter', this.customColorMouseEnterHandler); this.customColorMouseEnterHandler = null; } if (this.customColorMouseLeaveHandler) { - this.button.removeEventListener( - "mouseleave", - this.customColorMouseLeaveHandler, - ); + this.button.removeEventListener('mouseleave', this.customColorMouseLeaveHandler); this.customColorMouseLeaveHandler = null; } - this.button.style.removeProperty("background-color"); - this.button.style.removeProperty("background-image"); - this.button.style.removeProperty("border-color"); + this.button.style.removeProperty('background-color'); + this.button.style.removeProperty('background-image'); + this.button.style.removeProperty('border-color'); - const baseColor = this.options.buttonColor || ""; + const baseColor = this.options.buttonColor || ''; const hoverColor = this.options.buttonHoverColor || baseColor; if (!baseColor && !hoverColor) return; if (baseColor) { - this.button.style.backgroundImage = "none"; + this.button.style.backgroundImage = 'none'; this.button.style.backgroundColor = baseColor; this.button.style.borderColor = baseColor; } this.customColorMouseEnterHandler = () => { if (hoverColor) { - this.button.style.backgroundImage = "none"; + this.button.style.backgroundImage = 'none'; this.button.style.backgroundColor = hoverColor; this.button.style.borderColor = hoverColor; } @@ -654,26 +627,20 @@ class SocialShareButton { this.customColorMouseLeaveHandler = () => { if (baseColor) { - this.button.style.backgroundImage = "none"; + this.button.style.backgroundImage = 'none'; this.button.style.backgroundColor = baseColor; this.button.style.borderColor = baseColor; } else { - this.button.style.removeProperty("background-color"); - this.button.style.removeProperty("background-image"); - this.button.style.removeProperty("border-color"); + this.button.style.removeProperty('background-color'); + this.button.style.removeProperty('background-image'); + this.button.style.removeProperty('border-color'); } }; // Note: Custom color handlers are managed separately (not in listeners) // because they need to be removed/reapplied when colors change - this.button.addEventListener( - "mouseenter", - this.customColorMouseEnterHandler, - ); - this.button.addEventListener( - "mouseleave", - this.customColorMouseLeaveHandler, - ); + this.button.addEventListener('mouseenter', this.customColorMouseEnterHandler); + this.button.addEventListener('mouseleave', this.customColorMouseLeaveHandler); } // --------------------------------------------------------------------------- @@ -699,8 +666,8 @@ class SocialShareButton { */ _getContainer() { if (!this.options.container) return null; - if (typeof document === "undefined") return null; - return typeof this.options.container === "string" + if (typeof document === 'undefined') return null; + return typeof this.options.container === 'string' ? document.querySelector(this.options.container) : this.options.container; } @@ -731,7 +698,7 @@ class SocialShareButton { const payload = { version: ANALYTICS_SCHEMA_VERSION, - source: "social-share-button", + source: 'social-share-button', eventName, interactionType, platform: extra.platform || null, @@ -748,14 +715,14 @@ class SocialShareButton { // Optional console output for development / debugging if (this.options.debug) { // eslint-disable-next-line no-console - console.log("[SocialShareButton Analytics]", payload); + console.log('[SocialShareButton Analytics]', payload); } // Path 1 — DOM CustomEvent (framework-agnostic, CDN-friendly) // Bubbles from the container element so delegated listeners work naturally. - if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") { + if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') { try { - const domEvent = new CustomEvent("social-share", { + const domEvent = new CustomEvent('social-share', { bubbles: true, cancelable: false, composed: true, // crosses shadow-DOM boundaries; safe to set in all envs @@ -763,23 +730,29 @@ class SocialShareButton { }); const el = this._getContainer(); (el || document).dispatchEvent(domEvent); - } catch (_) {} + } catch (_) { + /* ignore */ + } } // Path 2 — onAnalytics callback (direct, single-consumer hook) - if (typeof this.options.onAnalytics === "function") { + if (typeof this.options.onAnalytics === 'function') { try { this.options.onAnalytics(payload); - } catch (_) {} + } catch (_) { + /* ignore */ + } } // Path 3 — plugin / adapter registry (supports multiple simultaneous consumers) if (Array.isArray(this.options.analyticsPlugins)) { for (const plugin of this.options.analyticsPlugins) { - if (plugin && typeof plugin.track === "function") { + if (plugin && typeof plugin.track === 'function') { try { plugin.track(payload); - } catch (_) {} + } catch (_) { + /* ignore */ + } } } } @@ -791,10 +764,10 @@ SocialShareButton.openModalCount = 0; SocialShareButton.originalBodyOverflow = null; // Export for different module systems -if (typeof module !== "undefined" && module.exports) { +if (typeof module !== 'undefined' && module.exports) { module.exports = SocialShareButton; } -if (typeof window !== "undefined") { +if (typeof window !== 'undefined') { window.SocialShareButton = SocialShareButton; }