/** * Save manager - handleInlineSave and showInlineSuccess * * Reads (via globals set by earlier modules): * SFE.Context - .activeEditor (r/w), .draftEditState (r/w), * .pageRevisionToken (r/w), .uuidMap, * .actionBar * SFE.ElementState - .ElementState * SFE.Api - .apiCall * SFE.BlockSerializer - .buildBlockPayload * SFE.ListBlockTracker * SFE.ElementUpdater - .applyNewHTML * SFE.TIMING * SFE.SaveHelpers - .setButtonLoading, .clearButtonLoading, * .lockSaveUI, .unlockSaveUI, * .createSuccessElement, * .handleRevisionConflict, * .updatePageRevisionToken * SFE.PostLockManager - .ensureLock * SFE.ManagerData - .postId, .permissions * SFE.restoreElementContent - set by EditorLifecycle.js * SFE.rebindChildren - set by EditorLifecycle.js * SFE.attachActionBarToElement - set by frontend-inline-edit.js (via HoverManager) * * Exposes: SFE.SaveManager { handleInlineSave, showInlineSuccess } */ (function() { 'use strict'; window.MWP = window.MWP || {}; window.MWP.SFE = window.MWP.SFE || {}; const SFE = window.MWP.SFE; SFE.ManagerData = SFE.ManagerData || {}; function getSaveHooks() { return SFE.SaveHooks || null; } function getProDraftApi() { return SFE.PRO?.DraftApi || null; } function resolveSaveStrategy(editorState) { let strategy = editorState?.saveStrategy || 'single'; const hooks = getSaveHooks(); if (hooks && typeof hooks.resolveStrategy === 'function') { try { const override = hooks.resolveStrategy({ strategy, editorState }); if (override === 'single' || override === 'batch') { strategy = override; } } catch (error) { console.warn('FrontEdit: save strategy hook failed', error); } } return strategy; } async function runSaveHook(hookName, payload) { const hooks = getSaveHooks(); if (!hooks || typeof hooks[hookName] !== 'function') return; try { await hooks[hookName](payload); } catch (error) { console.warn(`FrontEdit: save hook ${hookName} failed`, error); } } /** * Read the cached published baseline HTML for a block, if one has been * captured during this page lifecycle. * * @param {string} uuid Block UUID. * @returns {string} Cached outer HTML snapshot or an empty string. */ function getPublishedBaselineHTML(uuid) { if (!uuid || !SFE.Context?.uuidMap?.[uuid]) return ''; const cached = SFE.Context.uuidMap[uuid].publishedBaselineOuterHTML; return (typeof cached === 'string' && cached.trim()) ? cached : ''; } /** * Restore the draft-submit display element from the cached published * baseline when available, falling back to the per-editor restore snapshot. * * @param {Object} editorState Editor state for the active save session. * @returns {Element} The restored element. */ function restoreDraftSubmittedElement(editorState) { const baselineHTML = getPublishedBaselineHTML(editorState?.uuid); if (!baselineHTML) { return SFE.restoreElementContent(editorState.element, editorState); } const temp = document.createElement('div'); temp.innerHTML = baselineHTML; const restoredElement = temp.firstElementChild; if (!restoredElement || !editorState?.element?.parentNode) { return SFE.restoreElementContent(editorState.element, editorState); } editorState.element.parentNode.replaceChild(restoredElement, editorState.element); return restoredElement; } /** * Destroy persisted block edit sessions after one successful save lifecycle. * * Saving ends the block session entirely, so any cached undo history for the * saved UUIDs must be discarded before the next editor open starts fresh. * * @param {string|string[]} uuids One UUID or a list of UUIDs to destroy. * @returns {void} */ function destroySavedBlockSessions(uuids) { const list = Array.isArray(uuids) ? uuids : [uuids]; if (typeof SFE.destroyPersistedBlockEditSessions === 'function') { SFE.destroyPersistedBlockEditSessions(list); return; } if (typeof SFE.destroyPersistedBlockEditSession !== 'function') { return; } list.forEach((uuid) => { SFE.destroyPersistedBlockEditSession(uuid); }); } async function handleInlineSave(editorState) { const ctx = SFE.Context; const publicApiBridge = SFE.PublicApiBridge || null; const { apiCall, ensureResolvedMediaAttributes } = SFE.Api; const { buildBlockPayload } = SFE.BlockSerializer; const { setButtonLoading, clearButtonLoading, lockSaveUI, unlockSaveUI, handleRevisionConflict, updatePageRevisionToken, fetchRenderedBlockHTML, reloadAfterRefreshFailure } = SFE.SaveHelpers; const TIMING = SFE.TIMING; const postId = SFE.ManagerData.postId; const perms = SFE.ManagerData.permissions || {}; const uuidMap = ctx.uuidMap; const batchManager = SFE.BatchEditManager || null; const proDraftApi = getProDraftApi(); const saveStrategy = resolveSaveStrategy(editorState); const strategyPayload = { strategy: saveStrategy, editorState }; if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:before', editorState, { source: 'sfe', saveStrategy, }); } await runSaveHook('beforeSave', strategyPayload); if ( saveStrategy === 'batch' && batchManager && typeof batchManager.isSessionActive === 'function' && batchManager.isSessionActive() ) { const saveBtn = editorState.actionsContainer?.querySelector('.mwp-sfe-btn-primary-inline') || null; const cancelBtn = editorState.actionsContainer?.querySelector('.mwp-sfe-btn-secondary-inline') || null; if (cancelBtn) cancelBtn.disabled = true; try { await batchManager.handleBatchSave(saveBtn); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:after', editorState, { source: 'sfe', saveStrategy, success: true, }); } await runSaveHook('afterSave', { ...strategyPayload, success: true }); } finally { // Re-enable the Cancel button in case save failed before showInlineSuccess could clean up the editor. if (cancelBtn) cancelBtn.disabled = false; } return; } // Build canonical block payload via wp.blocks API (serialize → parse round-trip) try { if (typeof ensureResolvedMediaAttributes === 'function') { await ensureResolvedMediaAttributes(editorState); } } catch (error) { console.error('FrontEdit: resolveMediaAttributes failed:', error); alert('Error saving: ' + error.message); return; } let payloadContent; try { const blockStructure = buildBlockPayload( editorState.element, editorState ); if ( !blockStructure ) { alert( 'Error saving: Could not build block structure. Please refresh and try again.' ); return; } payloadContent = JSON.stringify( blockStructure ); } catch ( error ) { console.error( 'FrontEdit: buildBlockPayload failed:', error ); alert( 'Error saving: ' + error.message ); return; } // Show loading state const saveBtn = editorState.actionsContainer.querySelector('.mwp-sfe-btn-primary-inline'); const cancelBtn = editorState.actionsContainer.querySelector('.mwp-sfe-btn-secondary-inline'); const originalText = saveBtn.textContent; lockSaveUI(saveBtn); if (cancelBtn) cancelBtn.disabled = true; // Check if we're editing a draft const draftEditState = ctx.draftEditState; const isEditingDraft = draftEditState && draftEditState.draftElement === editorState.element; saveBtn.textContent = perms.can_publish ? 'Saving...' : 'Submitting...'; // Helper to restore save button to clickable state on any failure path const restoreSaveBtn = () => { unlockSaveUI(saveBtn); saveBtn.textContent = originalText; if (cancelBtn) cancelBtn.disabled = false; }; // Handle based on permissions and draft state if (perms.can_publish) { if (!await SFE.PostLockManager.ensureLock()) { restoreSaveBtn(); return; } try { if (isEditingDraft && draftEditState.version) { if (!proDraftApi || typeof proDraftApi.approveWithEdit !== 'function') { throw new Error('Draft approve-with-edit is unavailable.'); } const approveResult = await proDraftApi.approveWithEdit({ post_id: postId, element_uuid: editorState.uuid, version: draftEditState.version, new_content: payloadContent, page_revision_token: ctx.pageRevisionToken // conflict detection }); // Update token so subsequent saves don't false-conflict against our own revision. updatePageRevisionToken(approveResult); ctx.draftEditState = null; } else { const applyResult = await apiCall('/apply', { post_id: postId, element_uuid: editorState.uuid, handler_id: editorState.handler.id, edit_content: payloadContent, page_revision_token: ctx.pageRevisionToken }); // Update token to the revision the plugin just created, so subsequent // inline saves in this session don't false-conflict against our own revision. updatePageRevisionToken(applyResult); } const refreshedHTML = await fetchRenderedBlockHTML(editorState.uuid, { force: true }); if (refreshedHTML) { destroySavedBlockSessions(editorState.uuid); showInlineSuccess(editorState, 'Changes Saved', refreshedHTML); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:after', editorState, { source: 'sfe', saveStrategy, success: true, }); } await runSaveHook('afterSave', { ...strategyPayload, success: true }); } else { reloadAfterRefreshFailure('FrontEdit: Save succeeded but refreshed block HTML was unavailable.'); return; } } catch (error) { if (error.message === 'POST_LOCKED') { await SFE.PostLockManager?.handleLockedError(error); restoreSaveBtn(); return; } if (error.message === 'BLOCK_HTML_REFRESH_FAILED') { reloadAfterRefreshFailure('FrontEdit: Save succeeded but refreshed block HTML could not be extracted from the post render.'); return; } if (error.message === 'REVISION_CONFLICT') { if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:error', editorState, { source: 'sfe', saveStrategy, message: 'REVISION_CONFLICT', }); } const shouldRetry = await handleRevisionConflict(restoreSaveBtn); if (!shouldRetry) return; // Retry without token - server skips conflict check when token is absent. // Use the same endpoint that originally triggered the conflict. try { if (isEditingDraft && draftEditState.version) { if (!proDraftApi || typeof proDraftApi.approveWithEdit !== 'function') { throw new Error('Draft approve-with-edit is unavailable.'); } await proDraftApi.approveWithEdit({ post_id: postId, element_uuid: editorState.uuid, version: draftEditState.version, new_content: payloadContent // No token - force approve }); ctx.draftEditState = null; } else { await apiCall('/apply', { post_id: postId, element_uuid: editorState.uuid, handler_id: editorState.handler.id, edit_content: payloadContent // No token - force save }); } const refreshedHTML = await fetchRenderedBlockHTML(editorState.uuid, { force: true }); if (refreshedHTML) { destroySavedBlockSessions(editorState.uuid); showInlineSuccess( editorState, 'Changes Saved', refreshedHTML, false, 'success', { reloadAfterSuccess: true } ); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:after', editorState, { source: 'sfe', saveStrategy, success: true, }); } await runSaveHook('afterSave', { ...strategyPayload, success: true }); } else { reloadAfterRefreshFailure('FrontEdit: Retry save succeeded but refreshed block HTML was unavailable.'); return; } } catch (retryError) { if (retryError.message === 'BLOCK_HTML_REFRESH_FAILED') { reloadAfterRefreshFailure('FrontEdit: Retry save succeeded but refreshed block HTML could not be extracted from the post render.'); return; } console.error('Save failed on retry:', retryError); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:error', editorState, { source: 'sfe', saveStrategy, message: String(retryError.message || 'SAVE_FAILED'), }); } if ( retryError.message && retryError.message.indexOf( '403' ) !== -1 ) { alert( 'You no longer have permission to save. Your publish access may have been changed. Please refresh the page.' ); } else { alert('Error: ' + retryError.message); } restoreSaveBtn(); } return; } console.error('Save failed:', error); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:error', editorState, { source: 'sfe', saveStrategy, message: String(error.message || 'SAVE_FAILED'), }); } if ( error.message && error.message.indexOf( '403' ) !== -1 ) { alert( 'You no longer have permission to save. Your publish access may have been changed. Please refresh the page.' ); } else { alert('Error: ' + error.message); } restoreSaveBtn(); } } else if (perms.can_draft) { try { if (!proDraftApi || typeof proDraftApi.submitDraft !== 'function') { throw new Error('Draft submission is unavailable.'); } const data = await proDraftApi.submitDraft({ post_id: postId, element_uuid: editorState.uuid, handler_id: editorState.handler.id, edit_content: payloadContent }); editorState.element.classList.add('mwp-sfe-status-pending'); if (uuidMap[editorState.uuid]) { uuidMap[editorState.uuid].is_pending = true; uuidMap[editorState.uuid].pending_info = { version: data.entry.version, user: data.entry.user_name, date: new Date(data.entry.timestamp * 1000).toLocaleString() }; } showInlineSuccess( editorState, `Draft Submitted (Version ${data.entry.version})`, null, true, 'warning' ); destroySavedBlockSessions(editorState.uuid); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:after', editorState, { source: 'sfe', saveStrategy, success: true, }); } await runSaveHook('afterSave', { ...strategyPayload, success: true }); } catch (error) { console.error('Draft submit failed:', error); if (publicApiBridge) { publicApiBridge.emitSaveEvent('save:error', editorState, { source: 'sfe', saveStrategy, message: String(error.message || 'SAVE_FAILED'), }); } alert('Error: ' + error.message); restoreSaveBtn(); } } } /** * Show success message and update element * Handles both simple comments and complex save/draft operations */ function showInlineSuccess(editorState, message, newHTML, isDraft = false, variant = 'success', options = {}) { const ctx = SFE.Context; const TIMING = SFE.TIMING; const ListBlockTracker = SFE.ListBlockTracker; const { ElementState } = SFE.ElementState; const { createSuccessElement } = SFE.SaveHelpers; const { applyNewHTML } = SFE.ElementUpdater; const actionBar = ctx.actionBar; const uuidMap = ctx.uuidMap; const shouldReloadAfterSuccess = !!options.reloadAfterSuccess; const reloadAfterSuccess = shouldReloadAfterSuccess && SFE.SaveHelpers && typeof SFE.SaveHelpers.reloadPageWithGuardBypass === 'function' ? SFE.SaveHelpers.reloadPageWithGuardBypass : null; // Detect if this is a simple comment (minimal editorState) vs complex save/draft const isComment = !editorState.actionsContainer; // Create and position success message (common to all types) const successDiv = createSuccessElement(message, variant); document.body.appendChild(successDiv); // Force a reflow before allowing transitions again successDiv.offsetHeight; successDiv.style.transition = ''; // === SIMPLE PATH: Comments === if (isComment) { // Comment cleanup already done by exitCommentMode // Just show message, wait, and fade out setTimeout(() => { successDiv.style.opacity = '0'; setTimeout(() => successDiv.remove(), TIMING.SUCCESS_FADE); }, TIMING.SUCCESS_DISPLAY); return; } // === COMPLEX PATH: Save/Draft Operations === // Remove any wp-elements-* styles injected into for this draft preview. if (editorState.uuid) { document.querySelectorAll(`style[data-mwp-sfe-draft-uuid="${editorState.uuid}"]`) .forEach(el => el.remove()); } // Store reference to this editor session for validation const thisEditorSession = editorState; // Cleanup editor resources if (editorState.resizeObserver) { editorState.resizeObserver.disconnect(); } if (editorState.toolbarContainer) editorState.toolbarContainer.remove(); if (editorState.actionsContainer) { editorState.actionsContainer.style.display = 'none'; } if (editorState.previewOverlay) editorState.previewOverlay.remove(); if (editorState.updatePositions) { window.removeEventListener('scroll', editorState.updatePositions, true); window.removeEventListener('resize', editorState.updatePositions); } if (editorState.updatePreviewPosition) { window.removeEventListener('scroll', editorState.updatePreviewPosition, true); } if (editorState.escapeHandler) { document.removeEventListener('keydown', editorState.escapeHandler); } if (editorState.cleanupFocus) editorState.cleanupFocus(); const switchTarget = editorState._mwpComponentSwitchTarget || editorState.element || null; if (editorState.componentSwitchHandler && switchTarget) { switchTarget.removeEventListener('mousedown', editorState.componentSwitchHandler, true); delete editorState.componentSwitchHandler; } if (editorState.componentClickGuard && switchTarget) { switchTarget.removeEventListener('click', editorState.componentClickGuard, true); delete editorState.componentClickGuard; } if (editorState.componentTabHandler && switchTarget) { switchTarget.removeEventListener('keydown', editorState.componentTabHandler, true); delete editorState.componentTabHandler; } delete editorState._mwpComponentSwitchTarget; if (editorState._mwpSchemaMediaSession && typeof editorState._mwpSchemaMediaSession.cleanup === 'function') { editorState._mwpSchemaMediaSession.cleanup({ preserveChanges: true }); delete editorState._mwpSchemaMediaSession; } if (Array.isArray(editorState.editableComponents)) { const elementPrep = SFE.ElementPrep || null; const textEditorHost = SFE.SchemaEditorHost?.resolveTextEditorHost?.(editorState) || null; editorState.editableComponents.forEach(component => { if (!component || !component.element) return; component.element.classList.remove( 'mwp-sfe-inline-editor', 'mwp-sfe-component-active', 'mwp-sfe-editable-component' ); if (elementPrep && typeof elementPrep.pruneEmptyClassAttribute === 'function') { elementPrep.pruneEmptyClassAttribute(component.element); } component.element.removeAttribute('contenteditable'); component.element.removeAttribute('spellcheck'); component.element.removeAttribute('data-mwp-sfe-editable-component'); component.element.removeAttribute('data-mwp-sfe-active-component'); if (textEditorHost && component.element._mwpEditor === textEditorHost) { delete component.element._mwpEditor; } }); } // Cleanup list tracker if (editorState.listTracker) { ListBlockTracker.destroy(editorState.listTracker); delete editorState.listTracker; } // Cleanup media focus manager (stored on bar, not editorState) if (editorState.actionsContainer && editorState.actionsContainer._cleanupMediaFocus) { editorState.actionsContainer._cleanupMediaFocus(); delete editorState.actionsContainer._cleanupMediaFocus; } if (editorState.textarea) editorState.textarea.remove(); if (editorState.contentWrapper) editorState.contentWrapper.remove(); // DON'T re-enable other elements yet - keep them disabled during animation // Update element content based on operation type if (isDraft) { // For drafts: restore the block to the cached published baseline when // available so repeated local draft edits do not overwrite the live // visitor state shown after submission. Fall back to the editor's // session snapshot if the baseline was never captured. const restoredElement = restoreDraftSubmittedElement(editorState); // Remove editing-active but KEEP element-active to prevent hover during success display. // IMPORTANT: restoreElementContent reconstructs from originalOuterHTML which never had // mwp-sfe-element-active. Without explicitly re-adding it here, the click guard // (document.querySelector('.mwp-sfe-element-active')) immediately returns null, // allowing the user to click any other element and interrupt the cleanup sequence. restoredElement.classList.remove('mwp-sfe-editing-active'); restoredElement.classList.add('mwp-sfe-element-active'); if (editorState.clearPending) { // Discard path: draft was rejected - element is no longer pending. restoredElement.classList.remove('mwp-sfe-status-pending'); // Also clear uuidMap so the element becomes fully editable again. const uuidEntry = uuidMap[editorState.uuid]; if (uuidEntry) { uuidEntry.is_pending = false; delete uuidEntry.pending_info; } } else { // Save-as-draft path: element is still pending review. restoredElement.classList.add('mwp-sfe-status-pending'); } // Update reference for later use editorState.element = restoredElement; // NOTE: rebindChildren is intentionally deferred to the post-enableAllElements // requestAnimationFrame below - same reasoning as the non-draft path (Bug 1 fix): // children are new DOM nodes not in disabledElements and would become immediately // interactive while the success banner is still visible. } else { // For published: Replace the entire element with server-rendered HTML. // applyNewHTML handles the DOM swap, UUID attributes, and uuidMap cleanup. const newElement = applyNewHTML(editorState.element, editorState.uuid, newHTML); if (newElement) { // Keep element marked as active during success display to prevent hover. newElement.classList.add('mwp-sfe-element-active'); // Update reference for reattaching action bar editorState.element = newElement; // NOTE: Do NOT call rebindChildren here. The new element's inner blocks // are fresh DOM nodes not in disabledElements, so they'd become interactive // immediately while the success animation is still running. Rebind is deferred // to the final cleanup block below, after enableAllElements fires. } } // Handle additional elements supplied by batch save. // Each item: { element, uuid, html } - the other dirty blocks beyond the active editor. // These have no open editor to clean up; they only need a DOM swap + lock during banner. const additionalNewEls = []; const additionalItems = options.additionalElements || []; for (const item of additionalItems) { if (!item.element || !item.uuid || !item.html) continue; const newEl = applyNewHTML(item.element, item.uuid, item.html); if (!newEl) continue; // Lock during success banner (same as the primary element above). newEl.classList.add('mwp-sfe-element-active'); if (isDraft) { newEl.classList.add('mwp-sfe-status-pending'); if (uuidMap[item.uuid]) { uuidMap[item.uuid].is_pending = true; } } additionalNewEls.push(newEl); } // Animate and final cleanup // Store timeout ID for potential cleanup const successTimeoutId = setTimeout(() => { successDiv.style.opacity = '0'; setTimeout(() => { successDiv.remove(); // Only cleanup if no new editor session has started // We check if UUIDs match. Media editing uses a "mock" state object for saving // that is a different object reference than activeEditor, so strictly checking (===) fails. const activeEditor = ctx.activeEditor; const isMatchingSession = activeEditor && (activeEditor === thisEditorSession || activeEditor.uuid === thisEditorSession.uuid); if (activeEditor === null || isMatchingSession) { // Now re-enable other elements ElementState.enableAllElements(); // Centralized unlock let triggerBtn = null; if (editorState && editorState.actionsContainer) { triggerBtn = editorState.actionsContainer.querySelector('.mwp-sfe-btn-primary-inline'); } if (SFE.SaveHelpers && SFE.SaveHelpers.unlockSaveUI) { SFE.SaveHelpers.unlockSaveUI(triggerBtn); } else { ctx.isSaving = false; } // Create cleanup callback (none needed for success state) const successCleanup = () => { // Hide bar using consolidated system if (editorState.actionsContainer) { actionBar.hide(); } }; // Remove active class now that we're resetting editorState.element.classList.remove('mwp-sfe-element-active'); // Use consolidated exit logic (will handle restoration and smart hiding) actionBar.reset( editorState.actionsContainer, editorState.element, editorState.uuid, successCleanup ); // Reattach click/hover action bar to the (possibly new) element // and rebind inner blocks NOW - after enableAllElements has fired, // so they enter the enabled state along with everything else. requestAnimationFrame(() => { try { SFE.rebindChildren(editorState.element); SFE.attachActionBarToElement(editorState.element); } catch (err) { console.error('FrontEdit: failed to reattach action bar after save', err); } // Rebind additional elements from batch save (if any). for (const el of additionalNewEls) { el.classList.remove('mwp-sfe-element-active'); try { SFE.rebindChildren(el); SFE.attachActionBarToElement(el); } catch (err) { console.error('FrontEdit: failed to rebind batch element after save', err); } } }); // Force clear activeEditor if it matches this session if (isMatchingSession) { ctx.activeEditor = null; } if (reloadAfterSuccess) { reloadAfterSuccess(); return; } } // If a new editor session started, don't interfere }, TIMING.SUCCESS_FADE); }, TIMING.SUCCESS_DISPLAY); // Store timeout ID on editorState for cleanup if (editorState) { editorState._successTimeoutId = successTimeoutId; } } SFE.SaveManager = { handleInlineSave, showInlineSuccess }; })(); (function (global, factory) { if (typeof exports === 'object' && typeof module !== 'undefined') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else { global = typeof globalThis !== 'undefined' ? globalThis : global || self; global.Chart = factory(); } })(this, (function () { 'use strict'; // Core functionality of Chart.js const Chart = function(context, config) { this.canvas = context.canvas || context; this.ctx = context; this.config = config; this.data = config.data; this.options = config.options; this.tooltip = { x: 0, y: 0, opacity: 0, dataPoint: null }; this.initialize(); }; Chart.prototype.initialize = function() { if (this.config.type === 'doughnut') { this.setupEventListeners(); this.drawDoughnutChart(); } }; Chart.prototype.setupEventListeners = function() { this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this)); this.canvas.addEventListener('mouseleave', this.handleMouseLeave.bind(this)); }; Chart.prototype.handleMouseMove = function(event) { const rect = this.canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const dataPoint = this.getDataPointAtPosition(x, y); if (dataPoint) { this.tooltip.x = x; this.tooltip.y = y; this.tooltip.opacity = 1; this.tooltip.dataPoint = dataPoint; this.drawDoughnutChart(); } else { this.handleMouseLeave(); } }; Chart.prototype.handleMouseLeave = function() { this.tooltip.opacity = 0; this.tooltip.dataPoint = null; this.drawDoughnutChart(); }; Chart.prototype.getDataPointAtPosition = function(x, y) { const rect = this.canvas.getBoundingClientRect(); const centerX = this.canvas.width / 2; const centerY = this.canvas.height / 2; const radius = Math.min(this.canvas.width, this.canvas.height) / 2; // Convert mouse position to canvas coordinates const scaleX = this.canvas.width / rect.width; const scaleY = this.canvas.height / rect.height; const canvasX = x * scaleX; const canvasY = y * scaleY; // Calculate distance from center const dx = canvasX - centerX; const dy = canvasY - centerY; const distance = Math.sqrt(dx * dx + dy * dy); // Check if point is within donut area const cutoutRadius = radius * (this.options.cutout || 0) / 100; if (distance > radius || distance < cutoutRadius) { return null; } // Calculate angle let angle = Math.atan2(dy, dx); if (angle < 0) { angle += 2 * Math.PI; } angle = (angle + Math.PI / 2) % (2 * Math.PI); // Find which segment the point is in const total = this.data.datasets[0].data.reduce((sum, value) => sum + value, 0); let currentAngle = 0; for (let i = 0; i < this.data.datasets[0].data.length; i++) { const sliceAngle = (2 * Math.PI * this.data.datasets[0].data[i]) / total; if (angle >= currentAngle && angle <= currentAngle + sliceAngle) { return { index: i, value: this.data.datasets[0].data[i], label: this.data.labels[i] }; } currentAngle += sliceAngle; } return null; }; Chart.prototype.drawTooltip = function() { if (!this.tooltip.opacity || !this.tooltip.dataPoint) return; const ctx = this.ctx; const rect = this.canvas.getBoundingClientRect(); const dataPoint = this.tooltip.dataPoint; const total = this.data.datasets[0].data.reduce((t, i) => t + i, 0); const percentage = ((dataPoint.value / total) * 100).toFixed(0); // Calculate tooltip dimensions ctx.save(); ctx.font = 'bold 16px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'; const percentText = `${percentage}%`; const percentWidth = ctx.measureText(percentText).width; ctx.font = '13px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'; const labelText = `${dataPoint.label}: ${dataPoint.value}`; const labelWidth = ctx.measureText(labelText).width; const padding = 12; const tooltipWidth = Math.max(percentWidth, labelWidth) + (padding * 2); const tooltipHeight = 45; // Calculate tooltip position in screen coordinates let tooltipX = this.tooltip.x - (tooltipWidth / 2); let tooltipY = this.tooltip.y - tooltipHeight - 10; // Adjust for screen boundaries if (tooltipX < 0) { tooltipX = 0; } else if (tooltipX + tooltipWidth > rect.width) { tooltipX = rect.width - tooltipWidth; } if (tooltipY < 0) { tooltipY = this.tooltip.y + 10; } // Convert to canvas coordinates const scaleX = this.canvas.width / rect.width; const scaleY = this.canvas.height / rect.height; tooltipX *= scaleX; tooltipY *= scaleY; // Draw tooltip background with solid color ctx.fillStyle = '#4E4B66'; // Solid dark background color ctx.beginPath(); ctx.roundRect(tooltipX, tooltipY, tooltipWidth * scaleX, tooltipHeight * scaleY, 4); ctx.fill(); // Draw percentage with corresponding color const tooltipColors = { 'Accepted': '#33A881', // Solid green 'Rejected': '#EC4A5E', // Solid red 'Partially Accepted': '#4493F9' // Solid blue }; ctx.fillStyle = tooltipColors[dataPoint.label]; ctx.font = 'bold 16px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'; ctx.textAlign = 'left'; ctx.fillText(percentText, tooltipX + (padding * scaleX), tooltipY + (20 * scaleY)); // Draw label ctx.fillStyle = '#ffffff'; ctx.font = '13px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif'; ctx.fillText(labelText, tooltipX + (padding * scaleX), tooltipY + (38 * scaleY)); ctx.restore(); }; Chart.prototype.drawDoughnutChart = function() { const ctx = this.ctx; const data = this.data; const options = this.options; const width = this.canvas.width; const height = this.canvas.height; const centerX = width / 2; const centerY = height / 2; const radius = Math.min(width, height) / 2; const cutout = options.cutout ? (parseFloat(options.cutout) / 100) * radius : 0; // Clear canvas ctx.clearRect(0, 0, width, height); const total = data.datasets[0].data.reduce((sum, value) => sum + value, 0); let startAngle = -0.5 * Math.PI; // Chart colors aligned with admin consent chart styling const chartColors = { 'Accepted': 'rgba(51, 168, 129, 0.5)', // Light green with 0.5 opacity 'Rejected': 'rgba(236, 74, 94, 0.5)', // Light red with 0.5 opacity 'Partially Accepted': 'rgba(68, 147, 249, 0.5)' // Light blue with 0.5 opacity }; // Tooltip colors with lighter opacity const tooltipColors = { 'Accepted': 'rgba(51, 168, 129, 0.7)', // Light green with 0.7 opacity 'Rejected': 'rgba(236, 74, 94, 0.7)', // Light red with 0.7 opacity 'Partially Accepted': 'rgba(68, 147, 249, 0.7)' // Light blue with 0.7 opacity }; data.datasets[0].data.forEach((value, index) => { const sliceAngle = (2 * Math.PI * value) / total; const label = data.labels[index]; ctx.beginPath(); ctx.arc(centerX, centerY, radius, startAngle, startAngle + sliceAngle); ctx.arc(centerX, centerY, cutout, startAngle + sliceAngle, startAngle, true); ctx.closePath(); // Check if this slice is being hovered if (this.tooltip.dataPoint && this.tooltip.dataPoint.label === label) { ctx.fillStyle = tooltipColors[label]; } else { ctx.fillStyle = chartColors[label]; } ctx.fill(); startAngle += sliceAngle; }); // Draw tooltip if active this.drawTooltip(); }; // Export the Chart object return Chart; })); === WordPress Importer === Contributors: wordpressdotorg Tags: importer, wordpress Requires at least: 3.0 Tested up to: 4.6 Stable tag: 0.6.3 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Import posts, pages, comments, custom fields, categories, tags and more from a WordPress export file. == Description == The WordPress Importer will import the following content from a WordPress export file: * Posts, pages and other custom post types * Comments * Custom fields and post meta * Categories, tags and terms from custom taxonomies * Authors For further information and instructions please see the [Codex page on Importing Content](http://codex.wordpress.org/Importing_Content#WordPress) == Installation == The quickest method for installing the importer is: 1. Visit Tools -> Import in the WordPress dashboard 1. Click on the WordPress link in the list of importers 1. Click "Install Now" 1. Finally click "Activate Plugin & Run Importer" If you would prefer to do things manually then follow these instructions: 1. Upload the `wordpress-importer` folder to the `/wp-content/plugins/` directory 1. Activate the plugin through the 'Plugins' menu in WordPress 1. Go to the Tools -> Import screen, click on WordPress == Changelog == = 0.6.3 = * Add support for import term metadata. * Fix bug that caused slashes to be stripped from imported content. * Fix bug that caused characters to be stripped inside of CDATA in some cases. * Fix PHP notices. = 0.6.2 = * Add wp_import_existing_post filter. See: https://core.trac.wordpress.org/ticket/33721 = 0.6 = * Support for WXR 1.2 and multiple CDATA sections * Post aren't duplicates if their post_type's are different = 0.5.2 = * Double check that the uploaded export file exists before processing it. This prevents incorrect error messages when an export file is uploaded to a server with bad permissions and WordPress 3.3 or 3.3.1 is being used. = 0.5 = * Import comment meta (requires export from WordPress 3.2) * Minor bugfixes and enhancements = 0.4 = * Map comment user_id where possible * Import attachments from `wp:attachment_url` * Upload attachments to correct directory * Remap resized image URLs correctly = 0.3 = * Use an XML Parser if possible * Proper import support for nav menus * ... and much more, see [Trac ticket #15197](http://core.trac.wordpress.org/ticket/15197) = 0.1 = * Initial release == Upgrade Notice == = 0.6 = Support for exports from WordPress 3.4. = 0.5.2 = Fix incorrect error message when the export file could not be uploaded. = 0.5 = Import comment meta and other minor bugfixes and enhancements. = 0.4 = Bug fixes for attachment importing and other small enhancements. = 0.3 = Upgrade for a more robust and reliable experience when importing WordPress export files, and for compatibility with WordPress 3.1. == Frequently Asked Questions == = Help! I'm getting out of memory errors or a blank screen. = If your exported file is very large, the import script may run into your host's configured memory limit for PHP. A message like "Fatal error: Allowed memory size of 8388608 bytes exhausted" indicates that the script can't successfully import your XML file under the current PHP memory limit. If you have access to the php.ini file, you can manually increase the limit; if you do not (your WordPress installation is hosted on a shared server, for instance), you might have to break your exported XML file into several smaller pieces and run the import script one at a time. For those with shared hosting, the best alternative may be to consult hosting support to determine the safest approach for running the import. A host may be willing to temporarily lift the memory limit and/or run the process directly from their end. -- [WordPress Codex: Importing Content](http://codex.wordpress.org/Importing_Content#Before_Importing) == Filters == The importer has a couple of filters to allow you to completely enable/block certain features: * `import_allow_create_users`: return false if you only want to allow mapping to existing users * `import_allow_fetch_attachments`: return false if you do not wish to allow importing and downloading of attachments * `import_attachment_size_limit`: return an integer value for the maximum file size in bytes to save (default is 0, which is unlimited) There are also a few actions available to hook into: * `import_start`: occurs after the export file has been uploaded and author import settings have been chosen * `import_end`: called after the last output from the importer /*! For license information please see env.js.LICENSE.txt */ !function(){"use strict";var n={d:function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},r:function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})}},e={};n.r(e),n.d(e,{InvalidEnvError:function(){return InvalidEnvError},__resetEnv:function(){return __resetEnv},initEnv:function(){return initEnv},parseEnv:function(){return parseEnv}});let t=null;function initEnv(n){t=n}function __resetEnv(){t=null}function parseEnv(n,e=n=>n){let r={},o=!1;const i=new Proxy(r,{get(n,e){return o||parse(),r[e]},ownKeys(){return o||parse(),Reflect.ownKeys(r)},getOwnPropertyDescriptor(){return{configurable:!0,enumerable:!0}}}),parse=()=>{try{const o=t?.[n];if(!o)throw new InvalidEnvError("Settings object not found");if("object"!=typeof o)throw new InvalidEnvError(`Expected settings to be \`object\`, but got \`${typeof o}\``);r=e(o)}catch(e){if(!(e instanceof InvalidEnvError))throw e;console.warn(`${n} - ${e.message}`),r={}}finally{o=!0}};return{validateEnv:parse,env:i}}class InvalidEnvError extends Error{}(window.elementorV2=window.elementorV2||{}).env=e}(),window.elementorV2.env?.init?.(); //# sourceMappingURL=env.js.map=== WordPress Importer === Contributors: wordpressdotorg Donate link: https://wordpressfoundation.org/donate/ Tags: importer, wordpress Requires at least: 5.2 Tested up to: 6.4.2 Requires PHP: 5.6 Stable tag: 0.8.2 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Import posts, pages, comments, custom fields, categories, tags and more from a WordPress export file. == Description == The WordPress Importer will import the following content from a WordPress export file: * Posts, pages and other custom post types * Comments and comment meta * Custom fields and post meta * Categories, tags and terms from custom taxonomies and term meta * Authors For further information and instructions please see the [documention on Importing Content](https://wordpress.org/support/article/importing-content/#wordpress). == Installation == The quickest method for installing the importer is: 1. Visit Tools -> Import in the WordPress dashboard 1. Click on the WordPress link in the list of importers 1. Click "Install Now" 1. Finally click "Activate Plugin & Run Importer" If you would prefer to do things manually then follow these instructions: 1. Upload the `wordpress-importer` folder to the `/wp-content/plugins/` directory 1. Activate the plugin through the 'Plugins' menu in WordPress 1. Go to the Tools -> Import screen, click on WordPress == Changelog == = 0.8.2 = * Update compatibility tested-up-to to WordPress 6.4.2. * Update doc URL references. * Adjust workflow triggers. = 0.8.1 = * Update compatibility tested-up-to to WordPress 6.2. * Update paths to build status badges. = 0.8 = * Update minimum WordPress requirement to 5.2. * Update minimum PHP requirement to 5.6. * Update compatibility tested-up-to to WordPress 6.1. * PHP 8.0, 8.1, and 8.2 compatibility fixes. * Fix a bug causing blank lines in content to be ignored when using the Regex Parser. * Fix a bug resulting in a PHP fatal error when IMPORT_DEBUG is enabled and a category creation error occurs. * Improved Unit testing & automated testing. = 0.7 = * Update minimum WordPress requirement to 3.7 and ensure compatibility with PHP 7.4. * Fix bug that caused not importing term meta. * Fix bug that caused slashes to be stripped from imported meta data. * Fix bug that prevented import of serialized meta data. * Fix file size check after download of remote files with HTTP compression enabled. * Improve accessibility of form fields by adding missing labels. * Improve imports for remote file URLs without name and/or extension. * Add support for `wp:base_blog_url` field to allow importing multiple files with WP-CLI. * Add support for term meta parsing when using the regular expressions or XML parser. * Developers: All PHP classes have been moved into their own files. * Developers: Allow to change `IMPORT_DEBUG` via `wp-config.php` and change default value to the value of `WP_DEBUG`. = 0.6.4 = * Improve PHP7 compatibility. * Fix bug that caused slashes to be stripped from imported comments. * Fix for various deprecation notices including `wp_get_http()` and `screen_icon()`. * Fix for importing export files with multiline term meta data. = 0.6.3 = * Add support for import term metadata. * Fix bug that caused slashes to be stripped from imported content. * Fix bug that caused characters to be stripped inside of CDATA in some cases. * Fix PHP notices. = 0.6.2 = * Add `wp_import_existing_post` filter, see [Trac ticket #33721](https://core.trac.wordpress.org/ticket/33721). = 0.6 = * Support for WXR 1.2 and multiple CDATA sections * Post aren't duplicates if their post_type's are different = 0.5.2 = * Double check that the uploaded export file exists before processing it. This prevents incorrect error messages when an export file is uploaded to a server with bad permissions and WordPress 3.3 or 3.3.1 is being used. = 0.5 = * Import comment meta (requires export from WordPress 3.2) * Minor bugfixes and enhancements = 0.4 = * Map comment user_id where possible * Import attachments from `wp:attachment_url` * Upload attachments to correct directory * Remap resized image URLs correctly = 0.3 = * Use an XML Parser if possible * Proper import support for nav menus * ... and much more, see [Trac ticket #15197](https://core.trac.wordpress.org/ticket/15197) = 0.1 = * Initial release == Frequently Asked Questions == = Help! I'm getting out of memory errors or a blank screen. = If your exported file is very large, the import script may run into your host's configured memory limit for PHP. A message like "Fatal error: Allowed memory size of 8388608 bytes exhausted" indicates that the script can't successfully import your XML file under the current PHP memory limit. If you have access to the php.ini file, you can manually increase the limit; if you do not (your WordPress installation is hosted on a shared server, for instance), you might have to break your exported XML file into several smaller pieces and run the import script one at a time. For those with shared hosting, the best alternative may be to consult hosting support to determine the safest approach for running the import. A host may be willing to temporarily lift the memory limit and/or run the process directly from their end. -- [Support Article: Importing Content](https://wordpress.org/support/article/importing-content/#before-importing) == Filters == The importer has a couple of filters to allow you to completely enable/block certain features: * `import_allow_create_users`: return false if you only want to allow mapping to existing users * `import_allow_fetch_attachments`: return false if you do not wish to allow importing and downloading of attachments * `import_attachment_size_limit`: return an integer value for the maximum file size in bytes to save (default is 0, which is unlimited) There are also a few actions available to hook into: * `import_start`: occurs after the export file has been uploaded and author import settings have been chosen * `import_end`: called after the last output from the importer
/******/ (function() { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./packages/packages/core/editor-templates/src/init.ts": /*!*************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/init.ts ***! \*************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ init: function() { return /* binding */ init; } /* harmony export */ }); /* harmony import */ var _elementor_editor__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor */ "@elementor/editor"); /* harmony import */ var _elementor_editor__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/editor-styles-repository */ "@elementor/editor-styles-repository"); /* harmony import */ var _elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @elementor/editor-v1-adapters */ "@elementor/editor-v1-adapters"); /* harmony import */ var _elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @elementor/store */ "@elementor/store"); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_elementor_store__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _load_templates__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./load-templates */ "./packages/packages/core/editor-templates/src/load-templates.ts"); /* harmony import */ var _render_template_styles__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./render-template-styles */ "./packages/packages/core/editor-templates/src/render-template-styles.tsx"); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./store */ "./packages/packages/core/editor-templates/src/store.ts"); /* harmony import */ var _templates_styles_provider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./templates-styles-provider */ "./packages/packages/core/editor-templates/src/templates-styles-provider.ts"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./utils */ "./packages/packages/core/editor-templates/src/utils.ts"); function init() { if (!(0,_utils__WEBPACK_IMPORTED_MODULE_8__.isHandlingTemplateStyles)()) { return; } (0,_elementor_store__WEBPACK_IMPORTED_MODULE_3__.__registerSlice)(_store__WEBPACK_IMPORTED_MODULE_6__.slice); _elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_1__.stylesRepository.register(_templates_styles_provider__WEBPACK_IMPORTED_MODULE_7__.templatesStylesProvider); (0,_elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_2__.registerDataHook)('after', 'editor/documents/attach-preview', async () => { (0,_load_templates__WEBPACK_IMPORTED_MODULE_4__.unloadTemplates)(); (0,_templates_styles_provider__WEBPACK_IMPORTED_MODULE_7__.clearTemplatesStyles)(); await (0,_load_templates__WEBPACK_IMPORTED_MODULE_4__.loadTemplates)(); }); (0,_elementor_editor__WEBPACK_IMPORTED_MODULE_0__.injectIntoLogic)({ id: 'templates-styles', component: _render_template_styles__WEBPACK_IMPORTED_MODULE_5__.RenderTemplateStyles }); } /***/ }), /***/ "./packages/packages/core/editor-templates/src/load-templates.ts": /*!***********************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/load-templates.ts ***! \***********************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ loadTemplates: function() { return /* binding */ loadTemplates; }, /* harmony export */ unloadTemplates: function() { return /* binding */ unloadTemplates; } /* harmony export */ }); /* harmony import */ var _elementor_editor_documents__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor-documents */ "@elementor/editor-documents"); /* harmony import */ var _elementor_editor_documents__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_documents__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/editor-v1-adapters */ "@elementor/editor-v1-adapters"); /* harmony import */ var _elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @elementor/store */ "@elementor/store"); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_elementor_store__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./store */ "./packages/packages/core/editor-templates/src/store.ts"); const TEMPLATE_ATTRIBUTE = 'data-elementor-post-type="elementor_library"'; const DOCUMENT_WRAPPER_ATTR = 'data-elementor-id'; async function loadTemplates() { const iframeDocument = (0,_elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_1__.getCanvasIframeDocument)(); if (!iframeDocument) { return; } const currentDocumentId = (0,_elementor_editor_documents__WEBPACK_IMPORTED_MODULE_0__.getV1CurrentDocument)()?.id; const templateIds = getTemplateIds(iframeDocument, currentDocumentId); if (!templateIds.length) { return; } const documents = await fetchDocuments(templateIds); (0,_elementor_store__WEBPACK_IMPORTED_MODULE_2__.__dispatch)(_store__WEBPACK_IMPORTED_MODULE_3__.slice.actions.setTemplates(documents)); } function unloadTemplates() { (0,_elementor_store__WEBPACK_IMPORTED_MODULE_2__.__dispatch)(_store__WEBPACK_IMPORTED_MODULE_3__.slice.actions.clearTemplates()); } function getTemplateIds(iframeDocument, currentDocumentId) { const elements = [...iframeDocument.body.querySelectorAll(`[${TEMPLATE_ATTRIBUTE}]`)]; const ids = elements.map(el => Number(el.getAttribute(DOCUMENT_WRAPPER_ATTR))).filter(id => !isNaN(id) && id !== currentDocumentId); return [...new Set(ids)]; } async function fetchDocuments(ids) { const results = await Promise.all(ids.map(async id => { try { // using ajax.load instead of the document-manager as the latter causes an issue when trying to edit a template return await _elementor_editor_v1_adapters__WEBPACK_IMPORTED_MODULE_1__.ajax.load({ data: { id }, action: 'get_document_config', unique_id: `template-${id}` }); } catch { return null; } })); return results.filter(doc => doc !== null); } /***/ }), /***/ "./packages/packages/core/editor-templates/src/render-template-styles.tsx": /*!********************************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/render-template-styles.tsx ***! \********************************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ RenderTemplateStyles: function() { return /* binding */ RenderTemplateStyles; } /* harmony export */ }); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _templates_styles_provider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./templates-styles-provider */ "./packages/packages/core/editor-templates/src/templates-styles-provider.ts"); /* harmony import */ var _use_loaded_templates__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./use-loaded-templates */ "./packages/packages/core/editor-templates/src/use-loaded-templates.ts"); const RenderTemplateStyles = () => { const templates = (0,_use_loaded_templates__WEBPACK_IMPORTED_MODULE_2__.useLoadedTemplates)(); (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { const styles = templates.flatMap(extractStylesFromDocument); (0,_templates_styles_provider__WEBPACK_IMPORTED_MODULE_1__.addTemplateStyles)(styles); }, [templates]); return null; }; function extractStylesFromDocument(elements) { if (!elements.length) { return []; } return elements.flatMap(extractStylesFromElement); } function extractStylesFromElement(element) { return [...Object.values(element.styles ?? {}), ...(element.elements ?? []).flatMap(extractStylesFromElement)]; } /***/ }), /***/ "./packages/packages/core/editor-templates/src/store.ts": /*!**************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/store.ts ***! \**************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ selectTemplates: function() { return /* binding */ selectTemplates; }, /* harmony export */ slice: function() { return /* binding */ slice; } /* harmony export */ }); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/store */ "@elementor/store"); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_store__WEBPACK_IMPORTED_MODULE_0__); const initialState = { entities: {} }; const slice = (0,_elementor_store__WEBPACK_IMPORTED_MODULE_0__.__createSlice)({ name: 'templates', initialState, reducers: { setTemplates(state, action) { action.payload.forEach(doc => { state.entities[doc.id] = doc.elements ?? []; }); }, clearTemplates(state) { state.entities = {}; } } }); const selectEntities = state => state.templates.entities; const selectTemplates = (0,_elementor_store__WEBPACK_IMPORTED_MODULE_0__.__createSelector)([selectEntities], entities => Object.values(entities)); /***/ }), /***/ "./packages/packages/core/editor-templates/src/templates-styles-provider.ts": /*!**********************************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/templates-styles-provider.ts ***! \**********************************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ addTemplateStyles: function() { return /* binding */ addTemplateStyles; }, /* harmony export */ clearTemplatesStyles: function() { return /* binding */ clearTemplatesStyles; }, /* harmony export */ templatesStylesProvider: function() { return /* binding */ templatesStylesProvider; } /* harmony export */ }); /* harmony import */ var _elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor-styles-repository */ "@elementor/editor-styles-repository"); /* harmony import */ var _elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_0__); let styles = []; const listeners = new Set(); function addTemplateStyles(newStyles) { styles = [...styles, ...newStyles]; listeners.forEach(cb => cb()); } function clearTemplatesStyles() { styles = []; listeners.forEach(cb => cb()); } const templatesStylesProvider = (0,_elementor_editor_styles_repository__WEBPACK_IMPORTED_MODULE_0__.createStylesProvider)({ key: 'templates-styles', priority: 50, subscribe: cb => { listeners.add(cb); return () => { listeners.delete(cb); }; }, actions: { all: () => styles, get: id => styles.find(style => style.id === id) ?? null } }); /***/ }), /***/ "./packages/packages/core/editor-templates/src/use-loaded-templates.ts": /*!*****************************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/use-loaded-templates.ts ***! \*****************************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ useLoadedTemplates: function() { return /* binding */ useLoadedTemplates; } /* harmony export */ }); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/store */ "@elementor/store"); /* harmony import */ var _elementor_store__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_store__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./store */ "./packages/packages/core/editor-templates/src/store.ts"); function useLoadedTemplates() { return (0,_elementor_store__WEBPACK_IMPORTED_MODULE_0__.__useSelector)(_store__WEBPACK_IMPORTED_MODULE_1__.selectTemplates); } /***/ }), /***/ "./packages/packages/core/editor-templates/src/utils.ts": /*!**************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/utils.ts ***! \**************************************************************/ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ isHandlingTemplateStyles: function() { return /* binding */ isHandlingTemplateStyles; } /* harmony export */ }); /* harmony import */ var _elementor_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/utils */ "@elementor/utils"); /* harmony import */ var _elementor_utils__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_utils__WEBPACK_IMPORTED_MODULE_0__); const MIN_PRO_VERSION_FOR_SELF_HANDLED_STYLES = '4.1'; const isHandlingTemplateStyles = () => (0,_elementor_utils__WEBPACK_IMPORTED_MODULE_0__.isProActive)() && !(0,_elementor_utils__WEBPACK_IMPORTED_MODULE_0__.isProAtLeast)(MIN_PRO_VERSION_FOR_SELF_HANDLED_STYLES); /***/ }), /***/ "@elementor/editor": /*!*****************************************!*\ !*** external ["elementorV2","editor"] ***! \*****************************************/ /***/ (function(module) { module.exports = window["elementorV2"]["editor"]; /***/ }), /***/ "@elementor/editor-documents": /*!**************************************************!*\ !*** external ["elementorV2","editorDocuments"] ***! \**************************************************/ /***/ (function(module) { module.exports = window["elementorV2"]["editorDocuments"]; /***/ }), /***/ "@elementor/editor-styles-repository": /*!*********************************************************!*\ !*** external ["elementorV2","editorStylesRepository"] ***! \*********************************************************/ /***/ (function(module) { module.exports = window["elementorV2"]["editorStylesRepository"]; /***/ }), /***/ "@elementor/editor-v1-adapters": /*!***************************************************!*\ !*** external ["elementorV2","editorV1Adapters"] ***! \***************************************************/ /***/ (function(module) { module.exports = window["elementorV2"]["editorV1Adapters"]; /***/ }), /***/ "@elementor/store": /*!****************************************!*\ !*** external ["elementorV2","store"] ***! \****************************************/ /***/ (function(module) { module.exports = window["elementorV2"]["store"]; /***/ }), /***/ "@elementor/utils": /*!****************************************!*\ !*** external ["elementorV2","utils"] ***! \****************************************/ /***/ (function(module) { module.exports = window["elementorV2"]["utils"]; /***/ }), /***/ "react": /*!**************************!*\ !*** external ["React"] ***! \**************************/ /***/ (function(module) { module.exports = window["React"]; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ !function() { /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function() { return module['default']; } : /******/ function() { return module; }; /******/ __webpack_require__.d(getter, { a: getter }); /******/ return getter; /******/ }; /******/ }(); /******/ /******/ /* webpack/runtime/define property getters */ /******/ !function() { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = function(exports, definition) { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ }(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ !function() { /******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } /******/ }(); /******/ /******/ /* webpack/runtime/make namespace object */ /******/ !function() { /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ }(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. !function() { /*!**************************************************************!*\ !*** ./packages/packages/core/editor-templates/src/index.ts ***! \**************************************************************/ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ init: function() { return /* reexport safe */ _init__WEBPACK_IMPORTED_MODULE_0__.init; }, /* harmony export */ isHandlingTemplateStyles: function() { return /* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_2__.isHandlingTemplateStyles; }, /* harmony export */ useLoadedTemplates: function() { return /* reexport safe */ _use_loaded_templates__WEBPACK_IMPORTED_MODULE_1__.useLoadedTemplates; } /* harmony export */ }); /* harmony import */ var _init__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./init */ "./packages/packages/core/editor-templates/src/init.ts"); /* harmony import */ var _use_loaded_templates__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./use-loaded-templates */ "./packages/packages/core/editor-templates/src/use-loaded-templates.ts"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./packages/packages/core/editor-templates/src/utils.ts"); }(); (window.elementorV2 = window.elementorV2 || {}).editorTemplates = __webpack_exports__; /******/ })() ; window.elementorV2.editorTemplates?.init?.(); //# sourceMappingURL=editor-templates.js.map https://gestyplus.com/post-sitemap1.xml 2026-08-24T12:53:01+00:00 https://gestyplus.com/page-sitemap1.xml 2026-07-14T07:49:04+00:00 https://gestyplus.com/category-sitemap1.xml 2026-09-08T22:01:39+00:00