1/** 2 * Frontend Notifications 3 * 4 * @since 2.23.0 5 * @author Andrew Munro <[email protected]> 6 */ 7 8/* globals HTMLElement, affiliateWPFENotifications, customElements */ 9 10/** 11 * Represents a custom notification element for AffiliateWP. 12 * 13 * @since 2.23.0 14 * 15 * This class extends HTMLElement to create a custom web component for displaying notifications. 16 * It includes functionality to dynamically set the theme based on admin settings, 17 * handle animations, and manage the display of notifications based on certain conditions. 18 */ 19class AffiliateWPNotification extends HTMLElement { 20 21 /** 22 * Constructor 23 * 24 * @since 2.23.0 25 * 26 * Constructs the AffiliateWPNotification element, initializing its shadow DOM 27 * and cloning the template content into it. 28 */ 29 constructor() { 30 31 super(); 32 33 this.attachShadow( { mode: 'open' } ); 34 35 const template = document.getElementById( 'affwp-notification' ); 36 37 if ( ! template ) { 38 39 window.console.error( 'Template not found' ); 40 return; 41 } 42 43 this.shadowRoot.appendChild( template.content.cloneNode( true ) ); 44 45 // Find and store references to dynamic parts of the template. 46 this.notificationDiv = this.shadowRoot.querySelector( '#notification' ); 47 } 48 49 /** 50 * Lifecycle hook called when the element is added to the document's DOM. 51 * 52 * @since 2.23.0 53 * 54 * It sets the theme based on admin settings, initializes CSS styling, 55 * and sets up animations and event listeners for the notification element. 56 */ 57 connectedCallback() { 58 59 // Set the data-mode attribute based on the admin setting. 60 const notificationDiv = this.shadowRoot.querySelector( '#notification' ); 61 const adminDarkModeSetting = affiliateWPFENotifications.darkMode; 62 63 let useDarkMode = false; 64 65 if ( adminDarkModeSetting === 'dark' ) { 66 useDarkMode = true; 67 } else if ( adminDarkModeSetting === 'auto' ) { 68 useDarkMode = window.matchMedia( '(prefers-color-scheme: dark)' ).matches; 69 } 70 71 if ( notificationDiv ) { 72 73 if ( useDarkMode ) { 74 notificationDiv.setAttribute( 'data-mode', 'dark' ); 75 } else { 76 notificationDiv.removeAttribute( 'data-mode' ); 77 } 78 } 79 80 // Create a link element for the CSS. 81 const cssLink = document.createElement( 'link' ); 82 83 cssLink.setAttribute( 'rel', 'stylesheet' ); 84 cssLink.setAttribute( 'type', 'text/css' ); 85 86 if ( affiliateWPFENotifications && affiliateWPFENotifications.cssUrl ) { 87 cssLink.setAttribute( 'href', affiliateWPFENotifications.cssUrl ); 88 } 89 90 this.shadowRoot.appendChild( cssLink ); 91 92 // Animation on mount. 93 const notification = this.shadowRoot.getElementById( 'notification' ); 94 95 if ( notification ) { 96 notification.style.animation = 'slideIn 0.3s forwards'; 97 } 98 99 // Event listener for close button 100 const closeButton = this.shadowRoot.querySelector( '#close-notification' ); 101 102 if ( closeButton ) { 103 104 closeButton.addEventListener( 'click', () => { 105 106 notification.style.animation = 'slideOut 0.3s forwards'; 107 108 setTimeout( () => this.remove(), 300 ); // Remove after animation. 109 } ); 110 } 111 } 112} 113 114( function() { 115 116 customElements.define( 'affiliatewp-notification', AffiliateWPNotification ); 117 118 /** 119 * Extracts the affiliate identifier from the current URL. 120 * 121 * @since 2.23.0 122 * 123 * Attempts to retrieve the affiliate ID using the query parameter first; if not found, 124 * falls back to parsing the URL's path segments. 125 * 126 * @return {string|null} The affiliate identifier if found, otherwise null. 127 */ 128 function getAffiliate() { 129 130 const urlParams = new URLSearchParams( window.location.search ); 131 let affiliate = urlParams.get( affiliateWPFENotifications.referralVar ); 132 133 if ( ! affiliate ) { 134 135 // For pretty permalink structure. 136 const pathArray = window.location.pathname.split( '/' ); 137 const refIndex = pathArray.indexOf( affiliateWPFENotifications.referralVar ); 138 139 if ( refIndex > -1 && pathArray.length > refIndex + 1 ) { 140 affiliate = pathArray[ refIndex + 1 ]; 141 } 142 } 143 144 return affiliate; 145 } 146 147 /** 148 * Creates and appends a custom notification element to the body of the document. 149 * 150 * @since 2.23.0 151 *
152 * This function is typically called in response to a specific event, such as a successful 153 * application of an affiliate coupon. 154 */ 155 function showCustomNotification() { 156 document.body.appendChild( document.createElement( 'affiliatewp-notification' ) ); 157 } 158 159 /** 160 * Event listener for the DOMContentLoaded event. 161 * 162 * @since 2.23.0 163 * 164 * Fetches and applies an affiliate coupon if an affiliate identifier is present in the URL. 165 * Upon successful application, displays a custom notification. 166 */ 167 document.addEventListener( 'DOMContentLoaded', () => { 168 169 const affiliate = getAffiliate(); 170 171 if ( affiliate ) { 172 173 const params = new URLSearchParams( { 174 action: 'apply_affiliate_coupon', 175 affiliate 176 } ); 177 178 fetch( `${affiliateWPFENotifications.ajaxUrl}?${params.toString()}`, { 179 method: 'POST', 180 credentials: 'same-origin' 181 } ) 182 183 .then( response => response.json() ) 184 185 .then( data => { 186 if ( data.couponApplied ) { 187 showCustomNotification(); 188 } 189 } ) 190 191 .catch( error => window.console.error( 'Error:', error ) ); 192 } 193 } ); 194} () );
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.