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 |