PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents / 4.6.0
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents v4.6.0
4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / EmbedPress / Elementor / Embedpress_Elementor_Integration.php
embedpress / EmbedPress / Elementor Last commit date
Controls 4 days ago Widgets 4 days ago Elementor_Upsale.php 3 months ago Embedpress_Elementor_Integration.php 4 days ago
Embedpress_Elementor_Integration.php
1268 lines
1 <?php
2
3 namespace EmbedPress\Elementor;
4
5
6 (defined('ABSPATH')) or die("No direct script access allowed.");
7
8 use EmbedPress\Compatibility;
9 use EmbedPress\Elementor\Widgets\Embedpress_Calendar;
10 use EmbedPress\Elementor\Widgets\Embedpress_Document;
11 use EmbedPress\Elementor\Widgets\Embedpress_Elementor;
12 use EmbedPress\Elementor\Widgets\Embedpress_Pdf;
13 use EmbedPress\Elementor\Widgets\Embedpress_Pdf_Gallery;
14 use EmbedPress\Elementor\Widgets\Embedpress_Google_Reviews;
15 use EmbedPress\Elementor\Controls\Place_Picker as GR_Place_Picker;
16 use EmbedPress\Includes\Classes\Helper;
17
18 class Embedpress_Elementor_Integration
19 {
20
21 /**
22 * @since 2.4.2
23 */
24 public function init()
25 {
26 $elements = (array) get_option(EMBEDPRESS_PLG_NAME . ":elements", []);
27 $e_blocks = isset($elements['elementor']) ? (array) $elements['elementor'] : [];
28 if (!empty($e_blocks['embedpress']) || !empty($e_blocks['embedpress-document']) || !empty($e_blocks['embedpress-pdf']) || !empty($e_blocks['embedpress-pdf-gallery']) || !empty($e_blocks['embedpress-google-reviews'])) {
29 // Custom control type for the Google Reviews place picker.
30 add_action('elementor/controls/register', [$this, 'register_google_reviews_controls']);
31 add_action('elementor/editor/before_enqueue_scripts', [$this, 'enqueue_google_reviews_picker']);
32 // Asset enqueuing now handled by AssetManager
33 add_action('elementor/elements/categories_registered', array($this, 'register_widget_categories'));
34 add_action('elementor/widgets/widgets_registered', array($this, 'register_widget'));
35 add_action('elementor/widgets/register', array($this, 'register_widget'));
36 add_filter('oembed_providers', [$this, 'addOEmbedProviders']);
37
38
39 //Just disabled rating and feedback
40 // if (Helper::get_options_value('turn_off_rating_help') || !is_plugin_active('embedpress-pro/embedpress-pro.php')) {
41 // add_action('elementor/editor/after_enqueue_scripts', [$this, 'elementor_upsale']);
42 // }
43 add_action('elementor/editor/after_enqueue_scripts', [$this, 'elementor_upsale']);
44 }
45
46 // AJAX handler for PDF Gallery thumbnail generation (must register early for admin-ajax.php)
47 // Uses Pdf_Thumbnail_Handler (no Elementor dependency) to avoid "Class Elementor\Widget_Base not found" fatal
48 if (!empty($e_blocks['embedpress-pdf-gallery']) || !isset($e_blocks['embedpress-pdf-gallery'])) {
49 add_action('wp_ajax_ep_generate_pdf_thumbnail', ['EmbedPress\Includes\Classes\Pdf_Thumbnail_Handler', 'ajax_generate_pdf_thumbnail']);
50 add_action('wp_ajax_ep_upload_pdf_thumbnail', ['EmbedPress\Includes\Classes\Pdf_Thumbnail_Handler', 'ajax_upload_pdf_thumbnail']);
51 }
52 }
53
54 /**
55 * Add elementor category
56 *
57 * @since 2.4.3
58 */
59 public function register_widget_categories($elements_manager)
60 {
61 $elements_manager->add_category(
62 'embedpress',
63 [
64 'title' => __('EmbedPress', 'embedpress'),
65 'icon' => 'font',
66 ],
67 1
68 );
69 }
70
71 /**
72 * Load elementor widget
73 *
74 * @param $widgets_manager
75 * @throws \Exception
76 * @since 2.4.2
77 */
78 public function register_widget($widgets_manager)
79 {
80 // Guard against double-registration when both `widgets_registered`
81 // and `widgets/register` hooks fire (Elementor fires both on modern
82 // versions). The pre-existing did_action guard misses the second
83 // call, which triggers PHP notices like "Cannot redeclare control
84 // with same name 'ep_gr_place'" from the Google Reviews widget.
85 static $registered = false;
86 if ($registered) {
87 return;
88 }
89 $registered = true;
90
91 if (
92 did_action('elementor/widgets/widgets_registered') &&
93 did_action('elementor/widgets/register') // doing action
94 ) {
95 return;
96 }
97
98 $elements = (array) get_option(EMBEDPRESS_PLG_NAME . ":elements", []);
99 $e_blocks = isset($elements['elementor']) ? (array) $elements['elementor'] : [];
100
101 if (method_exists($widgets_manager, 'register')) {
102 if (!empty($e_blocks['embedpress'])) {
103 $widgets_manager->register(new Embedpress_Elementor);
104 }
105 if (!empty($e_blocks['embedpress-document'])) {
106 $widgets_manager->register(new Embedpress_Document);
107 }
108
109 if (!empty($e_blocks['embedpress-pdf'])) {
110 $widgets_manager->register(new Embedpress_Pdf);
111 }
112 if (!isset($e_blocks['embedpress-pdf-gallery']) || !empty($e_blocks['embedpress-pdf-gallery'])) {
113 $widgets_manager->register(new Embedpress_Pdf_Gallery);
114 }
115 if (!empty($e_blocks['embedpress-calendar'])) {
116 $widgets_manager->register(new Embedpress_Calendar);
117 }
118 if (!isset($e_blocks['embedpress-google-reviews']) || !empty($e_blocks['embedpress-google-reviews'])) {
119 $widgets_manager->register(new Embedpress_Google_Reviews);
120 }
121 } else {
122 if (!empty($e_blocks['embedpress'])) {
123 $widgets_manager->register_widget_type(new Embedpress_Elementor);
124 }
125 if (!empty($e_blocks['embedpress-document'])) {
126 $widgets_manager->register_widget_type(new Embedpress_Document);
127 }
128
129 if (!empty($e_blocks['embedpress-pdf'])) {
130 $widgets_manager->register_widget_type(new Embedpress_Pdf);
131 }
132 if (!isset($e_blocks['embedpress-pdf-gallery']) || !empty($e_blocks['embedpress-pdf-gallery'])) {
133 $widgets_manager->register_widget_type(new Embedpress_Pdf_Gallery);
134 }
135 if (!empty($e_blocks['embedpress-calendar'])) {
136 $widgets_manager->register_widget_type(new Embedpress_Calendar);
137 }
138 if (!isset($e_blocks['embedpress-google-reviews']) || !empty($e_blocks['embedpress-google-reviews'])) {
139 $widgets_manager->register_widget_type(new Embedpress_Google_Reviews);
140 }
141 }
142 }
143
144 /**
145 * Enqueue elementor assets
146 * @since 2.4.3
147 * Now handled by AssetManager
148 */
149 public function embedpress_enqueue_style()
150 {
151 // Assets are now handled by the centralized AssetManager
152 // This method is kept for backward compatibility
153 }
154
155 /**
156 * Register custom Elementor controls for Google Reviews.
157 */
158 public function register_google_reviews_controls($controls_manager)
159 {
160 if (method_exists($controls_manager, 'register')) {
161 $controls_manager->register(new GR_Place_Picker());
162 } else {
163 $controls_manager->register_control(GR_Place_Picker::CONTROL_TYPE, new GR_Place_Picker());
164 }
165 }
166
167 /**
168 * Enqueue the Backbone view + stylesheet that drive the place picker
169 * custom control. Loaded only in the Elementor editor.
170 */
171 public function enqueue_google_reviews_picker()
172 {
173 // Enqueue from assets/ (the build output shipped in the dist zip), NOT
174 // static/ — `/static` is stripped by .distignore, so EMBEDPRESS_URL_STATIC
175 // 404s in production and the Elementor picker control renders unstyled.
176 wp_enqueue_style(
177 'embedpress-gr-elementor-picker',
178 EMBEDPRESS_URL_ASSETS . 'css/ep-gr-elementor-picker.css',
179 [],
180 EMBEDPRESS_VERSION
181 );
182 wp_enqueue_script(
183 'embedpress-gr-elementor-picker',
184 EMBEDPRESS_URL_ASSETS . 'js/ep-gr-elementor-control.js',
185 ['jquery', 'underscore', 'backbone', 'elementor-editor'],
186 EMBEDPRESS_VERSION,
187 true
188 );
189 // Search needs a provider. When the effective provider is the hosted
190 // EmbedPress API ('managed' — the default when the user has no own
191 // Google/Apify key) but the site isn't connected, the picker blocks
192 // search + prompts to connect (the backend refuses it too). A user with
193 // their own key isn't 'managed', so they're not blocked.
194 $gr_connected = true;
195 if (\EmbedPress\Includes\Classes\GoogleReviewsRenderer::get_search_provider() === 'managed') {
196 $gr_connected = \EmbedPress\Includes\Classes\GoogleReviewsManaged::get_auth() !== [];
197 }
198 wp_localize_script('embedpress-gr-elementor-picker', 'epGoogleReviewsElementor', [
199 'restUrl' => esc_url_raw(rest_url('embedpress/v1/google-reviews')),
200 'nonce' => wp_create_nonce('wp_rest'),
201 // Drives the search dropdown's free/Pro flow (results-first + "+ Select"
202 // / "Upgrade to add" / upsell note), matching the settings + block pickers.
203 'proActive' => defined('EMBEDPRESS_SL_ITEM_SLUG'),
204 'connected' => $gr_connected,
205 'upgradeUrl' => 'https://wpdeveloper.com/in/upgrade-embedpress',
206 'i18n' => [
207 'noResults' => __('No matches. Try a different spelling or include the city.', 'embedpress'),
208 'failed' => __('Search failed.', 'embedpress'),
209 'addKey' => __('Connect the EmbedPress API in EmbedPress → Google Reviews to search.', 'embedpress'),
210 'notConnected' => __('Connect to the EmbedPress API to search. Open EmbedPress → Google Reviews settings to connect.', 'embedpress'),
211 'select' => __('Select', 'embedpress'),
212 'added' => __('already added', 'embedpress'),
213 'upgrade' => __('Upgrade to add', 'embedpress'),
214 'limitLead' => __('You’ve added your 1 free place.', 'embedpress'),
215 'goPro' => __('Go Pro', 'embedpress'),
216 'limitTail' => __('to show reviews from multiple businesses at once.', 'embedpress'),
217 'limitTitle' => __('Showing more than one place needs EmbedPress Pro — click to upgrade.', 'embedpress'),
218 'review' => __('review', 'embedpress'),
219 'reviews' => __('reviews', 'embedpress'),
220 ],
221 ]);
222 }
223
224 public function editor_enqueue_style()
225 {
226 // Assets are now handled by the centralized AssetManager
227 // This method is kept for backward compatibility
228 }
229
230 public function editor_enqueue_scripts() {}
231
232 public function addOEmbedProviders($providers)
233 {
234 if (Compatibility::isWordPress5() && !Compatibility::isClassicalEditorActive()) {
235 unset($providers['#https?://(.+\.)?wistia\.com/medias/.+#i'], $providers['#https?://(.+\.)?fast\.wistia\.com/embed/medias/.+#i\.jsonp']);
236 }
237
238 return $providers;
239 }
240
241 public function elementor_upsale()
242 {
243 ?>
244 <style>
245 :root {
246 /* Light Mode Variables */
247 --background-color: #FDFAFF;
248 --text-color: #0C0D0E;
249 --secondary-text-color: #5f6c7f;
250 --description-text-color: #5f6c7f;
251 --border-color: #ECEFF5;
252 --button-bg: #5b4e96;
253 --button-text: #ffffff;
254 --upgrade-bg: linear-gradient(181.32deg, #fffbf8 1.12%, #ffffff 98.95%);
255 --star-color: #b1b8c2;
256 --placeholder-text-color: #5f6c7f;
257 --submit-button-color: #5b4e96;
258 --form-control-backgound: #fff;
259 --upgrade-box-title-color: #1d2939;
260 }
261
262 @media (prefers-color-scheme: light) {
263 :root {
264 /* Light Mode Variables */
265 --background-color: #FDFAFF;
266 --text-color: #0C0D0E;
267 --secondary-text-color: #5f6c7f;
268 --description-text-color: #5f6c7f;
269 --border-color: #ECEFF5;
270 --button-bg: #5b4e96;
271 --button-text: #ffffff;
272 --upgrade-bg: linear-gradient(181.32deg, #fffbf8 1.12%, #ffffff 98.95%);
273 --star-color: #b1b8c2;
274 --placeholder-text-color: #5f6c7f;
275 --submit-button-color: #5b4e96;
276 --form-control-backgound: #fff;
277 --upgrade-box-title-color: #1d2939;
278
279 }
280 }
281
282 @media (prefers-color-scheme: dark) {
283 :root {
284 /* Dark Mode Variables */
285 --background-color: #1A1C1F;
286 --text-color: #ffffff;
287 --secondary-text-color: #CBCBD0;
288 --description-text-color: #fff;
289 --border-color: #272A2F;
290 --button-bg: #4b3293;
291 --button-text: #ffffff;
292 --upgrade-bg: linear-gradient(181.32deg, #1F2023 1.12%, #18191B 98.95%);
293 --star-color: #676D76;
294 --placeholder-text-color: #CBCBD0;
295 --submit-button-color: #fff;
296 --form-control-backgound: #1F2124;
297 --upgrade-box-title-color: #fff;
298
299
300 }
301 }
302
303 /* .elementor-panel .plugin-rating {
304 border-top: 2px solid #e6e8ea;
305 } */
306
307 /* Applying Variables */
308 .elementor-panel .rating-chat-content {
309 /* background-color: var(--background-color); */
310 /* border: 0.6px solid var(--border-color); */
311 color: var(--text-color);
312 }
313
314 .elementor-panel .plugin-rating h4 {
315 color: var(--text-color);
316 }
317
318 .elementor-panel .plugin-rating .chat-button {
319 background-color: var(--button-bg);
320 color: var(--button-text);
321 }
322
323 .elementor-panel .plugin-rating .upgrade-box {
324 background: var(--upgrade-bg);
325 border: 0.6px solid var(--border-color);
326 }
327
328 .elementor-panel p.thank-you-message {
329 color: var(--secondary-text-color);
330 }
331
332 .elementor-panel .plugin-rating .stars .star {
333 color: var(--star-color);
334 }
335
336
337 .elementor-panel .plugin-rating {
338 font-family: system-ui;
339 padding: 15px;
340 padding-top: 0;
341 }
342
343 .elementor-panel .rating-chat-content {
344 border-radius: 4px;
345 border-width: 0.6px;
346 gap: 12px;
347 padding: 15px 5px;
348 position: relative;
349 display: flex;
350 flex-direction: column;
351 overflow: hidden;
352 /* margin-top: 15px; */
353 }
354
355 /* .rating-chat-content::after {
356 content: "";
357 position: absolute;
358 top: -65px;
359 right: -65px;
360 width: 120px;
361 height: 120px;
362 background: radial-gradient(circle, rgb(121 62 255 / 14%) 20%, transparent 70%);
363 border-radius: 50%;
364 } */
365
366
367 /* .rating-chat-content::after{
368 content: '';
369 background: linear-gradient(175.54deg, rgba(81, 241, 255, 0.117) 19.13%, rgba(71, 58, 217, 0.132319) 22.57%, rgba(127, 22, 255, 0.2085) 39.66%, rgba(17, 1, 35, 0.3) 60.19%);
370 transform: rotate(60deg);
371 top: 0;
372 right: 0;
373 z-index: 1212;
374 width: 118.63550843535211px;
375 height: 208.14842708207777px;
376 position: absolute;
377 transform: rotate(60deg);
378 backdrop-filter: blur(20px);
379 } */
380
381
382 .elementor-panel .plugin-rating h4 {
383 font-size: 15px;
384 font-weight: 500;
385 }
386
387 .elementor-panel .plugin-rating .stars {
388 display: flex;
389 gap: 5px;
390 margin-bottom: 10px;
391 color: #FFFFFF;
392 }
393
394 .elementor-panel .plugin-rating .stars .star {
395 cursor: pointer;
396 width: 20px;
397 height: 20px;
398 }
399
400 .elementor-panel .plugin-rating .thank-you-msg-container {
401 padding: 15px;
402 border-radius: 8px;
403 text-align: left;
404 background: linear-gradient(181.32deg, #f5f3ff 1.12%, #ffffff 98.95%);
405 border: 0.6px solid #f4efec;
406 position: relative;
407 }
408
409 .elementor-panel .plugin-rating .thank-you-msg-container span.close-icon {
410 position: absolute;
411 top: 8px;
412 right: 8px;
413 }
414
415 .elementor-panel .plugin-rating .thank-you-msg-container span.close-icon svg {
416 height: 12px;
417 width: 12px;
418 cursor: pointer;
419 }
420
421 /* .elementor-panel .plugin-rating .thankyou-msg-container span.close-icon {
422 position: absolute;
423 top: 8px;
424 right: 8px;
425 } */
426
427 .elementor-panel .plugin-rating .thank-you-msg-container p.thank-you-message {
428 font-weight: 400;
429 color: #232c39;
430 margin-bottom: 8px;
431 font-size: 12px;
432 }
433
434 .elementor-panel .plugin-rating .thank-you-msg-container span.undo-review {
435 color: #5b4e96;
436 font-weight: 400;
437 text-decoration: none;
438 cursor: pointer !important;
439 }
440
441 .elementor-panel .plugin-rating .chat-button {
442 color: white;
443 border: none;
444 padding: 10px 20px;
445 border-radius: 5px;
446 cursor: pointer;
447 font-size: 14px;
448 display: flex;
449 align-items: center;
450 justify-content: center;
451 gap: 5px;
452 font-weight: 400;
453 width: 100%;
454 }
455
456 .elementor-panel .plugin-rating .chat-button svg {
457 width: 18px;
458 height: 18px;
459 }
460
461 .elementor-panel .plugin-rating .chat-button:hover {
462 background-color: #4b3293;
463 }
464
465 .elementor-panel .plugin-rating .upgrade-box {
466 padding: 15px;
467 margin-top: 15px;
468 border-radius: 8px;
469 text-align: left;
470 }
471
472 .elementor-panel .plugin-rating .upgrade-box h5 {
473 font-size: 14px;
474 margin-top: 0;
475 margin-bottom: 10px;
476 color: #1d2939;
477 color: #fff;
478 color: var(--upgrade-box-title-color);
479 font-weight: 600;
480 }
481
482 .elementor-panel .plugin-rating .upgrade-box p {
483 font-size: 12px;
484 color: var(--secondary-text-color);
485 margin-bottom: 12px;
486 font-weight: 400;
487 line-height: 1.6;
488 }
489
490 .elementor-panel .plugin-rating .upgrade-box .upgrade-link {
491 color: #ec6e00;
492 font-weight: 400;
493 text-decoration: none;
494 }
495
496 .elementor-panel .plugin-rating .upgrade-box .upgrade-link:hover {
497 text-decoration: underline;
498 }
499
500
501
502 .elementor-panel .thankyou-msg-container,
503 .elementor-panel .feedback-submit-container {
504 border-radius: 8px;
505 text-align: left;
506 position: relative;
507 margin-bottom: 10px;
508 }
509
510 .elementor-panel .thankyou-msg-container textarea.form-control,
511 .elementor-panel .feedback-submit-container textarea.form-control {
512 width: 100%;
513 background: var(--form-control-backgound);
514 outline: 1px solid var(--border-color);
515 margin-bottom: 10px;
516 border: none;
517 font-weight: 400;
518 font-size: 14px;
519 line-height: 1.6;
520 font-family: system-ui;
521 padding: 8px 8px;
522 }
523
524 .elementor-panel .thankyou-msg-container textarea.form-control::placeholder,
525 .elementor-panel .feedback-submit-container textarea.form-control::placeholder {
526 font-weight: 400;
527 font-size: 14px;
528 line-height: 1.6;
529 color: var(--placeholder-text-color);
530 font-family: system-ui;
531 }
532
533 .elementor-panel .thankyou-msg-container textarea:focus,
534 .elementor-panel .feedback-submit-container textarea:focus {
535 outline-color: #5b4e96;
536 box-shadow: none !important;
537 outline: 1px solid #5b4e96;
538 }
539
540 .elementor-panel .submit-button,
541 .elementor-panel .rating-button {
542 border-radius: 4px;
543 border-width: 1px;
544 width: 100%;
545 border: 1px solid #5b4e96;
546 color: var(--submit-button-color);
547 background: transparent;
548 cursor: pointer;
549 display: flex;
550 align-items: center;
551 justify-content: center;
552 padding: 12px 8px;
553
554 }
555
556 .elementor-panel .submit-button svg,
557 .elementor-panel .rating-button svg {
558 height: 18px;
559 width: 18px;
560 }
561
562 .elementor-panel .help-message {
563 font-weight: 500;
564 font-size: 15px;
565 line-height: 1.6;
566 letter-spacing: 0%;
567 margin-bottom: 10px;
568 margin-top: 0;
569 color: var(--text-color);
570 }
571
572 .elementor-panel p.form-description {
573 font-size: 14px;
574 margin-bottom: 12px;
575 font-family: system-ui;
576 color: var(--description-text-color);
577 line-height: 1.4;
578 }
579
580 .elementor-panel span.close-icon {
581 position: absolute;
582 top: 8px;
583 right: 8px;
584 }
585
586 .elementor-panel span.close-icon svg {
587 height: 12px;
588 width: 12px;
589 cursor: pointer;
590 }
591
592 .elementor-panel span.undo-review {
593 color: #5b4e96;
594 font-weight: 400;
595 text-decoration: none;
596 cursor: pointer;
597 }
598
599 .elementor-panel p.thank-you-message {
600 font-weight: 400;
601 color: var(--secondary-text-color);
602 font-size: 14px;
603 line-height: 1.6;
604 }
605
606 .elementor-panel .chat-button {
607 background-color: #5b4e96;
608 color: white;
609 border: none;
610 padding: 10px 20px;
611 border-radius: 5px;
612 cursor: pointer;
613 font-size: 12px;
614 display: flex;
615 align-items: center;
616 justify-content: center;
617 gap: 5px;
618 font-weight: 500;
619 width: 100%;
620 }
621
622 .elementor-panel .chat-button {
623 background-color: transparent;
624 border: 1px solid #5B4E96;
625 color: #fff;
626 }
627
628 .elementor-panel .rating-button {
629 background-color: transparent;
630 border: 1px solid #5B4E96;
631 color: var(--submit-button-color);
632 margin-top: 20px;
633 }
634
635 .elementor-panel .chat-button svg {
636 width: 18px;
637 height: 18px;
638 }
639
640 .elementor-panel .chat-button:hover {
641 background-color: #4b3293;
642 }
643
644 /* Analytics Section Styles */
645 .elementor-panel .analytics-section {
646 padding: 12px 20px;
647 background: linear-gradient(180deg, #ffffff 28.76%, #fff9fd 66.19%, #faedff 85.34%);
648 border-radius: 8px;
649 margin-bottom: 0;
650 display: flex;
651 gap: 10px;
652 border-top: 1px solid var(--border-color);
653 overflow: visible;
654 width: calc(100% + 30px);
655 margin-left: -15px;
656 border: none;
657 border-radius: 0;
658 margin-top: 0;
659 }
660
661 @media (prefers-color-scheme: dark) {
662 .elementor-panel .analytics-section {
663 background: linear-gradient(180deg, #1F2023 28.76%, #1A1C1F 66.19%, #1F2124 85.34%);
664 }
665 }
666
667 .elementor-panel .analytics-section .analytics-chart {
668 display: flex;
669 align-items: center;
670 gap: 5px;
671 overflow: visible;
672 position: relative;
673 z-index: 10;
674 }
675
676 .elementor-panel .analytics-section .analytics-content {
677 flex: 1;
678 }
679
680 .elementor-panel .analytics-section .analytics-content h3 {
681 font-size: 12px;
682 font-weight: 600;
683 color: var(--text-color);
684 margin: 0 0 6px 0;
685 }
686
687 .elementor-panel .analytics-section .analytics-content p {
688 font-size: 11px;
689 color: var(--secondary-text-color);
690 margin: 0 0 8px 0;
691 line-height: 1.4;
692 }
693
694 .elementor-panel .analytics-section .view-analytics-link {
695 display: inline-flex;
696 align-items: center;
697 gap: 4px;
698 color: #5b4e96;
699 text-decoration: none;
700 font-size: 11px;
701 font-weight: 500;
702 }
703
704 @media (prefers-color-scheme: dark) {
705 .elementor-panel .analytics-section .view-analytics-link {
706 color: #8C73FA;
707 }
708 }
709
710 .elementor-panel .analytics-section .view-analytics-link:hover {
711 color: #4b3293;
712 }
713
714 @media (prefers-color-scheme: dark) {
715 .elementor-panel .analytics-section .view-analytics-link:hover {
716 color: #A89BFF;
717 }
718 }
719
720 .elementor-panel .mini-pie-chart {
721 width: 70px;
722 height: 70px;
723 overflow: visible;
724 position: relative;
725 z-index: 10;
726 }
727 </style>
728
729 <script>
730 document.addEventListener("DOMContentLoaded", function() {
731 if (typeof jQuery === 'undefined') {
732 console.error("❌ jQuery is not loaded!");
733 return;
734 }
735
736 jQuery(document).ready(function($) {
737
738 let message = ""; // Store the thank-you message state
739 let rating = 0; // Store rating state
740 let showThank = localStorage.getItem("feedbackSubmitted") ? 1 : 0; // Store rating state
741 let targetNode = null; // Store reference to the Elementor controls section
742
743 const currentUser = <?php echo json_encode(wp_get_current_user()->data); ?>;
744 const isProActive = <?php echo json_encode(is_plugin_active('embedpress-pro/embedpress-pro.php')); ?>;
745 const isEmbedpressFeedbackSubmited = <?php echo json_encode(get_option('embedpress_feedback_submited')); ?>;
746
747 const turnOffRattingHelp = <?php echo json_encode(Helper::get_options_value('turn_off_rating_help')); ?>;
748
749 function handleRating(selectedRating) {
750
751 rating = selectedRating;
752
753 $(".star").each(function() {
754 const starValue = $(this).data("rating");
755 $(this).attr("fill", starValue <= rating ? "#FFD700" : "#B1B8C2"); // Gold for selected, Grey for unselected
756 });
757
758 if (rating == 5) {
759 // Only send the rating, don't show thank you message yet
760 // Thank you message will be shown after successful API response
761 sendFiveStarRating();
762 } else {
763 // For ratings less than 5, show the form
764 setTimeout(() => {
765 message = "Please describe your issue in details.";
766 renderUpsellSection();
767 }, 500);
768 }
769
770 }
771
772 function setMessage(value) {
773 message = value;
774 renderUpsellSection();
775 }
776
777 function handleSubmit(event) {
778 event.preventDefault();
779
780 const submitButton = event.target.querySelector('.submit-button');
781 submitButton.disabled = true; // Disable the button
782 submitButton.textContent = 'Sending...'; // Update button text
783
784 const formData = new FormData(event.target);
785 const data = {
786 name: currentUser.display_name,
787 email: currentUser.user_email,
788 rating: rating,
789 message: formData.get('message')
790 };
791
792 fetch('<?php echo esc_url_raw(rest_url('embedpress/v1/send-feedback')); ?>', {
793 method: 'POST',
794 headers: {
795 'Content-Type': 'application/json',
796 'X-WP-Nonce': wpApiSettings.nonce
797 },
798 body: JSON.stringify(data)
799 })
800 .then(response => {
801 // Check if response is ok (status 200-299)
802 if (!response.ok) {
803 return response.json().then(errorData => {
804 throw new Error(errorData.message || 'HTTP error! status: ' + response.status);
805 });
806 }
807 return response.json();
808 })
809 .then(data => {
810 // Success - show thank you message
811 submitButton.disabled = false; // Re-enable the button
812 submitButton.textContent = 'Send'; // Reset button text
813 showThank = 1;
814
815 localStorage.setItem("feedbackSubmitted", "true");
816 renderUpsellSection();
817
818 setTimeout(() => {
819 $(".thankyou-msg-container").fadeOut(500, function() {
820 $(this).remove();
821 });
822 }, 3000); // Disappear after 3 seconds
823
824 })
825 .catch(error => {
826 console.error('Error:', error);
827 submitButton.disabled = false; // Re-enable the button
828 submitButton.textContent = 'Send'; // Reset button text
829
830 // Show user-friendly error message
831 const errorMessage = error.message || 'Failed to send feedback. Please try again.';
832 alert(errorMessage);
833 });
834 };
835
836 function sendFiveStarRating() {
837 const data = {
838 name: currentUser.display_name,
839 email: currentUser.user_email,
840 rating: '5',
841 message: ''
842 };
843
844 fetch('<?php echo esc_url_raw(rest_url('embedpress/v1/send-feedback')); ?>', {
845 method: 'POST',
846 headers: {
847 'Content-Type': 'application/json',
848 'X-WP-Nonce': wpApiSettings.nonce
849 },
850 body: JSON.stringify(data)
851 })
852 .then(response => {
853 // Check if response is ok (status 200-299)
854 if (!response.ok) {
855 return response.json().then(errorData => {
856 throw new Error(errorData.message || 'HTTP error! status: ' + response.status);
857 });
858 }
859 return response.json();
860 })
861 .then(data => {
862 // Success - show thank you message only on successful response
863 showThank = 1;
864 localStorage.setItem("feedbackSubmitted", "true");
865 renderUpsellSection();
866
867 })
868 .catch(error => {
869 console.error('Error:', error);
870
871 // Show user-friendly error message
872 const errorMessage = error.message || 'Failed to send feedback. Please try again.';
873 alert(errorMessage);
874 });
875 }
876
877 function renderUpsellSection() {
878 if (!targetNode) return;
879
880 $(".plugin-rating").remove(); // Remove previous upsell section
881
882
883 const thnkMsgHeading = rating < 5 ? 'We appreciate it!' : 'We’re glad that you liked us! 😍';
884 const thnkMsgDsc = rating < 5 ? 'A heartfelt gratitude for managing the time to share your thoughts with us' : 'If you don’t mind, could you take 30 seconds to review us on WordPress? Your feedback will help us improve and grow. Thank you in advance! 🙏';
885
886 let upsellHtml = `
887 <div class="plugin-rating">
888
889 <!-- Advanced Analytics Section -->
890 <div class="analytics-section">
891 <div class="analytics-chart">
892 <div id="mini-pie-chart-elementor" class="mini-pie-chart"></div>
893 </div>
894 <div class="analytics-content">
895 <h3>Advanced Analytics</h3>
896 <p>Get full analytics on how your embeds are performing.</p>
897 <a href="<?php echo admin_url('admin.php?page=embedpress-analytics'); ?>" class="view-analytics-link">
898 View Analytics
899 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
900 <path d="M6 12L10 8L6 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
901 </svg>
902 </a>
903 </div>
904 </div>
905
906 ${turnOffRattingHelp && false ? `
907 <div class="rating-chat-content">
908 ${!isEmbedpressFeedbackSubmited ? `
909 ${((rating && rating == 5) || showThank) ? `
910 <div class="thankyou-msg-container">
911
912 <h5 class="help-message">${thnkMsgHeading}</h5>
913 <p class="thank-you-message">${thnkMsgDsc}</p>
914
915 ${rating == 5 ? `
916 <button class="rating-button">
917 Rate the Plugin
918 <svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
919 <path d="M3.75 2.083 6.25 5l-2.5 2.917" stroke="#5B4E96" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />
920 </svg>
921 </button>
922 ` : ''}
923 </div>
924 ` : rating && rating < 5 ? `
925 <div class="feedback-submit-container">
926 <h5 class="help-message">Help us make it better!</h5>
927 <p class="form-description">Please share what went wrong with The EmbedPress so that we can improve further*</p>
928 <form id="feedback-form">
929 <div class="form-group">
930 <textarea name="message" placeholder="Describe your issue in details" type="text" rows="4" class="form-control" required></textarea>
931 </div>
932 <div class="form-group">
933 <button class="submit-button" type="submit">Send</button>
934 </div>
935 </form>
936 </div>
937 ` : `
938 <h4>Rate EmbedPress</h4>
939 <div class="stars">
940 ${[1, 2, 3, 4, 5].map(i => `
941 <svg class="star" data-rating="${i}" width="20" height="18.667" viewBox="0 0 20 18.667" fill="none" xmlns="http://www.w3.org/2000/svg"
942 style={{ cursor: "pointer", transition: "fill 0.2s ease-in-out" }}>
943 <g clip-path="url(#a)">
944 <path d="m7.3 5.709-4.963.72-.087.017a.777.777 0 0 0-.343 1.309l3.595 3.499-.848 4.943-.009.087a.777.777 0 0 0 1.139.733l4.437-2.333 4.428 2.333.077.036a.777.777 0 0 0 1.053-.855l-.849-4.944 3.596-3.5.061-.067a.777.777 0 0 0-.493-1.259l-4.961-.72-2.218-4.495a.777.777 0 0 0-1.396 0z"/>
945 </g>
946 <defs><clipPath id="a"><path fill="#fff" d="M.888 0h18.667v18.667H.888z"/></clipPath></defs>
947 </svg>
948 `).join('')}
949 </div>
950 `}
951 ` : ''}
952
953 <p style="font-weight: 500">Need help? We're here</p>
954 <a href="https://embedpress.com/?support=chat" target="_blank" class="chat-button">
955 <svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)" fill="#fff"><path d="M7.93.727H1.555C.97.727.5 1.198.5 1.782V6c0 .584.471 1.055 1.055 1.055h.351V8.11c0 .254.263.438.52.31.008-.008.022-.008.029-.015 1.934-1.297 1.5-1.008 1.933-1.294a.35.35 0 0 1 .19-.056H7.93c.583 0 1.054-.47 1.054-1.055V1.782c0-.584-.47-1.055-1.054-1.055M5.117 4.946h-2.86c-.463 0-.465-.703 0-.703h2.86c.464 0 .466.703 0 .703m2.11-1.406h-4.97c-.463 0-.465-.704 0-.704h4.97c.463 0 .465.704 0 .704" /><path d="M11.445 3.54H9.687V6c0 .97-.787 1.758-1.757 1.758H4.684l-.668.443v.612c0 .584.47 1.055 1.054 1.055h3.457l2.018 1.35c.276.153.549-.033.549-.296V9.868h.351c.584 0 1.055-.471 1.055-1.055V4.594c0-.583-.471-1.054-1.055-1.054" /></g><defs><clipPath id="a"><path fill="#fff" d="M.5 0h12v12H.5z" /></clipPath></defs></svg>
956 Let’s Chat
957 </a>
958 </div>` : ''}
959
960 <div class="rating-chat-content">
961 <p style="font-weight: 500">Need help? We're here</p>
962 <a href="https://embedpress.com/?support=chat" target="_blank" class="chat-button">
963 <svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)" fill="#fff"><path d="M7.93.727H1.555C.97.727.5 1.198.5 1.782V6c0 .584.471 1.055 1.055 1.055h.351V8.11c0 .254.263.438.52.31.008-.008.022-.008.029-.015 1.934-1.297 1.5-1.008 1.933-1.294a.35.35 0 0 1 .19-.056H7.93c.583 0 1.054-.47 1.054-1.055V1.782c0-.584-.47-1.055-1.054-1.055M5.117 4.946h-2.86c-.463 0-.465-.703 0-.703h2.86c.464 0 .466.703 0 .703m2.11-1.406h-4.97c-.463 0-.465-.704 0-.704h4.97c.463 0 .465.704 0 .704" /><path d="M11.445 3.54H9.687V6c0 .97-.787 1.758-1.757 1.758H4.684l-.668.443v.612c0 .584.47 1.055 1.054 1.055h3.457l2.018 1.35c.276.153.549-.033.549-.296V9.868h.351c.584 0 1.055-.471 1.055-1.055V4.594c0-.583-.471-1.054-1.055-1.054" /></g><defs><clipPath id="a"><path fill="#fff" d="M.5 0h12v12H.5z" /></clipPath></defs></svg>
964 Let’s Chat
965 </a>
966 </div>
967
968 ${!isProActive ? `
969 <div class="upgrade-box">
970 <h5>Want to explore more?</h5>
971 <p>Dive in and discover all the premium features</p>
972 <a href="https://embedpress.com/in/unlock-elementor-contorls" target="_blank" class="upgrade-link">Upgrade to PRO</a>
973 </div>` : ''}
974 </div>
975 `;
976
977 $(upsellHtml).insertAfter(targetNode);
978
979
980
981 let currentRating = 0; // Store the selected rating
982 $(".star").attr("fill", "#FFD700");
983
984 $(".star").on("mouseenter", function() {
985 let hoverRating = $(this).data("rating");
986
987 $(".star").each(function() {
988 $(this).attr("fill", $(this).data("rating") <= hoverRating ? "#FFD700" : "#B1B8C2");
989 });
990 });
991
992 $(".star").on("mouseleave", function() {
993 $(".star").each(function() {
994 $(this).attr("fill", $(this).data("rating") <= currentRating ? "#FFD700" : "#B1B8C2");
995 });
996 });
997
998 $(".star").on("click", function() {
999 currentRating = $(this).data("rating");
1000
1001 $(".star").each(function() {
1002 $(this).attr("fill", $(this).data("rating") <= currentRating ? "#FFD700" : "#B1B8C2");
1003 });
1004
1005 handleRating(currentRating);
1006 });
1007
1008
1009
1010 $(".rating-button").on("click", function() {
1011 window.open('https://wordpress.org/support/plugin/embedpress/reviews/#new-post')
1012 });
1013
1014 $("#feedback-form").on("submit", handleSubmit);
1015
1016 // Initialize Mini Pie Chart
1017 initMiniPieChart();
1018 }
1019
1020 function initMiniPieChart() {
1021 const chartContainer = document.getElementById('mini-pie-chart-elementor');
1022 if (!chartContainer || typeof am5 === 'undefined') {
1023
1024 // Load amCharts library
1025 const script1 = document.createElement('script');
1026 script1.src = 'https://cdn.amcharts.com/lib/5/index.js';
1027 script1.onload = function() {
1028 const script2 = document.createElement('script');
1029 script2.src = 'https://cdn.amcharts.com/lib/5/percent.js';
1030 script2.onload = function() {
1031 const script3 = document.createElement('script');
1032 script3.src = 'https://cdn.amcharts.com/lib/5/themes/Animated.js';
1033 script3.onload = function() {
1034 renderMiniPieChart();
1035 };
1036 document.head.appendChild(script3);
1037 };
1038 document.head.appendChild(script2);
1039 };
1040 document.head.appendChild(script1);
1041 } else {
1042 renderMiniPieChart();
1043 }
1044 }
1045
1046 function renderMiniPieChart() {
1047 const chartContainer = document.getElementById('mini-pie-chart-elementor');
1048 if (!chartContainer) return;
1049
1050 // Detect dark mode
1051 const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
1052
1053 // Function to render chart with given data
1054 function renderChartWithData(overview) {
1055 const chartContainer = document.getElementById('mini-pie-chart-elementor');
1056 if (!chartContainer) return;
1057
1058 // Get raw values
1059 const totalEmbeds = parseInt(overview.total_embeds) || 0;
1060 const totalViews = parseInt(overview.total_views) || 0;
1061 const totalClicks = parseInt(overview.total_clicks) || 0;
1062 const totalImpressions = parseInt(overview.total_impressions) || 0;
1063
1064 // Check if there's any real data
1065 const hasRealData = totalEmbeds > 0 || totalViews > 0 || totalClicks > 0 || totalImpressions > 0;
1066
1067 // Always show at least 1 for each metric to avoid blank chart
1068 const chartData = [{
1069 category: 'Views',
1070 value: hasRealData ? (totalViews || 1) : 1
1071 },
1072 {
1073 category: 'Clicks',
1074 value: hasRealData ? (totalClicks || 1) : 1
1075 },
1076 {
1077 category: 'Impr',
1078 value: hasRealData ? (totalImpressions || 1) : 1
1079 }
1080 ];
1081
1082 // Create chart
1083 const root = am5.Root.new(chartContainer);
1084 root._logo.dispose();
1085 root.setThemes([am5themes_Animated.new(root)]);
1086 root.animationThemesEnabled = false;
1087
1088 const chart = root.container.children.push(
1089 am5percent.PieChart.new(root, {
1090 layout: root.verticalLayout,
1091 innerRadius: am5.percent(75),
1092 radius: am5.percent(100),
1093 })
1094 );
1095
1096 const series = chart.series.push(
1097 am5percent.PieSeries.new(root, {
1098 valueField: 'value',
1099 categoryField: 'category',
1100 alignLabels: false,
1101 sequencedInterpolation: false,
1102 })
1103 );
1104
1105 series.slices.template.set("toggleKey", "none");
1106 series.labels.template.set("visible", false);
1107 series.ticks.template.set("visible", false);
1108
1109 // Tooltip colors based on theme
1110 const tooltipBg = isDarkMode ? "#1F2124" : "#fff";
1111 const tooltipText = isDarkMode ? "#CBCBD0" : "#333";
1112 const tooltipBorder = isDarkMode ? "#272A2F" : "#e0e0e0";
1113
1114 const tooltip = am5.Tooltip.new(root, {
1115 getFillFromSprite: false,
1116 labelText: `[${tooltipText}]{category}:\{value}[/]`,
1117 paddingTop: 6,
1118 paddingBottom: 6,
1119 paddingLeft: 8,
1120 paddingRight: 8,
1121 autoTextColor: false,
1122 pointerOrientation: "horizontal",
1123 centerX: am5.p50,
1124 centerY: am5.p50,
1125 background: am5.RoundedRectangle.new(root, {
1126 fill: am5.color(tooltipBg),
1127 cornerRadius: 4,
1128 strokeOpacity: 1,
1129 stroke: am5.color(tooltipBorder),
1130 strokeWidth: 1,
1131 shadowColor: am5.color("#000"),
1132 shadowBlur: 4,
1133 shadowOpacity: isDarkMode ? 0.3 : 0.1,
1134 shadowOffsetX: 0,
1135 shadowOffsetY: 2,
1136 }),
1137 });
1138
1139 tooltip.label.setAll({
1140 fill: am5.color(tooltipText),
1141 fontSize: 10,
1142 fontWeight: "400",
1143 textAlign: "center",
1144 oversizedBehavior: "wrap",
1145 maxWidth: 100,
1146 });
1147
1148 // Slice stroke color based on theme
1149 const sliceStroke = isDarkMode ? "#1A1C1F" : "#fff";
1150
1151 series.slices.template.setAll({
1152 tooltip: tooltip,
1153 stroke: am5.color(sliceStroke),
1154 strokeWidth: 1,
1155 cornerRadius: 4,
1156 interactive: true,
1157 hoverable: true,
1158 });
1159
1160 series.slices.template.states.create("hover", {
1161 scale: 1,
1162 });
1163
1164 const colors = ["#5B4E96", "#8C73FA", "#C4B5E8"];
1165 series.get('colors').set('colors', colors.map(c => am5.color(c)));
1166
1167 series.data.setAll(chartData);
1168
1169 // Center text colors based on theme
1170 const centerNumberColor = isDarkMode ? "#ffffff" : "#092161";
1171 const centerLabelColor = isDarkMode ? "#CBCBD0" : "#666";
1172
1173 // Add total embeds number
1174 // Show 1 instead of 0 to avoid blank display
1175 const displayEmbeds = hasRealData ? totalEmbeds : 1;
1176 chart.seriesContainer.children.push(
1177 am5.Label.new(root, {
1178 text: displayEmbeds.toLocaleString(),
1179 centerX: am5.p50,
1180 centerY: am5.p50,
1181 textAlign: "center",
1182 fontSize: 14,
1183 fontWeight: "700",
1184 fill: am5.color(centerNumberColor),
1185 dy: -8,
1186 })
1187 );
1188
1189 // Add "Total Embeds" label
1190 chart.seriesContainer.children.push(
1191 am5.Label.new(root, {
1192 text: "Total Embeds",
1193 centerX: am5.p50,
1194 centerY: am5.p50,
1195 textAlign: "center",
1196 fontSize: 7,
1197 fontWeight: "400",
1198 fill: am5.color(centerLabelColor),
1199 dy: 6,
1200 })
1201 );
1202 }
1203
1204 // Fetch analytics data
1205 const nonce = (typeof wpApiSettings !== 'undefined' && wpApiSettings.nonce) ? wpApiSettings.nonce : '<?php echo wp_create_nonce('wp_rest'); ?>';
1206 fetch('<?php echo esc_url_raw(rest_url('embedpress/v1/analytics/overview')); ?>?date_range=30', {
1207 headers: {
1208 'X-WP-Nonce': nonce
1209 }
1210 })
1211 .then(response => response.json())
1212 .then(result => {
1213 const overview = result.overview || result;
1214 renderChartWithData(overview);
1215 })
1216 .catch(error => {
1217
1218 // Render chart with default values when API fails
1219 renderChartWithData({
1220 total_embeds: 0,
1221 total_views: 0,
1222 total_clicks: 0,
1223 total_impressions: 0
1224 });
1225 });
1226 }
1227
1228 function addUpsellSection(node) {
1229 if (!node) return;
1230
1231 targetNode = node; // Store reference to the correct node
1232 if (!$(".plugin-rating").length) {
1233 renderUpsellSection();
1234 }
1235 }
1236
1237 // MutationObserver to detect Elementor panel changes
1238 const observer = new MutationObserver((mutations) => {
1239 mutations.forEach((mutation) => {
1240 mutation.addedNodes.forEach((node) => {
1241 if ($(node).hasClass("elementor-controls-stack")) {
1242 const elementorControls = node.querySelector("#elementor-controls:has(.elementor-control-embedpress_elementor_content_settings, .elementor-control-embedpress_pdf_content_settings, .elementor-control-embedpress_document_content_settings, .elementor-control-embedpress_calendar_content_settings, .elementor-control-ep_gr_section_place)");
1243
1244 if (elementorControls) {
1245 addUpsellSection(elementorControls);
1246 }
1247 }
1248 });
1249 });
1250 });
1251
1252 // Start observing Elementor panel for changes
1253 const elementorPanel = document.querySelector(".elementor-panel");
1254 if (elementorPanel) {
1255 observer.observe(elementorPanel, {
1256 childList: true,
1257 subtree: true,
1258 });
1259 } else {
1260 }
1261 });
1262 });
1263 </script>
1264
1265 <?php
1266 }
1267 }
1268