1'use strict'; 2 3/** 4 * Smash Balloon Elementor Handler 5 * 6 * Handles click/drag behaviour for faux SB widgets in the Elementor editor: 7 * - Disables drag on inactive (uninstalled) SB widgets 8 * - Shows install popup modal on click 9 * - Calls `am_recommended_block_install` AJAX action to install plugins 10 */ 11 12let SbElementorHandler = window.SbElementorHandler || ( function( _document, window, $ ) { 13 14 const smashBalloonPlugins = sbHandler.smashPlugins; 15 16 let app = { 17 18 _observer: null, 19 _debounceTimer: null, 20 21 init: function() { 22 app.events(); 23 }, 24 25 events: function() { 26 app.waitForPanel( function() { 27 app.disableInactiveSmashWidgets(); 28 app.observePanelChanges(); 29 }); 30 }, 31 32 /** 33 * Poll until window.parent.elementor.panel exists, then invoke callback. 34 */ 35 waitForPanel: function( callback ) { 36 var check = function() { 37 if ( window.parent.elementor && window.parent.elementor.panel && window.parent.elementor.panel.$el ) { 38 callback(); 39 } else { 40 setTimeout( check, 200 ); 41 } 42 }; 43 check(); 44 }, 45 46 /** 47 * Attach a MutationObserver on the panel element to detect re-renders. 48 * When Elementor destroys/re-creates the panel DOM (e.g. navigating 49 * between widget list and widget settings), re-apply disable logic. 50 */ 51 observePanelChanges: function() { 52 if ( app._observer ) { 53 app._observer.disconnect(); 54 } 55 56 var panelEl = window.parent.elementor.panel.$el[0]; 57 if ( ! panelEl ) { 58 return; 59 } 60 61 app._observer = new window.parent.MutationObserver( function() { 62 clearTimeout( app._debounceTimer ); 63 app._debounceTimer = setTimeout( function() { 64 app.disableInactiveSmashWidgets(); 65 }, 100 ); 66 }); 67 68 app._observer.observe( panelEl, { 69 childList: true, 70 subtree: true 71 }); 72 }, 73 74 disableInactiveSmashWidgets: function() { 75 var panel$ = window.parent.elementor.panel.$el; 76 if ( ! panel$ || ! panel$.length ) { 77 return; 78 } 79 80 // Scan the entire panel rather than scoping to 81 // `#elementor-panel-category-smashballoon`. Elementor renders the 82 // search-results widget list (and lazy-loaded scrolled tiles) 83 // outside that category container, so a category-scoped query 84 // misses faux widgets shown there. 85 for ( const pluginName in smashBalloonPlugins ) { 86 panel$ 87 .find( '.sb-elem-inactive.sb-elem-' + pluginName ) 88 .each( function() { 89 var pluginWrapper = $( this ).closest( '.elementor-element-wrapper' ); 90 if ( ! pluginWrapper.length ) { 91 return; 92 } 93 94 // Skip if already processed 95 if ( pluginWrapper.find( '.sb-click-overlay' ).length ) { 96 return; 97 } 98 99 var pluginWidget = pluginWrapper.find( '.elementor-element' ); 100 101 // Block all pointer events on the actual draggable element 102 pluginWidget.css( 'pointer-events', 'none' ); 103 104 // Add a click-capturing overlay 105 pluginWrapper.css( 'position', 'relative' );
106 var $overlay = $( '<div class="sb-click-overlay"></div>' ).css({ 107 position: 'absolute', 108 top: 0, 109 left: 0, 110 width: '100%', 111 height: '100%', 112 zIndex: 10, 113 cursor: 'pointer' 114 }); 115 116 // Use closure to capture pluginName 117 (function( name ) { 118 // Suppress mousedown so Elementor's native Pro 119 // upsell dialog (bound on mousedown for Pro 120 // widgets) does not fire on top of our SB 121 // upsell modal. 122 $overlay.on( 'mousedown', function( e ) { 123 e.stopPropagation(); 124 e.preventDefault(); 125 }); 126 $overlay.on( 'click', function( e ) { 127 e.stopPropagation(); 128 e.preventDefault(); 129 app.createUpsellPopup( name ); 130 }); 131 })( pluginName ); 132 133 pluginWrapper.append( $overlay ); 134 }); 135 } 136 }, 137 138 createUpsellPopup: function( pluginName ) { 139 let plugin = smashBalloonPlugins[ pluginName ], 140 spinnerIcon = '<svg x="0px" y="0px" width="20px" height="20px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"><path fill="#fff" d="M43.935,25.145c0-10.318-8.364-18.683-18.683-18.683c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615c8.072,0,14.615,6.543,14.615,14.615H43.935z"><animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.6s" repeatCount="indefinite"/></path></svg>', 141 upsellPopupOutput = 142 '<div class="sb-source-ctn sb-fs-boss sb-center-boss">\ 143 <div class="sb-source-popup sb-popup-inside sb-install-plugin-modal">\ 144 <div class="sb-popup-cls">\ 145 <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">\ 146 <path d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z" fill="#141B38"/>\ 147 </svg>\ 148 </div>\ 149 <div class="sb-install-plugin-body sb-fs">\ 150 <div class="sb-install-plugin-header">\ 151 <div class="sb-plugin-image">' + plugin["svgIcon"] + '\ 152 <svg class="sb-plugin-cta-logo" width="26" height="33" viewBox="0 0 26 33" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M25.5608 15.2708C25.5608 6.86286 20.0495 0.046814 13.2486 0.046814C6.44763 0.046814 0.933838 6.86286 0.933838 15.2708C0.933838 23.3312 5.98416 29.9079 12.3795 30.4596L11.6995 32.6132L15.9639 32.2505L14.4677 30.4218C20.6943 29.6661 25.5608 23.1725 25.5608 15.2708Z" fill="#FE544F"/><path fill-rule="evenodd" clip-rule="evenodd" d="M16.1843 5.39911L16.7768 11.5131L22.9165 11.6894L18.4752 15.8189L21.983 20.8926L16.0735 19.7817L14.282 25.6913L11.5618 20.3993L6.0693 22.916L8.18218 17.2428L2.82544 14.5409L8.55968 12.6968L6.97737 7.04787L12.4024 10.1407L16.1843 5.39911Z" fill="white"/></svg>\ 153 </div>\ 154 <div class="sb-plugin-name">\ 155 <strong>Requires</strong>\ 156 <h3>\ 157 ' + plugin["displayName"] + '\ 158 <span>Free</span>\ 159 </h3>\ 160 </div>\ 161 </div>\ 162 <div class="sb-install-plugin-content">\ 163 <p>' + plugin["description"] + '</p>\ 164 <button class="sb-install-plugin-btn sb-btn-orange sb-plugin-btn" data-plugin="' + plugin['download_plugin'] + '">\ 165 <span class="sb-install-plugin-spinner" style="display:none">' + spinnerIcon + '</span>\ 166 ' + (plugin["pluginInstalled"] ? 'Activate' : 'Install') + '\ 167 </button>\ 168 <button class="sb-install-refresh-btn sb-btn-blue sb-plugin-btn" style="display:none">\ 169 Refresh The Page\ 170 </button>\ 171 </div>\ 172 </div>\ 173 </div>\ 174 </div>'; 175 176 if ( $( window.parent.document.body ).find( '.sb-center-boss' ).length === 0 ) { 177 $( window.parent.document.body ).append( upsellPopupOutput ); 178 179 $( window.parent.document.body ).find( '.sb-install-plugin-btn' ).on( 'click', function() { 180 let downloadPlugin = $( this ).attr( 'data-plugin' ); 181 $( this ).find( '.sb-install-plugin-spinner' ).show(); 182 app.installPlugin( downloadPlugin, pluginName ); 183 }); 184 185 $( window.parent.document.body ).find( '.sb-install-refresh-btn' ).on( 'click', function() { 186 window.parent.location.reload(); 187 }); 188 189 $( window.parent.document.body ).find( '.sb-popup-cls' ).on( 'click', function() { 190 app.closeUpsellPopup(); 191 }); 192 193 $( window.parent.document.body ).find( '.sb-center-boss' ).on( 'click', function( e ) { 194 if ( e.target === this ) { 195 app.closeUpsellPopup(); 196 } 197 }); 198 } 199 }, 200 201 closeUpsellPopup: function() { 202 $( window.parent.document.body ).find( '.sb-center-boss' ).remove(); 203 }, 204 205 installPlugin: function( downloadPlugin, pluginName ) { 206 let data = new FormData(); 207 data.append( 'action', 'am_recommended_block_install' ); 208 data.append( 'nonce', sbHandler.nonce ); 209 data.append( 'plugin', downloadPlugin ); 210 211 let $btn = $( window.parent.document.body ).find( '.sb-install-plugin-btn' ); 212 213 fetch( sbHandler.ajax_handler, { 214 method: 'POST', 215 credentials: 'same-origin', 216 body: data 217 }) 218 .then( function( response ) { 219 if ( ! response.ok ) { 220 throw new Error( 'Server error' ); 221 } 222 return response.json(); 223 } ) 224 .then( function( data ) { 225 if ( data.success === true ) { 226 smashBalloonPlugins[ pluginName ].pluginInstalled = true; 227 $btn.hide(); 228 $( window.parent.document.body ).find( '.sb-install-refresh-btn' ).show(); 229 } else { 230 $btn.find( '.sb-install-plugin-spinner' ).hide(); 231 $btn.text( 'Error. Please try again.' ); 232 } 233 }) 234 .catch( function( err ) { 235 console.error( '[SB] Plugin install error:', err ); 236 $btn.find( '.sb-install-plugin-spinner' ).hide(); 237 $btn.text( 'Error. Please try again.' ); 238 }); 239 } 240 241 }; 242 243 return app; 244 245}( document, window, jQuery ) ); 246 247SbElementorHandler.init();
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.