PluginProbe
Elementor Website Builder – more than just a page builder / 4.0.7
Elementor Website Builder – more than just a page builder v4.0.7
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / modules / interactions / interactions-frontend-handler.php

interactions-frontend-handler.php in Elementor Website Builder – more than just a page builder 4.0.7, at modules/interactions/interactions-frontend-handler.php

217 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\Interactions;
4
5 use Elementor\Plugin;
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit; // Exit if accessed directly.
9 }
10
11 /**
12 * Handles frontend-specific interaction logic including:
13 * - Collecting interactions from document elements during render
14 * - Outputting interaction data as JSON in the page footer
15 *
16 * This class is responsible for the frontend rendering pipeline of interactions,
17 * working with the Interactions_Collector for data storage and Adapter for data transformation.
18 */
19 class Interactions_Frontend_Handler {
20 /**
21 * @var callable|null
22 */
23 private $config_provider;
24
25 public function __construct( $config_provider = null ) {
26 $this->config_provider = is_callable( $config_provider ) ? $config_provider : null;
27 }
28
29 /**
30 * Collect interactions from document elements during frontend render.
31 *
32 * This method is hooked to 'elementor/frontend/builder_content_data' filter
33 * to capture interactions from all documents (header, footer, post content)
34 * as they are rendered.
35 *
36 * @param array $elements_data The document's elements data.
37 * @param int $post_id The document's post ID.
38 * @return array The unmodified elements data (pass-through filter).
39 */
40 public function collect_document_interactions( $elements_data, $post_id ) {
41 // Only collect on frontend, not in editor
42 if ( Plugin::$instance->editor->is_edit_mode() ) {
43 return $elements_data;
44 }
45
46 if ( empty( $elements_data ) || ! is_array( $elements_data ) ) {
47 return $elements_data;
48 }
49
50 $collector = Interactions_Collector::instance();
51
52 // Recursively collect interactions from all elements
53 $this->collect_interactions_recursive( $elements_data, $collector );
54
55 return $elements_data;
56 }
57
58 /**
59 * Recursively iterate through all elements and collect interactions.
60 *
61 * @param array $elements Array of element data.
62 * @param Interactions_Collector $collector The collector instance.
63 */
64 private function collect_interactions_recursive( $elements, $collector ) {
65 if ( ! is_array( $elements ) ) {
66 return;
67 }
68
69 foreach ( $elements as $element ) {
70 if ( ! is_array( $element ) ) {
71 continue;
72 }
73
74 // Check if this element has interactions
75 if ( ! empty( $element['id'] ) && isset( $element['interactions'] ) ) {
76 $element_id = $element['id'];
77 $interactions = $element['interactions'];
78
79 // Decode if it's a JSON string
80 if ( is_string( $interactions ) ) {
81 $decoded = json_decode( $interactions, true );
82 if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
83 $interactions = $decoded;
84 } else {
85 $interactions = null;
86 }
87 }
88
89 // Normalize the interactions format - ensure we have items array
90 if ( is_array( $interactions ) ) {
91 // If interactions has 'items' key, it's already in the right format
92 // If not, check if it's a direct array of items or has other structure
93 if ( ! isset( $interactions['items'] ) ) {
94 // Check if this looks like a direct array of interaction items
95 // (first element has 'trigger' or 'animation' or '$$type')
96 $first_item = reset( $interactions );
97 if ( is_array( $first_item ) && ( isset( $first_item['trigger'] ) || isset( $first_item['animation'] ) || isset( $first_item['$$type'] ) ) ) {
98 // It's a direct array of items, wrap it
99 $interactions = [ 'items' => $interactions ];
100 }
101 }
102
103 // Register with collector if we have valid items
104 $items = $interactions['items'] ?? [];
105 if ( ! empty( $items ) || ! empty( $interactions ) ) {
106 $collector->register( $element_id, $interactions );
107 }
108 }
109 }
110
111 // Recursively process child elements
112 if ( ! empty( $element['elements'] ) && is_array( $element['elements'] ) ) {
113 $this->collect_interactions_recursive( $element['elements'], $collector );
114 }
115 }
116 }
117
118 /**
119 * Output collected interaction data as a JSON script tag in the footer.
120 *
121 * This method is hooked to 'wp_footer' to output all collected interactions
122 * as a centralized JSON data block that the frontend JavaScript can consume.
123 */
124 public function print_interactions_data() {
125 // Only output on frontend, not in editor
126 if ( Plugin::$instance->editor->is_edit_mode() ) {
127 return;
128 }
129
130 $collector = Interactions_Collector::instance();
131 $all_interactions = $collector->get_all();
132
133 if ( empty( $all_interactions ) ) {
134 return;
135 }
136
137 // Format: array of elements, each with elementId, dataId, and cleaned interactions
138 $elements_with_interactions = [];
139 foreach ( $all_interactions as $element_id => $interactions ) {
140 $items = $this->extract_interaction_items( $interactions );
141
142 if ( empty( $items ) ) {
143 continue;
144 }
145
146 // Build element entry with elementId, dataId, and cleaned interactions array
147 $elements_with_interactions[] = [
148 'elementId' => $element_id,
149 'dataId' => $element_id,
150 'interactions' => $items,
151 ];
152 }
153
154 if ( empty( $elements_with_interactions ) ) {
155 return;
156 }
157
158 $this->enqueue_interactions_assets();
159
160 // Output as JSON script tag
161 $json_data = wp_json_encode( $elements_with_interactions, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
162
163 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON data is already encoded
164 echo '<script type="application/json" id="' . Module::SCRIPT_ID_INTERACTIONS_DATA . '">' . $json_data . '</script>';
165 }
166
167 /**
168 * Extract interaction items from various data formats.
169 *
170 * Handles multiple formats:
171 * - v1 format: { items: [...] }
172 * - v2 format with $$type: { items: { $$type: '...', value: [...] } }
173 * - Direct arrays: [{ trigger: ..., animation: ... }, ...]
174 *
175 * @param array $interactions The interactions data.
176 * @return array The extracted items array.
177 */
178 private function extract_interaction_items( $interactions ) {
179 if ( ! is_array( $interactions ) ) {
180 return [];
181 }
182
183 // Check if it has 'items' key (standard format)
184 if ( isset( $interactions['items'] ) ) {
185 $items = $interactions['items'];
186
187 return is_array( $items ) ? $items : [];
188 }
189
190 // Check if interactions itself is a direct array of items
191 // (first element has interaction-related keys)
192 $first_item = reset( $interactions );
193 if ( is_array( $first_item ) && (
194 isset( $first_item['trigger'] ) ||
195 isset( $first_item['animation'] ) ||
196 isset( $first_item['$$type'] )
197 ) ) {
198 return $interactions;
199 }
200
201 return [];
202 }
203
204 private function enqueue_interactions_assets() {
205 wp_enqueue_script( Module::HANDLE_MOTION_JS );
206 wp_enqueue_script( Module::HANDLE_FRONTEND );
207
208 $config = $this->config_provider ? call_user_func( $this->config_provider ) : [];
209
210 wp_localize_script(
211 Module::HANDLE_FRONTEND,
212 Module::JS_CONFIG_OBJECT,
213 $config
214 );
215 }
216 }
217