PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.5.0
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.5.0
4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 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.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 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.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 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Integrations / Elementor / assets / editor.js
surecart / app / src / Integrations / Elementor / assets Last commit date
container.css 10 months ago editor.css 4 months ago editor.js 1 year ago
editor.js
335 lines
1 let inertElements = [];
2 let currentDocType = '';
3
4 jQuery(window).ready(function () {
5 if (typeof elementor === 'undefined') {
6 return;
7 }
8
9 // Form edit link
10 elementor.channels.editor.on('surecart:form:edit', function (view) {
11 let block_id = view?.container?.settings?.get('sc_form_block');
12
13 if (!block_id) {
14 return;
15 }
16
17 let win = window.open(
18 scElementorData.site_url +
19 `/wp-admin/post.php?post=${block_id}&action=edit`,
20 '_blank'
21 );
22 win.focus();
23 });
24
25 // Form create link
26 elementor.channels.editor.on('surecart:form:create', function () {
27 let win = window.open(
28 scElementorData.site_url +
29 `/wp-admin/post-new.php?post_type=sc_form`,
30 '_blank'
31 );
32 win.focus();
33 });
34
35 /**
36 * When adding a SureCart Widgets, remove the default SureCart block and insert the SureCart template.
37 */
38 for (const template of Object.values(
39 window?.scElementorData?.templates || {}
40 )) {
41 const widgetName = template?.widget_name || '';
42 if (!widgetName) {
43 continue;
44 }
45
46 elementor.hooks.addAction(
47 'panel/open_editor/widget/' + widgetName,
48 function (panel, model, view) {
49 // Remove the default SureCart block by clearing the model.
50 model.destroy();
51
52 // Clear the preview container if it's empty.
53 maybeClearElementorPreview();
54
55 // Insert the SureCart template.
56 insertSureCartTemplate(template);
57 }
58 );
59 }
60
61 /**
62 * Clears the preview container if its first container element and which is empty.
63 */
64 function maybeClearElementorPreview() {
65 const firstContainer =
66 elementor.getPreviewContainer().document?.container?.children?.[0];
67
68 if (
69 0 === firstContainer?.children?.length &&
70 firstContainer?.type === 'container'
71 ) {
72 $e.run('document/elements/empty', {
73 force: true,
74 });
75 }
76 }
77
78 /**
79 * Filter templates by document type and set empty state message.
80 *
81 * @param {jQuery} modal - The modal element
82 */
83 function filterTemplatesAndSetEmptyState(modal) {
84 // Apply document type class for CSS filtering.
85 const docType = elementor.documents.getCurrent()?.config?.type || '';
86 currentDocType = docType;
87 modal.removeClass('surecart-product loop-item').addClass(docType);
88
89 // Get translation strings.
90 const i18n = window?.scElementorData?.i18n || {};
91
92 // Document type to template type mapping.
93 const docTypeMapping = {
94 'surecart-product': {
95 templateType: 'product-form',
96 emptyMessage: i18n?.no_product_form_templates,
97 },
98 'loop-item': {
99 templateType: 'product-card',
100 emptyMessage: i18n?.no_product_card_templates,
101 },
102 };
103
104 // Set empty state message based on document type.
105 const cardContainer = modal.find('.sc-elementor-modal__card-container');
106 const config = docTypeMapping[docType] || {
107 emptyMessage: i18n?.no_templates,
108 };
109 cardContainer.attr('data-empty-message', config.emptyMessage);
110
111 // Filter templates if we have a mapping for this document type.
112 if (config.templateType) {
113 modal.find('.sc-elementor-modal__card').each(function () {
114 const templateType = jQuery(this).data('template-type') || '';
115 jQuery(this).toggle(templateType === config.templateType);
116 });
117 }
118
119 // Check for visible templates after filtering and show empty state if needed.
120 setTimeout(() => {
121 const visibleTemplates = modal.find(
122 '.sc-elementor-modal__card:visible'
123 ).length;
124 if (visibleTemplates === 0) {
125 // Instead of emptying the container, add a message element.
126 if (!cardContainer.find('.sc-elementor-empty-state').length) {
127 const emptyMessage =
128 cardContainer.attr('data-empty-message');
129 cardContainer.html(
130 `<div class="sc-elementor-empty-state">${emptyMessage}</div>`
131 );
132 }
133 } else {
134 // Remove any existing empty state message.
135 cardContainer.find('.sc-elementor-empty-state').remove();
136 }
137 }, 100);
138 }
139
140 /**
141 * Opens the SureCart template modal.
142 */
143 function openModal() {
144 const modal = jQuery('#sc-elementor-modal-dialog');
145 modal.addClass('show');
146
147 // Filter templates and set empty state message.
148 filterTemplatesAndSetEmptyState(modal);
149
150 // Make other items inert.
151 inertElements = [];
152 document
153 .querySelectorAll('body > :not(#sc-elementor-modal-dialog)')
154 .forEach((el) => {
155 if (!el.hasAttribute('inert')) {
156 el.setAttribute('inert', '');
157 inertElements.push(el);
158 }
159 });
160
161 // Get and focus the first focusable element.
162 const firstFocusableElement = modal.find(
163 'button, [href], input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
164 )[0];
165 if (firstFocusableElement) {
166 setTimeout(() => {
167 firstFocusableElement.focus();
168 }, 100);
169 }
170 }
171
172 /**
173 * Closes the SureCart template modal.
174 */
175 function closeModal() {
176 jQuery('#sc-elementor-modal-dialog').removeClass('show');
177 // remove inert attribute from all children of the document
178 inertElements.forEach((el) => {
179 el.removeAttribute('inert');
180 });
181 inertElements = [];
182 }
183
184 /**
185 * Sets up the SureCart template button in the Elementor editor.
186 */
187 function setupSureCartTemplateButton() {
188 const templateAddSection = jQuery('#tmpl-elementor-add-section');
189 if (templateAddSection?.length > 0) {
190 let oldTemplateButton = templateAddSection.html();
191 if (
192 !oldTemplateButton.includes(
193 'elementor-surecart-template-button'
194 )
195 ) {
196 oldTemplateButton = oldTemplateButton.replace(
197 '<div class="elementor-add-section-drag-title',
198 `<# if ( 'loop-item' === elementor.documents.getCurrent()?.config?.type || 'surecart-product' === elementor.documents.getCurrent()?.config?.type ) { #><div class="elementor-add-section-area-button elementor-surecart-template-button" title="SureCart"></div><# } #>
199 <div class="elementor-add-section-drag-title`
200 );
201 templateAddSection.html(oldTemplateButton);
202 }
203 }
204
205 elementor.on('preview:loaded', function () {
206 jQuery(elementor.$previewContents[0].body).on(
207 'click',
208 '.elementor-surecart-template-button',
209 function (event) {
210 event.preventDefault();
211 openModal();
212 }
213 );
214 });
215
216 jQuery(document).on(
217 'click',
218 '#sc-elementor-modal-close, .sc-elementor-modal__overlay',
219 function () {
220 closeModal();
221 }
222 );
223
224 jQuery(document).on('keydown', function (event) {
225 if (event.key === 'Escape') {
226 closeModal();
227 }
228 });
229
230 jQuery(document).on('click', '.sc-elementor-modal__card', function () {
231 // Check if the template is valid for current document type
232 const templateType = jQuery(this).data('template-type') || '';
233 const isValidTemplate =
234 (currentDocType === 'surecart-product' &&
235 templateType === 'product-form') ||
236 (currentDocType === 'loop-item' &&
237 templateType === 'product-card') ||
238 !currentDocType ||
239 !templateType;
240
241 if (!isValidTemplate) {
242 return;
243 }
244
245 closeModal();
246 const templateKey = jQuery(this).data('template-key');
247 insertSureCartTemplate(
248 window?.scElementorData?.templates?.[templateKey]
249 );
250 });
251 }
252
253 // Run initially to set up the SureCart template button.
254 setupSureCartTemplateButton();
255
256 /**
257 * Show our template selection popup when the user is on a SureCart product page.
258 */
259 elementor.on('document:loaded', function () {
260 // document is loaded.
261 if (
262 'surecart-product' !==
263 elementor.documents.getCurrent()?.config?.type
264 ) {
265 return;
266 }
267
268 // If the document has elements, don't show the modal.
269 if (elementor.documents.getCurrent()?.config?.elements?.length !== 0) {
270 return;
271 }
272
273 // close the library.
274 $e.run('library/close');
275
276 // open our modal.
277 openModal();
278 });
279
280 function generateUniqueIds(element) {
281 element.id = elementorCommon.helpers.getUniqueId();
282 if (element?.elements) {
283 // Recursively generate IDs for child elements.
284 element.elements.forEach((childElement) => {
285 generateUniqueIds(childElement);
286 });
287 }
288 return element;
289 }
290
291 function insertSureCartTemplate(template) {
292 // Generate elements with unique IDs.
293 const elements = [];
294
295 // Check if we have the new structure with content property or the old structure
296 const templateContent = template.content || template;
297
298 if (!templateContent || !templateContent.elements) {
299 console.error('Invalid template structure:', template);
300 return;
301 }
302
303 for (let element of templateContent.elements) {
304 element = generateUniqueIds(element);
305 elements.push(element);
306 }
307
308 $e.run('document/elements/create', {
309 container: elementor.getPreviewContainer(),
310 model: {
311 id: elementorCommon.helpers.getUniqueId(),
312 elType: 'container',
313 settings: templateContent?.settings || {},
314 elements,
315 },
316 });
317
318 // If there are outer elements, create a new container for them and add them.
319 // This is useful for templates that have outer elements like extra sections or containers.
320 if (templateContent.outer_elements) {
321 $e.run('document/elements/create', {
322 container: elementor.getPreviewContainer(),
323 model: {
324 id: elementorCommon.helpers.getUniqueId(),
325 elType: 'container',
326 elements: templateContent.outer_elements.map((element) => {
327 element = generateUniqueIds(element);
328 return element;
329 }),
330 },
331 });
332 }
333 }
334 });
335