/** * External dependencies */ import { setWith, clone } from 'lodash'; /** * Utility for updating state and only cloning objects in the path that changed. * * @param {Object} state The state being updated * @param {Array} path The path being updated * @param {*} value The value to update for the path * * @return {Object} The new state */ export default function updateState( state, path, value ) { return setWith( clone( state ), path, value, clone ); } __( 'Save as default', 'elementor' ); __( 'Sure you want to change default settings?', 'elementor' ); __( 'Your changes will automatically be saved for future uses of this element. %1$sNote:%2$s This includes sensitive information like emails, API keys, etc.', 'elementor' ); __( 'Save', 'elementor' ); __( 'Cancel', 'elementor' ); __( 'Do not show this message again', 'elementor' ); __( 'Previous settings restored.', 'elementor' ); __( 'An error occurred.', 'elementor' ); __( 'Default settings has been reset.', 'elementor' ); __( 'An error occurred.', 'elementor' ); __( 'Default settings changed.', 'elementor' ); __( 'Undo', 'elementor' ); __( 'An error occurred.', 'elementor' );# Varela Round font `VarelaRound-Regular.ttf` is the unmodified Varela Round Regular font from the [Google Fonts repository](https://github.com/google/fonts/tree/main/ofl/varelaround). It is included locally so FrontEdit does not make a request to Google Fonts. The font is licensed under the SIL Open Font License, Version 1.1; the complete license and required attribution are included in `OFL.txt`. {"translation-revision-date":"2026-MO-DA HO:MI+ZONE","generator":"WP-CLI\/2.12.0","source":"src\/Components\/NavMenu.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"pl_PL","plural-forms":"nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);"},"Header & Footer":["Nag\u0142\u00f3wek i stopka"],"Dashboard":["Panel sterowania"],"Widgets":["Widgety"],"Settings":["Ustawienia"],"What's New?":["Co nowego?"],"Learn":["Naucz si\u0119"],"Free vs Pro":["Darmowe vs Pro"],"Get Full Control":["Uzyskaj pe\u0142n\u0105 kontrol\u0119"],"Free":["Darmowe"],"Version":["Wersja"],"Useful Resources":["Przydatne zasoby"],"Getting Started":["Jak zacz\u0105\u0107"],"How to use widgets":["Jak u\u017cywa\u0107 widget\u00f3w"],"How to use features":["Jak u\u017cywa\u0107 funkcji"],"How to use templates":["Jak u\u017cywa\u0107 szablon\u00f3w"],"Contact us":["Skontaktuj si\u0119 z nami"]}}}/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ /** * List block tracker for UUID-aware list editing and serialization. * * Dependencies: wp.blocks, SFE.ElementPrep * Exposes: SFE.ListBlockTracker */ (function() { 'use strict'; window.MWP = window.MWP || {}; window.MWP.SFE = window.MWP.SFE || {}; const SFE = window.MWP.SFE; SFE.ManagerData = SFE.ManagerData || {}; const { createBlock, serialize: serializeBlocks } = window.wp?.blocks || {}; const UUID_ATTR_KEYS = ['mwpSfeUuid', 'mwpSfeUuidShadow']; const LIST_ITEM_TEXT_ATTR = 'data-mwp-sfe-list-item-text'; const LIST_ITEM_TEXT_CLASS = 'mwp-sfe-list-item-text'; const PLACEHOLDER_ATTR = 'data-rich-text-placeholder'; const LIST_ID_ATTR = 'data-list-id'; const LIST_ITEM_ID_ATTR = 'data-item-id'; const LIST_RUNTIME_UUID_ATTR = 'data-mwp-sfe-list-runtime-uuid'; const LIST_ITEM_RUNTIME_UUID_ATTR = 'data-mwp-sfe-list-item-runtime-uuid'; /** * Return whether one node is a nested list element. * * @param {Node|null} node Candidate DOM node. * @returns {boolean} True when the node is a nested list. */ function isNestedListNode(node) { return !!( node && node.nodeType === Node.ELEMENT_NODE && (node.tagName === 'UL' || node.tagName === 'OL') ); } /** * Return whether one node is ignorable formatting whitespace between list * structures. * * Pretty-printed block markup often leaves direct `\n` text nodes between a * list item's own text and its nested child list. Those nodes should not be * moved into the direct text surface because they become visible editing * artifacts once wrapped. * * @param {Node|null} node Candidate DOM node. * @returns {boolean} True when the node is ignorable whitespace. */ function isIgnorableListWhitespaceNode(node) { return !!( node && node.nodeType === Node.TEXT_NODE && String(node.textContent || '') .replace(/\uFEFF/g, '') .replace(/\u00A0/g, ' ') .trim() .length === 0 ); } /** * Return the direct inline text surface for one list item. * * @param {HTMLLIElement|null} liElement Candidate list item. * @returns {HTMLElement|null} Existing direct text surface, if any. */ function getDirectItemTextSurface(liElement) { if (!liElement || liElement.nodeType !== Node.ELEMENT_NODE || liElement.tagName !== 'LI') { return null; } return Array.from(liElement.children || []).find(child => ( child && child.nodeType === Node.ELEMENT_NODE && child.getAttribute(LIST_ITEM_TEXT_ATTR) === '1' )) || null; } /** * Return the DOM attribute name that stores one session-scoped runtime UUID. * * Runtime UUIDs are distinct from the existing serialization/attr UUIDs. They * exist only while the editor session is open so external callers can point at * the current live list cursor/target without having to manage shifting paths. * * @param {string} type Supported runtime identity type. * @returns {string} Attribute name, or an empty string for invalid types. */ function getRuntimeUuidAttributeName(type) { if (type === 'list') { return LIST_RUNTIME_UUID_ATTR; } if (type === 'item') { return LIST_ITEM_RUNTIME_UUID_ATTR; } return ''; } /** * Normalize one runtime UUID candidate. * * @param {*} value Candidate runtime UUID. * @returns {string} Trimmed runtime UUID, or an empty string. */ function normalizeRuntimeUuid(value) { return String(value || '').trim(); } /** * Return whether one element matches the requested runtime-identity type. * * @param {Element|null} element Candidate DOM element. * @param {string} type Supported runtime identity type. * @returns {boolean} True when the element matches the type. */ function isRuntimeIdentityElement(element, type) { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return false; } if (type === 'list') { return element.tagName === 'UL' || element.tagName === 'OL'; } if (type === 'item') { return element.tagName === 'LI'; } return false; } /** * Return one tracked element by runtime UUID from the live DOM. * * This intentionally queries the current DOM first instead of trusting only a * cached map so history restores and other structural replacements can keep * runtime UUID resolution stable as long as the attributes remain present. * * @param {Object|null} tracker Active list tracker. * @param {string} runtimeUuid Session-scoped runtime UUID. * @param {string} type Supported runtime identity type. * @returns {Element|null} Matching live DOM element. */ function findElementByRuntimeUuid(tracker, runtimeUuid, type) { const normalizedRuntimeUuid = normalizeRuntimeUuid(runtimeUuid); const attrName = getRuntimeUuidAttributeName(type); if ( !tracker?.listElement || !normalizedRuntimeUuid || !attrName || !isRuntimeIdentityElement(tracker.listElement, 'list') ) { return null; } if ( type === 'list' && tracker.listElement.getAttribute(attrName) === normalizedRuntimeUuid ) { return tracker.listElement; } try { const selector = `[${attrName}="${CSS.escape(normalizedRuntimeUuid)}"]`; return tracker.listElement.querySelector(selector); } catch (error) { const escapedUuid = normalizedRuntimeUuid.replace(/"/g, '\\"'); return tracker.listElement.querySelector(`[${attrName}="${escapedUuid}"]`); } } /** * Return every direct text surface currently attached to one list item. * * Merge/delete flows can temporarily move multiple direct text surfaces into * the same list item. The shared list normalizer must collapse them back to * one canonical surface before placeholder syncing or serialization. * * @param {HTMLLIElement|null} liElement Candidate list item. * @returns {HTMLElement[]} Direct text surfaces in DOM order. */ function getAllDirectItemTextSurfaces(liElement) { if (!liElement || liElement.nodeType !== Node.ELEMENT_NODE || liElement.tagName !== 'LI') { return []; } return Array.from(liElement.children || []).filter(child => ( child && child.nodeType === Node.ELEMENT_NODE && child.getAttribute(LIST_ITEM_TEXT_ATTR) === '1' )); } /** * Return whether one node is only placeholder/caret scaffolding. * * Duplicate direct text surfaces may carry empty placeholder anchors or * caret `
` nodes. Those artifacts should be dropped when collapsing * duplicate surfaces instead of being concatenated into visible stacks. * * @param {Node|null} node Candidate DOM node. * @returns {boolean} True when the node is redundant placeholder UI. */ function isRedundantSurfaceArtifact(node) { if (!node) { return true; } if (isIgnorableListWhitespaceNode(node)) { return true; } if (node.nodeType === Node.TEXT_NODE) { return String(node.textContent || '') .replace(/\uFEFF/g, '') .replace(/\u00A0/g, ' ') .trim() .length === 0; } if (node.nodeType !== Node.ELEMENT_NODE) { return false; } if (node.tagName === 'BR') { return true; } return !!node.hasAttribute?.(PLACEHOLDER_ATTR); } /** * Ensure one list item owns one direct inline text surface. * * The root list remains the single live editor host, but this wrapper gives * schema/ABE flows one stable DOM surface per list item's own text content. * * @param {HTMLLIElement|null} liElement Candidate list item. * @returns {HTMLElement|null} Ensured direct text surface. */ function ensureDirectItemTextSurface(liElement) { if (!liElement || liElement.nodeType !== Node.ELEMENT_NODE || liElement.tagName !== 'LI') { return null; } let surface = getDirectItemTextSurface(liElement); if (!surface) { surface = document.createElement('span'); surface.setAttribute(LIST_ITEM_TEXT_ATTR, '1'); surface.classList.add(LIST_ITEM_TEXT_CLASS); liElement.insertBefore( surface, Array.from(liElement.childNodes || []).find(isNestedListNode) || null ); } getAllDirectItemTextSurfaces(liElement) .filter(candidate => candidate && candidate !== surface) .forEach(duplicateSurface => { Array.from(duplicateSurface.childNodes || []).forEach(node => { if (isRedundantSurfaceArtifact(node)) { node.remove(); return; } surface.appendChild(node); }); duplicateSurface.remove(); }); const childNodes = Array.from(liElement.childNodes || []); childNodes.forEach(node => { if ( node && node !== surface && !isNestedListNode(node) && isIgnorableListWhitespaceNode(node) ) { node.remove(); } }); const movableNodes = childNodes.filter(node => { if (!node || node === surface || isNestedListNode(node)) { return false; } if (isIgnorableListWhitespaceNode(node)) { return false; } return !( node.nodeType === Node.ELEMENT_NODE && node.getAttribute?.(LIST_ITEM_TEXT_ATTR) === '1' ); }); movableNodes.forEach(node => surface.appendChild(node)); return surface; } /** * Clone list attrs while optionally stripping plugin UUID ownership. * * The outermost core/list is the only persisted UUID owner for an entire * list tree. Nested core/list blocks are structural children of that root. * If we preserve a nested list UUID here, one accidental assignment can be * re-serialized forever and split a single logical list into multiple * history/edit targets. Keep this guard unless the ownership model changes * everywhere else in PHP and JS at the same time. * * @param {Object} attrs Parsed Gutenberg attrs. * @param {boolean} allowUuidOwnership True for the root list only. * @returns {Object} Safe cloned attrs. */ function cloneListAttrs(attrs, allowUuidOwnership = true) { const clonedAttrs = JSON.parse(JSON.stringify(attrs || {})); if (allowUuidOwnership) { return clonedAttrs; } UUID_ATTR_KEYS.forEach(key => delete clonedAttrs[key]); return clonedAttrs; } /** * Return the direct list-item children for one list element. * * @param {HTMLElement|null} listElement Candidate list element. * @returns {HTMLLIElement[]} Direct child list items. */ function getDirectListItems(listElement) { if (!listElement || listElement.nodeType !== Node.ELEMENT_NODE) { return []; } return Array.from(listElement.children || []).filter(child => child.tagName === 'LI'); } /** * Normalize one path-like value into zero-based list indexes. * * Supported inputs: * - `0_1_2` * - `1.2.3` * - arrays of integers * * @param {string|Array|null} pathValue Candidate path value. * @returns {number[]|null} Parsed zero-based indexes. */ function normalizePathIndexes(pathValue) { if (Array.isArray(pathValue)) { const indexes = pathValue.map(value => Number.parseInt(value, 10)); return indexes.every(Number.isInteger) && indexes.every(index => index >= 0) ? indexes : null; } const raw = typeof pathValue === 'string' ? pathValue.trim() : ''; if (!raw) { return []; } const separator = raw.includes('.') ? '.' : '_'; const parts = raw.split(separator).filter(Boolean); if (!parts.length) { return []; } const indexes = parts.map(part => Number.parseInt(part, 10)); if (!indexes.every(Number.isInteger)) { return null; } if (separator === '.') { return indexes.every(index => index > 0) ? indexes.map(index => index - 1) : null; } return indexes.every(index => index >= 0) ? indexes : null; } /** * Convert one zero-based path index list into public path metadata. * * @param {number[]} indexes Zero-based indexes. * @returns {{path: string, pathLabel: string, depth: number}} Path metadata. */ function buildPathMeta(indexes) { const safeIndexes = Array.isArray(indexes) ? indexes.filter(Number.isInteger) : []; return { path: safeIndexes.join('_'), pathLabel: safeIndexes.map(index => index + 1).join('.'), depth: Math.max(0, safeIndexes.length - 1), }; } /** * Return the direct child list element for one list item. * * @param {HTMLLIElement|null} listItem Candidate list item. * @returns {HTMLElement|null} Direct nested list, if present. */ function getDirectChildList(listItem) { if (!listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI') { return null; } return Array.from(listItem.children || []).find(child => ( child.tagName === 'UL' || child.tagName === 'OL' )) || null; } /** * Return the preferred nested list tag for one list item. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Parent list item. * @returns {string} `UL` or `OL`. */ function getPreferredChildListTagName(tracker, listItem) { const directChildList = getDirectChildList(listItem); if (directChildList) { return directChildList.tagName; } const parentList = listItem?.parentElement; if (parentList && (parentList.tagName === 'UL' || parentList.tagName === 'OL')) { return parentList.tagName; } return tracker?.listElement?.tagName === 'OL' ? 'OL' : 'UL'; } /** * Return one direct child list for a list item, creating it only when needed. * * Outdent can promote one item and then re-home its trailing siblings beneath * that promoted item. When the promoted item already owns a child list, those * siblings must be appended into the existing list so the DOM mirrors native * editor behavior instead of creating duplicate sibling list wrappers. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Parent list item. * @param {string} preferredTagName Fallback list tag name. * @returns {HTMLElement|null} Direct child list element. */ function ensureDirectChildList(tracker, listItem, preferredTagName = '') { if (!listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI') { return null; } let childList = getDirectChildList(listItem); if (childList) { return childList; } childList = document.createElement( preferredTagName || getPreferredChildListTagName(tracker, listItem) ); childList.classList.add('wp-block-list'); listItem.appendChild(childList); return childList; } /** * Remove empty nested list wrappers up the ancestry chain. * * The root list element is never removed, even when it becomes empty. * * @param {Object|null} tracker Active list tracker. * @param {HTMLElement|null} startList First candidate nested list. * @returns {void} */ function cleanupEmptyAncestorLists(tracker, startList) { let currentList = startList; while ( currentList && currentList !== tracker?.listElement && currentList.nodeType === Node.ELEMENT_NODE && (currentList.tagName === 'UL' || currentList.tagName === 'OL') && !getDirectListItems(currentList).length ) { const parentItem = currentList.parentElement?.tagName === 'LI' ? currentList.parentElement : null; currentList.remove(); currentList = parentItem ? parentItem.parentElement : null; } } /** * Build one new list item element from a structural operation payload. * * @param {Object|null} operation Candidate operation payload. * @returns {HTMLLIElement} New list item element. */ function buildListItemFromOperation(operation) { const li = document.createElement('li'); const directSurface = ensureDirectItemTextSurface(li); const runtimeUuid = normalizeRuntimeUuid( operation?.itemUuid ?? operation?.newItemUuid ); const html = typeof operation?.contentHtml === 'string' ? operation.contentHtml : (typeof operation?.html === 'string' ? operation.html : ''); const text = typeof operation?.contentText === 'string' ? operation.contentText : (typeof operation?.text === 'string' ? operation.text : ''); if (html) { directSurface.innerHTML = html; } else if (text) { directSurface.textContent = text; } if (runtimeUuid) { li.setAttribute(LIST_ITEM_RUNTIME_UUID_ATTR, runtimeUuid); } return li; } /** * Copy one donor item's structural/style attributes onto the destination list * item while preserving the destination runtime UUID. * * Native Enter list splitting happens inside the browser's contenteditable * engine, so the new sibling inherits the source `li` element's attributes * such as class and style automatically. API-driven insert/move operations * should mirror that behavior by cloning the donor item's `li` attributes, * except for the session-scoped runtime UUID which must stay unique. * * @param {HTMLLIElement|null} listItem Destination list item. * @param {HTMLLIElement|null} donorItem Style/structure donor item. * @returns {void} */ function copyDonorItemAttributes(listItem, donorItem) { if ( !listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI' || !donorItem || donorItem.nodeType !== Node.ELEMENT_NODE || donorItem.tagName !== 'LI' ) { return; } const destinationRuntimeUuid = normalizeRuntimeUuid( listItem.getAttribute(LIST_ITEM_RUNTIME_UUID_ATTR) ); Array.from(listItem.attributes || []).forEach(attr => { if (attr?.name === LIST_ITEM_RUNTIME_UUID_ATTR) { return; } listItem.removeAttribute(attr.name); }); Array.from(donorItem.attributes || []).forEach(attr => { if (attr?.name === LIST_ITEM_RUNTIME_UUID_ATTR) { return; } listItem.setAttribute(attr.name, attr.value); }); if (destinationRuntimeUuid) { listItem.setAttribute(LIST_ITEM_RUNTIME_UUID_ATTR, destinationRuntimeUuid); } } /** * Reassign one list item's structural ID from its destination styling * context. * * When an explicit target item is known, its structural `data-item-id` * becomes the style donor for insert-before, insert-after, move-before, and * move-after commands. This keeps the inheritance rule simple and matches the * public API's explicit `targetItemUuid` model. * * For internal list-path insert/move cases that do not resolve through one * target item, fall back to the local destination neighbors. If there is no * neighboring item at all, clear the structural ID so the next tracker rebuild * seeds a fresh item identity instead of accidentally preserving source attrs. * * @param {HTMLLIElement|null} listItem Destination list item. * @param {HTMLLIElement|null} targetItem Explicit style donor item. * @returns {string} Applied structural ID or an empty string. */ function inheritDestinationItemId(listItem, targetItem = null) { if (!listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI') { return ''; } const explicitTargetItem = targetItem && targetItem.nodeType === Node.ELEMENT_NODE && targetItem.tagName === 'LI' ? targetItem : null; const previousItem = listItem.previousElementSibling?.tagName === 'LI' ? listItem.previousElementSibling : null; const nextItem = listItem.nextElementSibling?.tagName === 'LI' ? listItem.nextElementSibling : null; const inheritedId = normalizeRuntimeUuid( explicitTargetItem?.getAttribute(LIST_ITEM_ID_ATTR) || previousItem?.getAttribute(LIST_ITEM_ID_ATTR) || nextItem?.getAttribute(LIST_ITEM_ID_ATTR) ); if (inheritedId) { listItem.setAttribute(LIST_ITEM_ID_ATTR, inheritedId); return inheritedId; } listItem.removeAttribute(LIST_ITEM_ID_ATTR); return ''; } /** * Apply one remove-list-item operation. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Target list item. * @returns {boolean} True when the mutation applied. */ function applyRemoveListItemOperation(tracker, listItem) { if (!tracker?.listElement || !listItem) { return false; } const oldParentList = listItem.parentElement; listItem.remove(); cleanupEmptyAncestorLists(tracker, oldParentList); return true; } /** * Apply one indent-list-item operation. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Target list item. * @returns {boolean} True when the mutation applied. */ function applyIndentListItemOperation(tracker, listItem) { if (!tracker?.listElement || !listItem) { return false; } const previousItem = listItem.previousElementSibling?.tagName === 'LI' ? listItem.previousElementSibling : null; if (!previousItem) { return false; } const nestedList = ensureDirectChildList(tracker, previousItem); nestedList.appendChild(listItem); return true; } /** * Apply one outdent-list-item operation. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Target list item. * @returns {boolean} True when the mutation applied. */ function applyOutdentListItemOperation(tracker, listItem) { if (!tracker?.listElement || !listItem) { return false; } const parentList = listItem.parentElement; const parentItem = parentList?.parentElement?.tagName === 'LI' ? parentList.parentElement : null; if (!parentList || !parentItem) { return false; } const ancestorList = parentItem.parentElement; const followingSiblings = []; let next = listItem.nextElementSibling; while (next) { followingSiblings.push(next); next = next.nextElementSibling; } ancestorList.insertBefore(listItem, parentItem.nextElementSibling); if (followingSiblings.length) { const nestedList = ensureDirectChildList(tracker, listItem, parentList.tagName); followingSiblings.forEach(sibling => nestedList.appendChild(sibling)); } cleanupEmptyAncestorLists(tracker, parentList); return true; } /** * Apply one toggle-list-type operation. * * @param {Object|null} tracker Active list tracker. * @param {Object} rawOperation Structural operation payload. * @param {Object} options Apply-operation options. * @returns {boolean} True when the mutation applied. */ function applyToggleListTypeOperation(tracker, rawOperation, options = {}) { if (!tracker?.listElement) { return false; } const listPath = rawOperation.listPath ?? rawOperation.list_path ?? ''; const targetList = getListByPath(tracker, listPath); const editorHost = options?.editorHost && typeof options.editorHost.changeListType === 'function' ? options.editorHost : null; const requestedType = targetList ? ( normalizeListTypeTagName( rawOperation.value ?? rawOperation.listType ?? rawOperation.list_type ?? rawOperation.ordered ) || (targetList.tagName === 'OL' ? 'UL' : 'OL') ) : ''; if ( !targetList || !requestedType || targetList.tagName === requestedType || !editorHost ) { return false; } const nextList = editorHost.changeListType(targetList, requestedType.toLowerCase(), { saveHistory: options.saveHistory !== false, restoreCursor: options.restoreCursor !== false, }); return !!nextList; } /** * Apply one update-list-item-text operation. * * @param {HTMLLIElement} listItem Target list item. * @param {Object} rawOperation Structural operation payload. * @returns {boolean} True when the mutation applied. */ function applyUpdateListItemTextOperation(listItem, rawOperation) { if (!listItem) { return false; } const directSurface = ensureDirectItemTextSurface(listItem); const html = typeof rawOperation?.contentHtml === 'string' ? rawOperation.contentHtml : (typeof rawOperation?.html === 'string' ? rawOperation.html : ''); const text = typeof rawOperation?.contentText === 'string' ? rawOperation.contentText : (typeof rawOperation?.text === 'string' ? rawOperation.text : ''); if (!directSurface) { return false; } directSurface.innerHTML = ''; if (html) { directSurface.innerHTML = html; } else if (text) { directSurface.textContent = text; } return true; } /** * Insert one new list item relative to explicit before/after/list anchors. * * @param {Object|null} tracker Active list tracker. * @param {Object} rawOperation Structural operation payload. * @param {Object} trackerApi List tracker API surface. * @returns {boolean} True when the mutation applied. */ function applyInsertListItemOperation(tracker, rawOperation, trackerApi) { if (!tracker?.listElement || !trackerApi) { return false; } const beforePath = rawOperation.beforePath ?? rawOperation.before_path ?? ''; const afterPath = rawOperation.afterPath ?? rawOperation.after_path ?? ''; const listPath = rawOperation.listPath ?? rawOperation.list_path ?? ''; const beforeItem = trackerApi.getItemByPath(tracker, beforePath); const afterItem = trackerApi.getItemByPath(tracker, afterPath); const newListItem = buildListItemFromOperation(rawOperation); if (beforeItem?.parentElement) { beforeItem.parentElement.insertBefore(newListItem, beforeItem); copyDonorItemAttributes(newListItem, beforeItem); inheritDestinationItemId(newListItem, beforeItem); return true; } if (afterItem?.parentElement) { afterItem.parentElement.insertBefore(newListItem, afterItem.nextElementSibling); copyDonorItemAttributes(newListItem, afterItem); inheritDestinationItemId(newListItem, afterItem); return true; } const targetList = getListByPath(tracker, listPath); if (!targetList) { return false; } const position = typeof rawOperation.position === 'string' ? rawOperation.position.trim().toLowerCase() : 'append'; if (position === 'prepend' && targetList.firstElementChild) { targetList.insertBefore(newListItem, targetList.firstElementChild); } else { targetList.appendChild(newListItem); } inheritDestinationItemId(newListItem); return true; } /** * Move one existing list item relative to explicit before/after/list anchors. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Target list item. * @param {Object} rawOperation Structural operation payload. * @param {Object} trackerApi List tracker API surface. * @returns {boolean} True when the mutation applied. */ function applyMoveListItemOperation(tracker, listItem, rawOperation, trackerApi) { if (!tracker?.listElement || !listItem || !trackerApi) { return false; } const beforePath = rawOperation.beforePath ?? rawOperation.before_path ?? ''; const afterPath = rawOperation.afterPath ?? rawOperation.after_path ?? ''; const listPath = rawOperation.listPath ?? rawOperation.list_path ?? ''; const beforeItem = trackerApi.getItemByPath(tracker, beforePath); const afterItem = trackerApi.getItemByPath(tracker, afterPath); const oldParentList = listItem.parentElement; let didApply = false; if (beforeItem && beforeItem !== listItem && !listItem.contains(beforeItem)) { beforeItem.parentElement.insertBefore(listItem, beforeItem); copyDonorItemAttributes(listItem, beforeItem); inheritDestinationItemId(listItem, beforeItem); didApply = true; } else if (afterItem && afterItem !== listItem && !listItem.contains(afterItem)) { afterItem.parentElement.insertBefore(listItem, afterItem.nextElementSibling); copyDonorItemAttributes(listItem, afterItem); inheritDestinationItemId(listItem, afterItem); didApply = true; } else if (typeof listPath === 'string') { const targetList = getListByPath(tracker, listPath); const ownerItem = targetList?.parentElement?.tagName === 'LI' ? targetList.parentElement : null; if (targetList && ownerItem !== listItem && !listItem.contains(ownerItem || null)) { const position = typeof rawOperation.position === 'string' ? rawOperation.position.trim().toLowerCase() : 'append'; if (position === 'prepend' && targetList.firstElementChild) { targetList.insertBefore(listItem, targetList.firstElementChild); } else { targetList.appendChild(listItem); } inheritDestinationItemId(listItem); didApply = true; } } if (didApply) { cleanupEmptyAncestorLists(tracker, oldParentList); } return didApply; } /** * Resolve one tracked list element from a list-path payload. * * The root list lives at the empty path. Nested lists are addressed by the * tree path of the parent item that owns that child list. * * @param {Object|null} tracker Active list tracker. * @param {string|Array} pathValue Root-empty list path or parent-item path. * @returns {HTMLElement|null} Matching list element. */ function getListByPath(tracker, pathValue) { if (!tracker?.listElement) { return null; } if ( pathValue === '' || pathValue === null || typeof pathValue === 'undefined' || (Array.isArray(pathValue) && !pathValue.length) || (typeof pathValue === 'string' && !pathValue.trim()) ) { return tracker.listElement; } const parentItem = ListBlockTracker.getItemByPath(tracker, pathValue); return parentItem ? getDirectChildList(parentItem) : null; } /** * Normalize one requested list-type value to a DOM tag name. * * @param {*} value Candidate list-type value. * @returns {string} `OL`, `UL`, or an empty string. */ function normalizeListTypeTagName(value) { if (value === true) { return 'OL'; } if (value === false) { return 'UL'; } const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'ordered' || normalized === 'ol' || normalized === 'true') { return 'OL'; } if (normalized === 'unordered' || normalized === 'ul' || normalized === 'false') { return 'UL'; } return ''; } const ListBlockTracker = { active: null, /** * Initialize tracker for a list element * @param {HTMLElement} listElement - The UL or OL element * @param {Object} originalBlock - The complete WordPress block structure */ init(listElement, originalBlock = {}) { if (!createBlock || !serializeBlocks) { console.error('wp.blocks not available'); } const tracker = { listElement, originalBlock: JSON.parse(JSON.stringify(originalBlock)), uuidMap: new Map(), // uuid -> {type, attrs, element} domMap: new WeakMap(), // element -> uuid runtimeUuidMap: new Map(), // runtimeUuid -> {type, element} runtimeDomMap: new WeakMap() // element -> runtimeUuid }; // Attach tracker to element to avoid singleton issues listElement._mwpListTracker = tracker; // Build UUID tracking from DOM and original block structure this.buildFromDOM(tracker, listElement, originalBlock); this.active = tracker; return tracker; }, /** * Build UUID mappings from DOM and original block structure * Assigns UUIDs to all lists and list items, mapping to their original attrs */ buildFromDOM(tracker, listElement, originalBlock) { const previousEntries = tracker.uuidMap instanceof Map ? new Map(tracker.uuidMap) : new Map(); const previousRuntimeEntries = tracker.runtimeUuidMap instanceof Map ? new Map(tracker.runtimeUuidMap) : new Map(); tracker.uuidMap.clear(); tracker.domMap = new WeakMap(); tracker.runtimeUuidMap.clear(); tracker.runtimeDomMap = new WeakMap(); tracker.listElement = listElement; this.syncEditableTextSurfaces(listElement); this.registerRuntimeIdentity( tracker, listElement, 'list', previousRuntimeEntries ); // Assign UUID to root list and map to original attrs const rootUuid = this.getOrCreateUuid(listElement, 'list'); const previousRootEntry = previousEntries.get(rootUuid); tracker.uuidMap.set(rootUuid, { type: 'list', attrs: previousRootEntry?.attrs ? cloneListAttrs(previousRootEntry.attrs, true) : cloneListAttrs(originalBlock.attrs || {}, true), element: listElement }); tracker.domMap.set(listElement, rootUuid); // Recursively process list structure this.processListRecursive( tracker, listElement, originalBlock.innerBlocks || [], previousEntries, previousRuntimeEntries ); }, /** * Recursively process list items and nested lists, assigning UUIDs */ processListRecursive( tracker, listElement, originalItems, previousEntries = new Map(), previousRuntimeEntries = new Map() ) { const items = getDirectListItems(listElement); items.forEach((li, index) => { this.syncEditableTextSurfaces(li); const originalItem = originalItems[index] || {}; this.registerRuntimeIdentity( tracker, li, 'item', previousRuntimeEntries ); const itemUuid = this.getOrCreateUuid(li, 'item'); const previousItemEntry = previousEntries.get(itemUuid); // Map UUID to original item attrs tracker.uuidMap.set(itemUuid, { type: 'item', attrs: previousItemEntry?.attrs ? JSON.parse(JSON.stringify(previousItemEntry.attrs || {})) : JSON.parse(JSON.stringify(originalItem.attrs || {})), element: li }); tracker.domMap.set(li, itemUuid); // Handle nested lists const nestedList = getDirectChildList(li); if (nestedList) { const originalNested = originalItem.innerBlocks?.[0] || {}; this.registerRuntimeIdentity( tracker, nestedList, 'list', previousRuntimeEntries ); const nestedUuid = this.getOrCreateUuid(nestedList, 'list'); const previousNestedEntry = previousEntries.get(nestedUuid); // Map nested list attrs without plugin UUID ownership. // The tracker still needs a temporary DOM identity for list // editing, but persisting mwpSfeUuid* on nested lists would // fracture one logical list into multiple save/history roots. tracker.uuidMap.set(nestedUuid, { type: 'list', attrs: previousNestedEntry?.attrs ? cloneListAttrs(previousNestedEntry.attrs, false) : cloneListAttrs(originalNested.attrs || {}, false), element: nestedList }); tracker.domMap.set(nestedList, nestedUuid); // Recurse into nested list this.processListRecursive( tracker, nestedList, originalNested.innerBlocks || [], previousEntries, previousRuntimeEntries ); } }); }, /** * Register one list or list-item runtime identity on the tracker. * * @param {Object} tracker Active list tracker. * @param {Element} element Live list or list-item element. * @param {string} type Supported runtime identity type. * @param {Map} previousRuntimeEntries Previous runtime entry map. * @returns {string} Resolved runtime UUID. */ registerRuntimeIdentity( tracker, element, type, previousRuntimeEntries = new Map() ) { const runtimeUuid = this.getOrCreateRuntimeUuid( tracker, element, type, previousRuntimeEntries ); if (!runtimeUuid) { return ''; } tracker.runtimeUuidMap.set(runtimeUuid, { type, element, }); tracker.runtimeDomMap.set(element, runtimeUuid); return runtimeUuid; }, /** * Return whether one runtime UUID is already owned by a different element. * * Native contenteditable list splitting can clone DOM attributes from the * source item into the newly created sibling. When that happens, the new * runtime UUID must be reseeded so each live cursor target stays unique. * * @param {Object} tracker Active list tracker. * @param {string} runtimeUuid Candidate runtime UUID. * @param {Element|null} element Element requesting that UUID. * @returns {boolean} True when the UUID belongs elsewhere. */ isRuntimeUuidClaimedByDifferentElement(tracker, runtimeUuid, element) { const normalizedRuntimeUuid = normalizeRuntimeUuid(runtimeUuid); if (!tracker?.runtimeUuidMap || !normalizedRuntimeUuid) { return false; } const existingEntry = tracker.runtimeUuidMap.get(normalizedRuntimeUuid); return !!(existingEntry?.element && existingEntry.element !== element); }, /** * Get the inherited structural ID for an element or seed it from the * runtime UUID when it does not exist yet. * * Structural IDs may be intentionally copied by native list splitting so * related items can retain style inheritance. They are not treated as * unique runtime cursor identifiers. */ getOrCreateUuid(element, type) { const attrName = type === 'list' ? LIST_ID_ATTR : LIST_ITEM_ID_ATTR; let uuid = element.getAttribute(attrName); if (!uuid) { uuid = normalizeRuntimeUuid( element.getAttribute( type === 'list' ? LIST_RUNTIME_UUID_ATTR : LIST_ITEM_RUNTIME_UUID_ATTR ) ) || this.generateTempUuid(); element.setAttribute(attrName, uuid); } return uuid; }, /** * Return the existing runtime UUID for one element or create one. * * Caller-supplied runtime UUIDs win for newly created items/lists. When an * element already belongs to the previous tracker build, its existing * runtime UUID is preserved so API references remain stable across rebuilds. * * @param {Object} tracker Active list tracker. * @param {Element} element Live list or list-item element. * @param {string} type Supported runtime identity type. * @param {Map} previousRuntimeEntries Previous runtime entry map. * @returns {string} Session-scoped runtime UUID. */ getOrCreateRuntimeUuid( tracker, element, type, previousRuntimeEntries = new Map() ) { const attrName = getRuntimeUuidAttributeName(type); if (!attrName || !isRuntimeIdentityElement(element, type)) { return ''; } let runtimeUuid = normalizeRuntimeUuid(element.getAttribute(attrName)); if (!runtimeUuid) { for (const [candidateUuid, entry] of previousRuntimeEntries.entries()) { if (entry?.element === element && entry.type === type) { runtimeUuid = normalizeRuntimeUuid(candidateUuid); break; } } } if (this.isRuntimeUuidClaimedByDifferentElement(tracker, runtimeUuid, element)) { runtimeUuid = ''; } if (!runtimeUuid) { runtimeUuid = this.generateTempUuid(); } element.setAttribute(attrName, runtimeUuid); return runtimeUuid; }, /** * Generate a RFC4122 version 4 UUID */ generateTempUuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }, /** * Return the zero-based tree path for one list item inside the tracked list. * * @param {Object} tracker Active list tracker. * @param {HTMLLIElement} listItem Candidate list item. * @returns {number[]|null} Zero-based indexes, or null on failure. */ getPathIndexesForItem(tracker, listItem) { if ( !tracker?.listElement || !listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI' || !tracker.listElement.contains(listItem) ) { return null; } const indexes = []; let currentItem = listItem; while (currentItem && tracker.listElement.contains(currentItem)) { const parentList = currentItem.parentElement; if (!parentList || (parentList.tagName !== 'UL' && parentList.tagName !== 'OL')) { return null; } const siblingItems = getDirectListItems(parentList); const itemIndex = siblingItems.indexOf(currentItem); if (itemIndex < 0) { return null; } indexes.unshift(itemIndex); const nextItem = parentList.closest('li'); if (!nextItem || !tracker.listElement.contains(nextItem)) { break; } currentItem = nextItem; } return indexes.length ? indexes : null; }, /** * Return the direct list item currently living at one tree path. * * @param {Object} tracker Active list tracker. * @param {string|Array} pathValue Zero-based path value. * @returns {HTMLLIElement|null} Matching list item. */ getItemByPath(tracker, pathValue) { const indexes = normalizePathIndexes(pathValue); if (!tracker?.listElement || !Array.isArray(indexes) || !indexes.length) { return null; } let currentList = tracker.listElement; let currentItem = null; for (let index = 0; index < indexes.length; index++) { const itemIndex = indexes[index]; const siblingItems = getDirectListItems(currentList); currentItem = siblingItems[itemIndex] || null; if (!currentItem) { return null; } if (index === indexes.length - 1) { return currentItem; } currentList = getDirectChildList(currentItem); if (!currentList) { return null; } } return currentItem; }, /** * Resolve one list element back to its public list path. * * The root list is addressed as an empty string. Nested child lists are * addressed by the path of the parent list item that owns them. * * @param {Object|null} tracker Active list tracker. * @param {HTMLElement|null} listElement Candidate list element. * @returns {string} Root-relative list path. */ getPathForList(tracker, listElement) { if (!tracker) tracker = this.active; if (!tracker?.listElement || !listElement || listElement.nodeType !== Node.ELEMENT_NODE) { return ''; } if (listElement === tracker.listElement) { return ''; } const parentItem = ( listElement.parentElement && listElement.parentElement.tagName === 'LI' ) ? listElement.parentElement : null; if (!parentItem) { return ''; } const indexes = this.getPathIndexesForItem(tracker, parentItem); return Array.isArray(indexes) ? indexes.join('_') : ''; }, /** * Return one tracked list item by runtime UUID. * * @param {Object|null} tracker Active list tracker. * @param {string} runtimeUuid Session-scoped item UUID. * @returns {HTMLLIElement|null} Matching list item. */ getItemByRuntimeUuid(tracker, runtimeUuid) { if (!tracker) tracker = this.active; const element = findElementByRuntimeUuid(tracker, runtimeUuid, 'item'); return element && element.tagName === 'LI' ? element : null; }, /** * Return one tracked list by runtime UUID. * * @param {Object|null} tracker Active list tracker. * @param {string} runtimeUuid Session-scoped list UUID. * @returns {HTMLElement|null} Matching list element. */ getListByRuntimeUuid(tracker, runtimeUuid) { if (!tracker) tracker = this.active; const element = findElementByRuntimeUuid(tracker, runtimeUuid, 'list'); return isListElementForRuntimeLookup(element) ? element : null; }, /** * Return the runtime UUID for one tracked list item. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement} listItem Live list item. * @returns {string} Session-scoped item UUID. */ getRuntimeUuidForItem(tracker, listItem) { if (!tracker) tracker = this.active; if (!tracker?.listElement || !listItem || listItem.tagName !== 'LI') { return ''; } return normalizeRuntimeUuid( listItem.getAttribute(LIST_ITEM_RUNTIME_UUID_ATTR) || tracker.runtimeDomMap?.get(listItem) ); }, /** * Return the runtime UUID for one tracked list. * * @param {Object|null} tracker Active list tracker. * @param {HTMLElement} listElement Live list element. * @returns {string} Session-scoped list UUID. */ getRuntimeUuidForList(tracker, listElement) { if (!tracker) tracker = this.active; if (!tracker?.listElement || !isListElementForRuntimeLookup(listElement)) { return ''; } return normalizeRuntimeUuid( listElement.getAttribute(LIST_RUNTIME_UUID_ATTR) || tracker.runtimeDomMap?.get(listElement) ); }, /** * Parse current DOM list structure to WordPress block format * Uses UUID to preserve original attrs, updates only content and ordered * * @param {HTMLElement} listElement Live UL/OL element. * @param {Object} tracker Active list tracker. * @param {boolean} allowUuidOwnership True only for the outermost * core/list. Nested lists must * serialize without persisted * plugin UUID attrs. */ parseListToBlock(listElement, tracker, allowUuidOwnership = true) { const isOrdered = listElement.tagName === 'OL'; const uuid = listElement.getAttribute(LIST_ID_ATTR); let attrs = { ordered: isOrdered }; if (uuid && tracker && tracker.uuidMap.has(uuid)) { const original = tracker.uuidMap.get(uuid); const originalAttrs = (Array.isArray(original.attrs) || !original.attrs) ? {} : cloneListAttrs(original.attrs, allowUuidOwnership); attrs = { ...originalAttrs, ordered: isOrdered }; } const listItems = Array.from(listElement.children).filter(el => el.tagName === 'LI'); const innerBlocks = listItems.map(li => this.parseListItemToBlock(li, tracker)); return createBlock('core/list', attrs, innerBlocks); }, /** * Parse a list item to WordPress block format * Preserves original attrs if UUID exists */ parseListItemToBlock(liElement, tracker) { const uuid = liElement.getAttribute(LIST_ITEM_ID_ATTR); let attrs = {}; if (uuid && tracker && tracker.uuidMap.has(uuid)) { const original = tracker.uuidMap.get(uuid); attrs = JSON.parse(JSON.stringify(original.attrs || {})); } // Recurse into nested list using the live element before cloning const innerBlocks = []; const nestedListEl = liElement.querySelector(':scope > ul, :scope > ol'); if (nestedListEl) { innerBlocks.push(this.parseListToBlock(nestedListEl, tracker, false)); } const clone = liElement.cloneNode(true); const directSurface = clone.querySelector(`:scope > [${LIST_ITEM_TEXT_ATTR}="1"]`); let content = ''; if (directSurface) { const cleanedSurface = this.cleanElement(directSurface); content = cleanedSurface.innerHTML.trim(); } else { const nestedInClone = clone.querySelector(':scope > ul, :scope > ol'); if (nestedInClone) nestedInClone.remove(); const cleaned = this.cleanElement(clone); content = cleaned.innerHTML.trim(); } return createBlock('core/list-item', { ...attrs, content }, innerBlocks); }, /** * Clean element using ElementPrep * Removes editing artifacts while preserving UUIDs and content */ cleanElement(element) { if (SFE.ElementPrep) { return SFE.ElementPrep.clean(element, { removeIdentity: true, removeControls: true, clone: false // Already working with a clone }); } console.error('ElementPrep not found'); return element; }, /** * Serialize the current list structure to WordPress block format * Preserves all original attrs, updates only content and ordered */ serialize(tracker) { if (!tracker) tracker = this.active; if (!tracker) return null; const block = this.parseListToBlock(tracker.listElement, tracker); return serializeBlocks([block]); }, /** * Update the tracker's element reference * Used when the list element is replaced (e.g., OL to UL conversion) */ updateElement(tracker, newElement) { if (!tracker) tracker = this.active; if (!tracker) return; // Ensure new element keeps the tracker reference newElement._mwpListTracker = tracker; this.syncEditableTextSurfaces(newElement); tracker.listElement = newElement; // Update element references in uuidMap tracker.uuidMap.forEach((value, key) => { if (value.type === 'list') { const newEl = newElement.querySelector(`[${LIST_ID_ATTR}="${key}"]`) || (newElement.getAttribute(LIST_ID_ATTR) === key ? newElement : null); if (newEl) { value.element = newEl; } } else if (value.type === 'item') { const newEl = newElement.querySelector(`[${LIST_ITEM_ID_ATTR}="${key}"]`); if (newEl) { value.element = newEl; } } }); tracker.runtimeUuidMap.forEach((value, key) => { if (value.type === 'list') { const newEl = this.getListByRuntimeUuid(tracker, key); if (newEl) { value.element = newEl; tracker.runtimeDomMap.set(newEl, key); } } else if (value.type === 'item') { const newEl = this.getItemByRuntimeUuid(tracker, key); if (newEl) { value.element = newEl; tracker.runtimeDomMap.set(newEl, key); } } }); }, /** * Return one tree-aware structural snapshot for the active list. * * @param {Object|null} tracker Active list tracker. * @returns {Object|null} Lightweight structure snapshot. */ getStructure(tracker) { if (!tracker) tracker = this.active; if (!tracker?.listElement) { return null; } const buildListNode = (listElement, listPath = '', parentIndexes = []) => ({ listUuid: this.getRuntimeUuidForList(tracker, listElement), listPath, ordered: listElement.tagName === 'OL', items: getDirectListItems(listElement).map((listItem, index) => { const indexes = [ ...parentIndexes, index ]; const pathMeta = buildPathMeta(indexes); const directSurface = ensureDirectItemTextSurface(listItem); const nestedList = getDirectChildList(listItem); return { itemUuid: this.getRuntimeUuidForItem(tracker, listItem), path: pathMeta.path, pathLabel: pathMeta.pathLabel, depth: pathMeta.depth, contentHtml: directSurface ? directSurface.innerHTML.trim() : '', childList: nestedList ? buildListNode(nestedList, pathMeta.path, indexes) : null, }; }), }); return buildListNode(tracker.listElement, '', []); }, /** * Apply one primitive structural list operation against the live tracked * DOM. * * Public API calls are translated into this lower-level operation set by * the shared schema executor so the tracker only needs to understand the * canonical primitive mutation layer. * * Supported primitive kinds: * - `insert_list_item` * - `remove_list_item` * - `move_list_item` * - `indent_list_item` * - `outdent_list_item` * - `update_list_item_text` * - `toggle_list_type` * * @param {Object|null} tracker Active list tracker. * @param {Object} rawOperation Structural operation payload. * @returns {Object|null} Result summary when applied. */ applyOperation(tracker, rawOperation, options = {}) { if (!tracker) tracker = this.active; if (!tracker?.listElement || !rawOperation || typeof rawOperation !== 'object') { return null; } const kind = typeof rawOperation.kind === 'string' ? rawOperation.kind.trim().toLowerCase() : ''; const path = rawOperation.path ?? rawOperation.itemPath ?? rawOperation.item_path ?? ''; const listItem = this.getItemByPath(tracker, path); let didApply = false; if (kind === 'remove_list_item' && listItem) { didApply = applyRemoveListItemOperation(tracker, listItem); } else if (kind === 'indent_list_item' && listItem) { didApply = applyIndentListItemOperation(tracker, listItem); } else if (kind === 'outdent_list_item' && listItem) { didApply = applyOutdentListItemOperation(tracker, listItem); } else if (kind === 'toggle_list_type') { didApply = applyToggleListTypeOperation(tracker, rawOperation, options); } else if (kind === 'update_list_item_text' && listItem) { didApply = applyUpdateListItemTextOperation(listItem, rawOperation); } else if (kind === 'insert_list_item') { didApply = applyInsertListItemOperation(tracker, rawOperation, this); } else if (kind === 'move_list_item' && listItem) { didApply = applyMoveListItemOperation(tracker, listItem, rawOperation, this); } if (!didApply) { return null; } this.syncEditableTextSurfaces(tracker.listElement); this.buildFromDOM(tracker, tracker.listElement, tracker.originalBlock || {}); return { kind, structure: this.getStructure(tracker), }; }, /** * Ensure every list item in one tree has one direct text surface. * * @param {HTMLElement|null} rootElement Candidate list root or list item. * @returns {HTMLElement|null} Normalized root element. */ syncEditableTextSurfaces(rootElement) { if (!rootElement || rootElement.nodeType !== Node.ELEMENT_NODE) { return null; } if (rootElement.tagName === 'LI') { ensureDirectItemTextSurface(rootElement); Array.from(rootElement.children || []) .filter(child => isNestedListNode(child)) .forEach(childList => this.syncEditableTextSurfaces(childList)); return rootElement; } if (rootElement.tagName !== 'UL' && rootElement.tagName !== 'OL') { return rootElement; } Array.from(rootElement.children || []) .filter(child => child.tagName === 'LI') .forEach(li => this.syncEditableTextSurfaces(li)); return rootElement; }, /** * Ensure runtime UUID attributes are unique within one live list tree. * * Native contenteditable list splitting can clone `li` and nested list * attributes verbatim before FrontEdit regains control. This pass reseeds only the * session-scoped runtime UUID attributes so freshly created structures become * distinct live API/editor targets immediately, while leaving `data-item-id` * and `data-list-id` untouched for their separate responsibilities. * * @param {HTMLElement|null} rootElement Candidate list root. * @returns {HTMLElement|null} Normalized root element. */ ensureUniqueRuntimeUuids(rootElement) { if ( !rootElement || rootElement.nodeType !== Node.ELEMENT_NODE || (rootElement.tagName !== 'UL' && rootElement.tagName !== 'OL') ) { return rootElement || null; } const seenListRuntimeUuids = new Set(); const seenItemRuntimeUuids = new Set(); const collectLists = [ rootElement, ...Array.from(rootElement.querySelectorAll('ul, ol')) ]; const collectItems = Array.from(rootElement.querySelectorAll('li')); collectLists.forEach(listElement => { const runtimeUuid = normalizeRuntimeUuid( listElement.getAttribute(LIST_RUNTIME_UUID_ATTR) ); if (!runtimeUuid) { return; } if (seenListRuntimeUuids.has(runtimeUuid)) { listElement.setAttribute(LIST_RUNTIME_UUID_ATTR, this.generateTempUuid()); return; } seenListRuntimeUuids.add(runtimeUuid); }); collectItems.forEach(listItem => { const runtimeUuid = normalizeRuntimeUuid( listItem.getAttribute(LIST_ITEM_RUNTIME_UUID_ATTR) ); if (!runtimeUuid) { return; } if (seenItemRuntimeUuids.has(runtimeUuid)) { listItem.setAttribute(LIST_ITEM_RUNTIME_UUID_ATTR, this.generateTempUuid()); return; } seenItemRuntimeUuids.add(runtimeUuid); }); return rootElement; }, /** * Ensure one live list tree has the required structural IDs and runtime UUIDs. * * On first editor activation, list elements may not yet have either identity * attribute family. Seed both from one generated UUID per element so the * initial history snapshot captures stable targeting data. Later native list * splits may intentionally copy the structural IDs; in those cases this pass * preserves the structural IDs and only reseeds duplicated runtime UUIDs. * * @param {HTMLElement|null} rootElement Candidate list root. * @returns {HTMLElement|null} Normalized root element. */ ensureIdentityAttributes(rootElement) { if ( !rootElement || rootElement.nodeType !== Node.ELEMENT_NODE || (rootElement.tagName !== 'UL' && rootElement.tagName !== 'OL') ) { return rootElement || null; } const syncIdentityPair = (element, structuralAttrName, runtimeAttrName) => { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return; } let structuralId = normalizeRuntimeUuid( element.getAttribute(structuralAttrName) ); let runtimeUuid = normalizeRuntimeUuid( element.getAttribute(runtimeAttrName) ); if (!structuralId && !runtimeUuid) { runtimeUuid = this.generateTempUuid(); structuralId = runtimeUuid; } else if (!runtimeUuid) { runtimeUuid = structuralId; } else if (!structuralId) { structuralId = runtimeUuid; } element.setAttribute(structuralAttrName, structuralId); element.setAttribute(runtimeAttrName, runtimeUuid); }; syncIdentityPair(rootElement, LIST_ID_ATTR, LIST_RUNTIME_UUID_ATTR); Array.from(rootElement.querySelectorAll('ul, ol')).forEach(listElement => { syncIdentityPair(listElement, LIST_ID_ATTR, LIST_RUNTIME_UUID_ATTR); }); Array.from(rootElement.querySelectorAll('li')).forEach(listItem => { syncIdentityPair(listItem, LIST_ITEM_ID_ATTR, LIST_ITEM_RUNTIME_UUID_ATTR); }); return this.ensureUniqueRuntimeUuids(rootElement); }, getDirectItemTextSurface, normalizePathIndexes, buildPathMeta, getRuntimeUuidAttributeName, getListRuntimeUuidAttributeName() { return LIST_RUNTIME_UUID_ATTR; }, getListItemRuntimeUuidAttributeName() { return LIST_ITEM_RUNTIME_UUID_ATTR; }, getListIdAttributeName() { return LIST_ID_ATTR; }, getListItemIdAttributeName() { return LIST_ITEM_ID_ATTR; }, /** * Destroy tracker and clean up */ destroy(tracker) { if (!tracker) tracker = this.active; if (!tracker) return; tracker.uuidMap.clear(); tracker.runtimeUuidMap.clear(); if (tracker.listElement) { delete tracker.listElement._mwpListTracker; } if (this.active === tracker) this.active = null; } }; /** * Return whether one element is a live list node for runtime lookup helpers. * * @param {Element|null} element Candidate DOM element. * @returns {boolean} True when the element is `UL` or `OL`. */ function isListElementForRuntimeLookup(element) { return !!( element && element.nodeType === Node.ELEMENT_NODE && (element.tagName === 'UL' || element.tagName === 'OL') ); } // Expose globally SFE.ListBlockTracker = ListBlockTracker; })(); .elementor-element,.elementor-lightbox{--swiper-theme-color:#000;--swiper-navigation-size:44px;--swiper-pagination-bullet-size:6px;--swiper-pagination-bullet-horizontal-gap:6px}.elementor-element .swiper .swiper-slide figure,.elementor-lightbox .swiper .swiper-slide figure{line-height:0}.elementor-element .swiper .elementor-lightbox-content-source,.elementor-lightbox .swiper .elementor-lightbox-content-source{display:none}.elementor-element .swiper .elementor-swiper-button,.elementor-element .swiper~.elementor-swiper-button,.elementor-lightbox .swiper .elementor-swiper-button,.elementor-lightbox .swiper~.elementor-swiper-button{color:hsla(0,0%,93%,.9);cursor:pointer;display:inline-flex;font-size:25px;position:absolute;top:50%;transform:translateY(-50%);z-index:1}.elementor-element .swiper .elementor-swiper-button svg,.elementor-element .swiper~.elementor-swiper-button svg,.elementor-lightbox .swiper .elementor-swiper-button svg,.elementor-lightbox .swiper~.elementor-swiper-button svg{fill:hsla(0,0%,93%,.9);height:1em;width:1em}.elementor-element .swiper .elementor-swiper-button-prev,.elementor-element .swiper~.elementor-swiper-button-prev,.elementor-lightbox .swiper .elementor-swiper-button-prev,.elementor-lightbox .swiper~.elementor-swiper-button-prev{left:10px}.elementor-element .swiper .elementor-swiper-button-next,.elementor-element .swiper~.elementor-swiper-button-next,.elementor-lightbox .swiper .elementor-swiper-button-next,.elementor-lightbox .swiper~.elementor-swiper-button-next{right:10px}.elementor-element .swiper .elementor-swiper-button.swiper-button-disabled,.elementor-element .swiper~.elementor-swiper-button.swiper-button-disabled,.elementor-lightbox .swiper .elementor-swiper-button.swiper-button-disabled,.elementor-lightbox .swiper~.elementor-swiper-button.swiper-button-disabled{opacity:.3}.elementor-element .swiper .swiper-image-stretch .swiper-slide .swiper-slide-image,.elementor-lightbox .swiper .swiper-image-stretch .swiper-slide .swiper-slide-image{width:100%}.elementor-element .swiper .swiper-horizontal>.swiper-pagination-bullets,.elementor-element .swiper .swiper-pagination-bullets.swiper-pagination-horizontal,.elementor-element .swiper .swiper-pagination-custom,.elementor-element .swiper .swiper-pagination-fraction,.elementor-element .swiper~.swiper-pagination-bullets.swiper-pagination-horizontal,.elementor-element .swiper~.swiper-pagination-custom,.elementor-element .swiper~.swiper-pagination-fraction,.elementor-lightbox .swiper .swiper-horizontal>.swiper-pagination-bullets,.elementor-lightbox .swiper .swiper-pagination-bullets.swiper-pagination-horizontal,.elementor-lightbox .swiper .swiper-pagination-custom,.elementor-lightbox .swiper .swiper-pagination-fraction,.elementor-lightbox .swiper~.swiper-pagination-bullets.swiper-pagination-horizontal,.elementor-lightbox .swiper~.swiper-pagination-custom,.elementor-lightbox .swiper~.swiper-pagination-fraction{bottom:5px}.elementor-element .swiper.swiper-cube .elementor-swiper-button,.elementor-element .swiper.swiper-cube~.elementor-swiper-button,.elementor-lightbox .swiper.swiper-cube .elementor-swiper-button,.elementor-lightbox .swiper.swiper-cube~.elementor-swiper-button{transform:translate3d(0,-50%,1px)}.elementor-element :where(.swiper-horizontal)~.swiper-pagination-bullets,.elementor-lightbox :where(.swiper-horizontal)~.swiper-pagination-bullets{bottom:5px;left:0;width:100%}.elementor-element :where(.swiper-horizontal)~.swiper-pagination-bullets .swiper-pagination-bullet,.elementor-lightbox :where(.swiper-horizontal)~.swiper-pagination-bullets .swiper-pagination-bullet{margin:0 var(--swiper-pagination-bullet-horizontal-gap,4px)}.elementor-element :where(.swiper-horizontal)~.swiper-pagination-progressbar,.elementor-lightbox :where(.swiper-horizontal)~.swiper-pagination-progressbar{height:4px;left:0;top:0;width:100%}.elementor-element.elementor-pagination-position-outside .swiper,.elementor-lightbox.elementor-pagination-position-outside .swiper{padding-bottom:30px}.elementor-element.elementor-pagination-position-outside .swiper .elementor-swiper-button,.elementor-element.elementor-pagination-position-outside .swiper~.elementor-swiper-button,.elementor-lightbox.elementor-pagination-position-outside .swiper .elementor-swiper-button,.elementor-lightbox.elementor-pagination-position-outside .swiper~.elementor-swiper-button{top:calc(50% - 30px / 2)}.elementor-element .elementor-swiper,.elementor-lightbox .elementor-swiper{position:relative}.elementor-element .elementor-main-swiper,.elementor-lightbox .elementor-main-swiper{position:static}.elementor-element.elementor-arrows-position-outside .swiper,.elementor-lightbox.elementor-arrows-position-outside .swiper{width:calc(100% - 60px)}.elementor-element.elementor-arrows-position-outside .swiper .elementor-swiper-button-prev,.elementor-element.elementor-arrows-position-outside .swiper~.elementor-swiper-button-prev,.elementor-lightbox.elementor-arrows-position-outside .swiper .elementor-swiper-button-prev,.elementor-lightbox.elementor-arrows-position-outside .swiper~.elementor-swiper-button-prev{left:0}.elementor-element.elementor-arrows-position-outside .swiper .elementor-swiper-button-next,.elementor-element.elementor-arrows-position-outside .swiper~.elementor-swiper-button-next,.elementor-lightbox.elementor-arrows-position-outside .swiper .elementor-swiper-button-next,.elementor-lightbox.elementor-arrows-position-outside .swiper~.elementor-swiper-button-next{right:0}/*! elementor-pro - v3.23.0 - 05-08-2024 */ "use strict";(self.webpackChunkelementor_pro=self.webpackChunkelementor_pro||[]).push([[253],{9999:(e,t,o)=>{var n=o(3203);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n(o(2463));class LoopFilter extends elementorModules.editor.utils.Module{onElementorInit(){this.taxonomyFilter=new r.default("taxonomy-filter")}}t.default=LoopFilter},2463:(e,t,o)=>{var n=o(8003).__;const r=o(8200);e.exports=r.extend({__construct(){this.cache={},r.prototype.__construct.apply(this,arguments)},onInit(){elementor.channels.editor.on("editor:widget:taxonomy-filter:section_taxonomy_filter:activated",this.onTaxonomyFilterSectionActive)},onTaxonomyFilterSectionActive(){this.updateSelectedElementOptions();const e=this.getEditorControlView("selected_element").getControlValue();e&&this.updateTaxonomyOptions(e)},updateSelectedElementOptions(){const e=this.getEditorControlView("selected_element"),t=e.getControlValue();(t?elementor.$previewContents[0].querySelector(`[data-elementor-id="${elementor.config.document.id}"] .elementor-element-${t}`):"")||e.setValue("");const o=elementor.$previewContents[0].querySelectorAll(`[data-elementor-id="${elementor.config.document.id}"] .elementor-widget-loop-grid`),r={"":n("Select a widget","elementor-pro")};o.length||(this.updateOptions("selected_element",r),e.setValue(""));let s=1;for(const e of o)r[e.dataset.id]=`${n("Loop Grid","elementor-pro")} ${s++}`;this.updateOptions("selected_element",r)},onElementChange(e,t){if("selected_element"!==e)return;const o=t.getControlValue();o?this.updateTaxonomyOptions(o):this.updateOptions("taxonomy",{"":n("Select a taxonomy","elementor-pro")})},getPostSourceQueryPostType(e){const t=e.settings.attributes.post_query_post_type;let o="";switch(t){case"current_query":o=elementorPro.config.loopFilter.mainQueryPostType;break;case"by_id":case"related":o="post";break;default:o=t}return o},getLoopQueryPostType(e){const t=elementor.getContainer(e);return"post"===t.settings.attributes._skin?this.getPostSourceQueryPostType(t):"product"},updateTaxonomyOptions(e){const t=this.getLoopQueryPostType(e);return this.getPostTypeTaxonomies(t).then((e=>e instanceof Response?!e.ok||400<=e.status?(this.displayErrorDialog(),{}):e.json():e)).catch((()=>(this.displayErrorDialog(),{}))).then((e=>{let o=e?.data||e;Object.keys(o).length?(o={"":n("Select a taxonomy","elementor-pro"),...o},this.cache[t]=o,this.updateOptions("taxonomy",o)):this.updateOptions("taxonomy",{"":n("No taxonomies found","elementor-pro")})}))},getPostTypeTaxonomies(e){return this.cache[e]&&Object.keys(this.cache[e]).length?Promise.resolve(this.cache[e]):this.fetchPostTypeTaxonomies(e)},fetchPostTypeTaxonomies:e=>fetch(`${elementorCommon.config.urls.rest}elementor-pro/v1/get-post-type-taxonomies`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":elementorWebCliConfig.nonce},body:JSON.stringify({post_type:e})}),displayErrorDialog(){elementorCommon.dialogsManager.createWidget("alert",{id:"e-filter-error-message",className:"e-filter__error-message",headerMessage:n("Something went wrong","elementor-pro"),message:n("We are experiencing technical difficulties on our end. Please try again to reconnect.","elementor-pro"),position:{my:"center center",at:"center center"},strings:{confirm:n("OK","elementor-pro")}}).show()}})}}]);@font-face{font-family:"Rubik";src:url("fonts/Rubik-Regular.ttf") format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:"Rubik";src:url("/fonts/Rubik-Bold.ttf") format("truetype");font-weight:bold;font-style:normal}#toplevel_page_tve_dash_section ul li.current a[href="admin.php?page=about_tve_theme_team"]{font-weight:600;color:#fff}.tve-about-us-content-wrapper{margin:0 0 0 -20px}html body #wpbody{font-family:Rubik,sans-serif;color:#434648;line-height:20px}#wpbody .tvd-header{margin:0 auto;padding:0 10%;display:flex;flex-direction:column;justify-content:space-between;align-items:baseline}#wpbody .tvd-header nav{height:auto !important}#wpbody .tvd-header div.tve-about-us-content{display:flex;flex-direction:row;justify-content:space-between;align-items:flex-start;padding:20px 0;gap:6%;letter-spacing:.03px;text-align:left;line-height:20px;color:#434648}#wpbody .tvd-header div.tve-about-us-content div.about-us{width:506px}#wpbody .tvd-header div.tve-about-us-content div.about-us p{line-height:20px;font-weight:400}#wpbody .tvd-header div.tve-about-us-content div.about-us p:first-child{margin:0 0 2rem 0;font-weight:700}#wpbody .tvd-header div.tve-about-us-content div.about-us p:last-child{margin:1rem 0 0 0}#wpbody .tvd-header div.tve-about-us-content div.about-us p span{font-weight:700}#wpbody .tvd-header div.tve-about-us-content div.about-us p a{color:#039be5;text-decoration:none}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team{text-align:center;position:relative;flex:1}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team img.image-behind-the-team{width:100%;height:auto;border-radius:13px 0 0 0;margin-bottom:15px}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team div.image-info{text-align:center}.gt-page-header{margin:0 auto;padding:10px 10%;display:flex;flex-direction:row;justify-content:space-between;align-items:center;min-width:100%;min-height:100px}.gt-page-header div.gt-page-header-left{width:45%}.gt-page-header div.gt-page-header-left span{display:block}.gt-page-header div.gt-page-header-left .tvd-header-title{font-size:18px;line-height:23px;margin-bottom:6px}.gt-page-header div.gt-page-header-left .tvd-header-summary{color:#737d87}.gt-page-header div.tvd-filter{height:36px;display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between;align-items:flex-start;gap:20px}.gt-page-header div.tvd-filter .tvd-search-elem{position:relative;width:100%}.gt-page-header div.tvd-filter .tvd-search-elem input[type=search]{background:#fff;min-height:36px !important;max-height:36px !important;height:36px !important;border:1px solid #cdd3d8 !important;font-size:14px;color:#9a9ea9;line-height:19.6px;padding:0 40px 0 10px !important;width:258px;border-radius:4px;display:block;margin:0}.gt-page-header div.tvd-filter .tvd-search-elem input[type=search]::placeholder{font-size:14px;color:#9a9ea9;line-height:19.6px}.gt-page-header div.tvd-filter .tvd-search-elem input[type=search]:focus,.gt-page-header div.tvd-filter .tvd-search-elem input[type=search]:hover{outline:none;box-shadow:none !important;border-color:#9a9ea9 !important}.gt-page-header div.tvd-filter .tvd-search-elem input[type=search]:focus::placeholder,.gt-page-header div.tvd-filter .tvd-search-elem input[type=search]:hover::placeholder{color:#444648}.gt-page-header div.tvd-filter .tvd-search-elem .tvd-tools-search-icon,.gt-page-header div.tvd-filter .tvd-search-elem .tvd-clear-search-icon{position:absolute;border:none;background-color:#fff;cursor:pointer;right:11px;top:3px;padding:0;height:32px;display:flex;align-items:center;justify-content:center;transform:translate(0, 0)}.gt-page-header div.tvd-filter .tvd-search-elem .tvd-tools-search-icon:active,.gt-page-header div.tvd-filter .tvd-search-elem .tvd-tools-search-icon:focus,.gt-page-header div.tvd-filter .tvd-search-elem .tvd-tools-search-icon:visited,.gt-page-header div.tvd-filter .tvd-search-elem .tvd-clear-search-icon:active,.gt-page-header div.tvd-filter .tvd-search-elem .tvd-clear-search-icon:focus,.gt-page-header div.tvd-filter .tvd-search-elem .tvd-clear-search-icon:visited{background:none}.gt-page-header div.tvd-filter .tvd-search-elem .tvd-tools-search-icon svg{height:16px;width:16px;fill:#b0b9c1}.gt-page-header div.tvd-filter .tvd-search-elem .tvd-clear-search-icon svg{height:36px;width:36px}.gt-page-header div.tvd-filter .tvd-filter-tools span.select2-container{height:36px;width:220px !important;border:1px solid #cdd3d8;background:var(--colour-surface-screen, rgb(255, 255, 255));padding:0 25px 0 10px;margin:0;border-radius:4px;display:block;line-height:19.6px;text-align:left}.gt-page-header div.tvd-filter .tvd-filter-tools span.select2-container .select2-selection--single .select2-selection__rendered{color:#444648;line-height:36px;font-size:14px;padding-top:0;padding-bottom:0}.gt-page-header div.tvd-filter .tvd-filter-tools span.select2-container--open{border-radius:4px 4px 0 0}.select2-results__option{line-height:19.6px;text-align:left;color:#626670}.select2-results__option{padding-top:4px;padding-bottom:5px}.select2-container--default .select2-results__option--highlighted[aria-selected]{background:var(--colour-type-primary, rgb(68, 70, 72))}.select2-container--default .select2-results__option[aria-selected=true]{background:none}.select2-container--default .select2-selection--single{border-bottom:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{top:60%;border:none;background:url("drop-down-open.svg") no-repeat center center;background-size:contain;width:7px;height:7px;display:inline-block;content:""}.select2-container--default .select2-selection--single .select2-selection__arrow b{top:60%;border:none;background:url("drop-down-closed.svg") no-repeat center center;background-size:contain;width:7px;height:7px;display:inline-block;content:""}.select2-dropdown{margin-top:-8px;box-shadow:none;border:1px solid #cdd3d8;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.select2-dropdown--below{padding-top:0;padding-bottom:2px}.select2-results__option{padding-top:4px;padding-bottom:5px}select.tools-category-select option{padding:20px 0 8px 12px !important;border:1px solid #bcbcbc !important}select.tools-category-select option:hover{background-color:#bcbcbc}.select2-container--default .select2-results__option--highlighted[aria-selected]{background:#f6f6f6 !important;color:#444648}.tvd-search-elem input[type=search]:hover+.tvd-tools-search-icon svg use{fill:#444648}.growth-tools-list{margin:0 auto;padding:1% 10% !important;display:flex;flex-direction:column;justify-content:space-between;align-items:center;min-width:100%}.growth-tools-list div.growth-tools-category-item{width:100%}.growth-tools-list div.growth-tools-category-item:first-child{margin:0}.growth-tools-list div.growth-tools-category-item:not(:first-child){margin:25px 0}.growth-tools-list div.growth-tools-category-item span.category-title{line-height:18.75px;letter-spacing:0;text-align:left;color:#434648;display:block;margin-bottom:8px}.growth-tools-list div.growth-tools-category-item div.growth-tools-card{border:1px solid rgba(205,211,216,.3019607843);background:#fff;padding:20px;border-radius:4px}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item{display:flex;flex-direction:row;justify-content:space-between;align-items:center;gap:20px}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item:not(:last-child){border-bottom:1px solid rgba(115,125,135,.2);padding-bottom:20px;margin-bottom:20px}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-logo{width:52px;height:52px;flex:none;display:flex;align-items:center;justify-content:center;margin:0;border:1px solid rgba(205,211,216,.3);border-radius:10px}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-logo svg{width:50px;height:50px;border-radius:10px;speak:none;display:inline-block;font-style:normal;font-weight:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-content{flex:1;padding:0;min-width:auto;max-width:inherit}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-content>br{display:none}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-content span{line-height:18px;letter-spacing:0;text-align:left;color:#737d87;display:block}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-content span.tool-name{font-size:14px;font-weight:700;margin-bottom:5px;cursor:pointer;color:#202223}.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item div.growth-tool-content span.tool-summary{font-size:13px;font-weight:400}.tve-btn{width:120px;height:36px;border-radius:3px;border:none;color:#fff;font-family:Rubik,sans-serif;font-size:14px;font-weight:500;line-height:16px;letter-spacing:-.1071429029px;text-align:center;transition:.3s;-webkit-transition:.3s;-moz-transition:.3s;-ms-transition:.3s;-o-transition:.3s}.tve-btn>span>svg{height:13px;width:13px;margin-bottom:-2px}svg.td-icon-learn-more,svg.td-icon-install-now,svg.td-icon-activate{margin-left:3px}svg.td-icon-get-started{margin-right:3px}.tve-btn-learn-more{background:#fff;color:#737d87;border:1px solid #cdd3d8}.tve-btn-learn-more:hover{background:rgba(205,211,216,.5);color:#737d87;border:1px solid #cdd3d8}.tve-btn-learn-more:active,.tve-btn-learn-more:focus{background:#fff;color:#737d87;border:1px solid #cdd3d8}.tve-btn-install-now{background:#32a2d3}.tve-btn-install-now:hover{background:#44c0f7}.tve-btn-install-now:active,.tve-btn-install-now:focus{background:#32a2d3}.tve-btn-action-installing{background:#32a2d3 linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2))}.tve-btn-action-activate{background:#32a2d3}.tve-btn-action-activate:hover{background:#44c0f7}.tve-btn-action-activate:focus,.tve-btn-action-activate:active{background:#32a2d3}.tve-btn-action-activating{background:#32a2d3 linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2))}.tve-btn-get-started{background:#4bb35e;border:1px solid #4bb35e}.tve-btn-get-started:hover{background:#6cd07f;border:1px solid #6cd07f}.tve-btn-get-started:active,.tve-btn-get-started:focus{background:#4bb35e;border:1px solid #4bb35e}.growth-tools-not-found{display:flex;align-items:center;flex-direction:column;justify-content:space-between;margin-top:20px}.growth-tools-not-found span{font-size:16px;line-height:18.75px;text-align:center;color:#434648;margin-bottom:20px}.growth-tools-not-found svg{display:block;height:70px;width:73px}@media(min-width: 1600px){#wpbody .tvd-header{width:1200px;padding:0 75px}.gt-page-header{width:1200px;padding:1% 0;min-width:0}.growth-tools-list div.growth-tools-category-item{width:1200px}}@media(max-width: 1360px){#wpbody .tvd-header div.tve-about-us-content div.about-us{width:460px}}@media(max-width: 1200px){#wpbody .tvd-header div.tve-about-us-content div.about-us{width:360px}}@media(max-width: 1160px){#wpbody .tvd-header{flex-direction:column;justify-content:flex-start;align-items:flex-start}#wpbody .tvd-header div.tve-about-us-content .about-us{width:360px}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team{width:100%}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team img.image-behind-the-team{width:100%;height:auto}.gt-page-header{flex-direction:column}.gt-page-header div.gt-page-header-left{width:100%}.gt-page-header div.tvd-filter{justify-content:flex-start;align-items:flex-start;margin-top:20px;gap:10px;margin-bottom:10px;width:100%}}@media(max-width: 768px){#wpbody .tvd-header div.tve-about-us-content{gap:6%}}@media(max-width: 1000px){#wpbody .tvd-header div.tve-about-us-content{flex-direction:column;gap:10px}#wpbody .tvd-header div.tve-about-us-content div.about-us{flex-direction:column;width:100%;padding:0;margin-bottom:10%}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team img{width:100%;height:auto}}@media(max-width: 600px){#wpbody .tvd-header{padding:1% 10%}#wpbody .tvd-header div.tve-about-us-content{flex-direction:column;gap:10px}#wpbody .tvd-header div.tve-about-us-content div.about-us{flex-direction:column;padding:0;margin-bottom:10%}#wpbody .tvd-header div.tve-about-us-content div.the-tve-team img{width:100%;height:auto}.gt-page-header{flex-direction:column}.gt-page-header div.gt-page-header-left{width:100%}.gt-page-header div.tvd-filter{flex-direction:column;height:auto;margin-top:20px;gap:10px;margin-bottom:10px}.gt-page-header div.tvd-filter div.tvd-filter-tools,.gt-page-header div.tvd-filter div.tvd-filter-search,.gt-page-header div.tvd-filter div.tvd-search-elem input[type=search],.gt-page-header div.tvd-filter div.tvd-filter-tools span.select2-container{width:100% !important}.tvd-flex{align-items:normal}span.select2-container{width:100% !important;max-width:100%}.growth-tools-card .growth-tools-card-item{flex-direction:column;align-items:flex-start}}@media(max-width: 500px){.growth-tools-list div.growth-tools-category-item div.growth-tools-card .growth-tools-card-item{flex-direction:column}} /** * External dependencies */ import { text, boolean } from '@storybook/addon-knobs'; import { useValidationContext, ValidationContextProvider, } from '@woocommerce/base-context'; /** * Internal dependencies */ import CouponInput from '../'; export default { title: 'WooCommerce Blocks/@base-components/CouponInput', component: CouponInput, }; const StoryComponent = ( { validCoupon, isLoading, invalidCouponText } ) => { const { setValidationErrors } = useValidationContext(); const onSubmit = ( coupon ) => { if ( coupon !== validCoupon ) { setValidationErrors( { coupon: invalidCouponText } ); } }; return ; }; export const Default = () => { const validCoupon = text( 'A valid coupon code', 'validcoupon' ); const invalidCouponText = text( 'Error message for invalid code', 'Invalid coupon code.' ); const isLoading = boolean( 'Toggle isLoading state', false ); return ( ); }; (o=>{var t={loading:!1,init:function(){var t;o("a[data-embed-checkout]").length&&(t=this,o(document).on("click","a[data-embed-checkout]",function(e){e.preventDefault(),t.modal.open(o(this))}))},modal:{open:function(e){this.$target=e,this.product_url&&(this.product_url,this.product_url==e.data("embed-checkout"))||(this.product_url=e.data("embed-checkout")),this.show_checkout()},setup:function(){this.$el&&(this.$el.remove(),this.$el=null);var e=o("#udp-modal-template").html();this.$el=o(e),window.addEventListener("message",function(e){var t=e.data;if(t&&t.action)switch(t.action){case"domready":this.$el.removeClass("loading");break;case"closemodal":o(document).trigger("udp/checkout/close",t.data,this.$target),this.close();break;case"ordercomplete":console.log("Order complete:",t.data),o(document).trigger("udp/checkout/done",t.data,this.$target)}}.bind(this))},close:function(e){e&&e.preventDefault(),o("body").removeClass("udp-modal-is-opened"),this.$iframe&&(this.$iframe.remove(),this.$iframe_container.remove()),this.$el.hide()},show_checkout:function(){window.open(this.product_url,"_blank","noopener, noreferrer")}}};jQuery(function(e){t.init()})})(jQuery);(function ( $ ) { /** * Generate pie/doughnut charts * * Legend must be generated manually. If color is array (gradient), then legend won't show it. */ $.fn.vcRoundChart = function () { this.each( function ( ) { var data, gradient, chart, i, j, $this = $( this ), ctx = $this.find( 'canvas' )[ 0 ].getContext( '2d' ), stroke_width = $this.data( 'vcStrokeWidth' ) ? parseInt( $this.data( 'vcStrokeWidth' ), 10 ) : 0, options = { showTooltips: $this.data( 'vcTooltips' ), animationEasing: $this.data( 'vcAnimation' ), segmentStrokeColor: $this.data( 'vcStrokeColor' ), segmentShowStroke: 0 !== stroke_width, segmentStrokeWidth: stroke_width, responsive: true }, color_keys = [ 'color', 'highlight' ]; // If plugin has been called on already initialized element, reload it if ( $this.data( 'chart' ) ) { $this.data( 'chart' ).destroy(); } data = $this.data( 'vcValues' ); ctx.canvas.width = $this.width(); ctx.canvas.height = $this.width(); // If color/highlight is array (of 2 colors), replace it with generated gradient for ( i = data.length - 1; 0 <= i; i -- ) { for ( j = color_keys.length - 1; 0 <= j; j -- ) { if ( 'object' === typeof( data[ i ][ color_keys[ j ] ] ) && 2 === data[ i ][ color_keys[ j ] ].length ) { gradient = ctx.createLinearGradient( 0, 0, 0, ctx.canvas.height ); gradient.addColorStop( 0, data[ i ][ color_keys[ j ] ][ 0 ] ); gradient.addColorStop( 1, data[ i ][ color_keys[ j ] ][ 1 ] ); data[ i ][ color_keys[ j ] ] = gradient; } } } if ( 'doughnut' === $this.data( 'vcType' ) ) { chart = new Chart( ctx ).Doughnut( data, options ); } else { chart = new Chart( ctx ).Pie( data, options ); } $this.data( 'vcChartId', chart.id ); // We can later access chart to call methods on it $this.data( 'chart', chart ); } ); return this; }; /** * Allows users to rewrite function inside theme. */ if ( 'function' !== typeof( window.vc_round_charts ) ) { window.vc_round_charts = function ( model_id ) { var selector = '.vc_round-chart'; if ( 'undefined' !== typeof( model_id ) ) { selector = '[data-model-id="' + model_id + '"] ' + selector; } $( selector ).vcRoundChart(); }; } $( document ).ready( function () { ! window.vc_iframe && vc_round_charts(); } ); }( jQuery )); https://www.emiratesrdf.ae/page-sitemap.xml 2021-10-28T09:18:18+00:00 https://www.emiratesrdf.ae/category-sitemap.xml 2026-08-14T11:45:53+00:00 https://www.emiratesrdf.ae/post_tag-sitemap.xml 2026-08-14T11:45:53+00:00