/** * Velvet Vibes — Flower Zone Theme Engine v5 * CSS + JS only. No HTML/Twig edits. * Fixes: search, nav hover submenu, centered sections, FAQ height, * testimonials pagination, footer Important + Legal links. */ (function () { "use strict"; if (window.__VV_STABLE_V4__) return; window.__VV_STABLE_V4__ = true; window.__VV_STABLE_V5__ = true; window.__VV_STABLE_V3__ = true; /* prevent older pasted scripts from double-running */ /* -------------------------------------------------------------------------- Boot — stop the page restyling itself in front of the customer. Every rule in style.css is scoped to body.vv-fz-theme, and that class used to be added inside init(), which only runs on DOMContentLoaded. So the browser painted the stock Salla theme first and swapped to ours a moment later — that is the jump people see while the store opens. Two things fix it. The theme classes go on as soon as
exists, long before init(), so the stylesheet applies to the very first paint. And the page stays cloaked (see the boot cloak in style.css) until the first pass of init() has finished rearranging the header, footer and sliders, so the DOM surgery happens off-screen rather than in view. -------------------------------------------------------------------------- */ var THEME_BODY_CLASSES = [ "vv-body", "vv-clean-theme", "vv-override-theme", "vv-fz-theme", ]; function applyThemeClasses() { if (!document.body) return false; try { THEME_BODY_CLASSES.forEach(function (name) { document.body.classList.add(name); }); } catch (e) {} return true; } /* The script may be injected into , before is parsed. Watch for it rather than waiting for DOMContentLoaded, which is far too late. */ if (!applyThemeClasses() && window.MutationObserver) { var bodyWatcher = new MutationObserver(function () { if (applyThemeClasses()) bodyWatcher.disconnect(); }); try { bodyWatcher.observe(document.documentElement, { childList: true }); } catch (e) {} } /* Lifting the cloak. The stylesheet also reveals on its own after a short delay, so a failure in here can never leave the store blank. */ function revealStore() { if (!document.body) return; try { document.body.classList.add("vv-fz-ready"); } catch (e) {} } window.addEventListener("load", revealStore); setTimeout(revealStore, 1500); var ANNOUNCEMENTS = []; var STORE_MAPS_URL = "https://maps.app.goo.gl/G14sF9B33K39R4sF6"; var BOOK_PARTY_URL = "/احجز-حفلتك/c276742401"; var announceIndex = 0; var announceTimer = null; var contentObserver = null; var resizeTimer = null; var searchApi = null; var wishlistBound = false; function q(selector, root) { try { return (root || document).querySelector(selector); } catch (e) { return null; } } function qa(selector, root) { try { return Array.from((root || document).querySelectorAll(selector)); } catch (e) { return []; } } function safe(fn) { try { fn(); } catch (e) { if (window.console && console.warn) console.warn("[Velvet Vibes]", e); } } function setImportant(el, prop, value) { if (!el) return; try { el.style.setProperty(prop, value, "important"); } catch (e) {} } function decodePath(path) { try { return decodeURIComponent(path || ""); } catch (e) { return path || ""; } } /* -------------------------------------------------------------------------- Header stability — pin logo bar + category links together -------------------------------------------------------------------------- */ function ensureStickyShell(header, main) { if (!header || !main) return null; var shell = q(".vv-header-stick", header); if (shell && shell.contains(main)) return shell; shell = document.createElement("div"); shell.className = "vv-header-stick"; shell.setAttribute("data-vv-sticky", "1"); if (main.parentNode) { main.parentNode.insertBefore(shell, main); } else { header.appendChild(shell); } shell.appendChild(main); var existingNav = q(".vv-desktop-nav", header) || q(".vv-desktop-nav"); if (existingNav && existingNav.parentNode !== shell) { shell.appendChild(existingNav); } return shell; } function ensureHeaderSpacer(shell) { if (!shell || !shell.parentNode) return null; var spacer = q(".vv-header-stick-spacer"); if (!spacer) { spacer = document.createElement("div"); spacer.className = "vv-header-stick-spacer"; spacer.setAttribute("aria-hidden", "true"); shell.parentNode.insertBefore(spacer, shell); } return spacer; } function forceMainnavInFlow(main) { if (!main) return; ["fixed-header", "fixed-pinned", "sticky", "fixed"].forEach(function (c) { /* keep class names for theme, but override geometry */ }); setImportant(main, "position", "relative"); setImportant(main, "top", "auto"); setImportant(main, "right", "auto"); setImportant(main, "bottom", "auto"); setImportant(main, "left", "auto"); setImportant(main, "transform", "none"); setImportant(main, "inset", "auto"); } function syncPinnedHeader() { var header = q("header.store-header"); var main = q("#mainnav.main-nav-container"); if (!header || !main) return; var shell = ensureStickyShell(header, main); if (!shell) return; var nav = q(".vv-desktop-nav", shell) || q(".vv-desktop-nav", header) || q(".vv-desktop-nav"); if (nav && nav.parentNode !== shell) shell.appendChild(nav); var mobile = window.matchMedia("(max-width: 1023px)").matches; var barH = mobile ? "64px" : "88px"; var topBar = q(".top-navbar", header); var threshold = topBar ? topBar.offsetHeight : 0; var shouldPin = (window.scrollY || window.pageYOffset || 0) >= Math.max(4, threshold - 2); setImportant(header, "position", "relative"); setImportant(header, "top", "auto"); setImportant(header, "transform", "none"); setImportant(header, "height", "auto"); setImportant(header, "max-height", "none"); forceMainnavInFlow(main); setImportant(main, "width", "100%"); setImportant(main, "height", barH); setImportant(main, "min-height", barH); setImportant(main, "max-height", barH); [q(".inner", main), q(".inner > .container", main), q(".inner > .container > .relative", main)].forEach( function (el) { if (!el) return; setImportant(el, "height", barH); setImportant(el, "max-height", barH); setImportant(el, "min-height", "0px"); }, ); if (nav) { if (mobile) { setImportant(nav, "display", "none"); } else { setImportant(nav, "display", "flex"); setImportant(nav, "visibility", "visible"); setImportant(nav, "opacity", "1"); setImportant(nav, "position", "relative"); setImportant(nav, "height", "54px"); setImportant(nav, "min-height", "54px"); setImportant(nav, "max-height", "54px"); setImportant(nav, "pointer-events", "auto"); setImportant(nav, "z-index", "120"); } } var spacer = ensureHeaderSpacer(shell); if (shouldPin) { shell.classList.add("is-pinned"); document.body.classList.add("vv-header-pinned"); setImportant(shell, "position", "fixed"); setImportant(shell, "top", "0px"); setImportant(shell, "left", "0px"); setImportant(shell, "right", "0px"); setImportant(shell, "width", "100%"); setImportant(shell, "z-index", "1000"); setImportant(shell, "background", mobile ? "#5c1a1a" : "#ffffff"); var total = shell.offsetHeight || (mobile ? 64 : 88 + 54); if (spacer) { setImportant(spacer, "display", "block"); setImportant(spacer, "height", total + "px"); } } else { shell.classList.remove("is-pinned"); document.body.classList.remove("vv-header-pinned"); setImportant(shell, "position", "relative"); setImportant(shell, "top", "auto"); setImportant(shell, "left", "auto"); setImportant(shell, "right", "auto"); setImportant(shell, "z-index", "110"); if (spacer) { setImportant(spacer, "height", "0px"); } } } function normalizeHeaderDimensions() { syncPinnedHeader(); } function watchThemeHeaderFight() { var main = q("#mainnav.main-nav-container"); if (!main || main.getAttribute("data-vv-pin-watch") === "1") return; main.setAttribute("data-vv-pin-watch", "1"); if (!window.MutationObserver) return; var timer = null; new MutationObserver(function () { clearTimeout(timer); timer = setTimeout(function () { safe(syncPinnedHeader); safe(clearInlineHeaderFight); }, 16); }).observe(main, { attributes: true, attributeFilter: ["style", "class"] }); } function applyAnnouncementMode(track) { if (!track) return; initAnnouncement(); } function initAnnouncement() { if (announceTimer) { clearInterval(announceTimer); announceTimer = null; } qa(".vv-clean-announcement, .vv-ann-row, .vv-ticker-wrap, .vv-ann-track").forEach(function (el) { el.remove(); }); qa(".store-header .top-navbar, .top-navbar").forEach(function (bar) { bar.style.setProperty("display", "none", "important"); bar.style.setProperty("height", "0px", "important"); bar.style.setProperty("min-height", "0px", "important"); bar.style.setProperty("max-height", "0px", "important"); bar.style.setProperty("margin", "0px", "important"); bar.style.setProperty("padding", "0px", "important"); bar.style.setProperty("border", "none", "important"); bar.style.setProperty("overflow", "hidden", "important"); }); } /* -------------------------------------------------------------------------- Professional Velvet Vibes search popup Custom branded modal + live Salla search when available. -------------------------------------------------------------------------- */ var searchState = { api: null, reducedMotion: false }; var pageScrollLock = { active: false, y: 0 }; try { searchState.reducedMotion = window.matchMedia( "(prefers-reduced-motion: reduce)", ).matches; } catch (e) {} function lockPageScroll() { if (pageScrollLock.active) return; pageScrollLock.active = true; pageScrollLock.y = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0; document.documentElement.classList.add("vv-searchbox-open"); document.body.classList.add("vv-searchbox-open"); } function unlockPageScroll() { pageScrollLock.active = false; document.documentElement.classList.remove("vv-searchbox-open"); document.body.classList.remove("vv-searchbox-open"); [document.documentElement, document.body].forEach(function (el) { if (!el) return; [ "vv-searchbox-open", "modal-open", "overflow-hidden", "no-scroll", "s-modal-opened", "swal2-shown", "swal2-height-auto", ].forEach(function (c) { el.classList.remove(c); }); var style = el.style; if (!style) return; var lockedPos = style.position === "fixed"; if (lockedPos) { var top = Math.abs(parseInt(style.top, 10) || 0); if (top) pageScrollLock.y = top; style.removeProperty("position"); style.removeProperty("top"); style.removeProperty("left"); style.removeProperty("right"); style.removeProperty("width"); style.removeProperty("height"); } style.removeProperty("overflow"); style.removeProperty("overflow-x"); style.removeProperty("overflow-y"); style.removeProperty("touch-action"); style.removeProperty("overscroll-behavior"); style.removeProperty("padding-right"); style.removeProperty("padding-left"); }); /* Force-clear any leftover Salla modal scroll lock, then let theme CSS win */ try { document.documentElement.style.setProperty("overflow", "visible", "important"); document.body.style.setProperty("overflow-y", "auto", "important"); document.body.style.setProperty("overflow-x", "hidden", "important"); document.body.style.setProperty("touch-action", "auto", "important"); document.body.style.setProperty("pointer-events", "auto", "important"); } catch (e) {} requestAnimationFrame(function () { try { document.documentElement.style.removeProperty("overflow"); document.body.style.removeProperty("overflow-y"); document.body.style.removeProperty("overflow-x"); document.body.style.removeProperty("touch-action"); document.body.style.removeProperty("pointer-events"); /* If computed style still locks Y (stuck Salla rule), keep an unlock override */ var bodyOv = window.getComputedStyle(document.body).overflowY; var htmlOv = window.getComputedStyle(document.documentElement).overflowY; if (bodyOv === "hidden") { document.body.style.setProperty("overflow-y", "auto", "important"); } if (htmlOv === "hidden") { document.documentElement.style.setProperty("overflow-y", "auto", "important"); } } catch (e2) {} try { window.scrollTo(0, pageScrollLock.y || 0); } catch (e3) {} }); /* Hide stray native search backdrops that can freeze clicks/scroll */ qa( "salla-search[data-testid='store-search-modal'], salla-modal.s-search-modal", ).forEach(function (el) { if (el.closest && el.closest(".vv-searchbox")) return; try { if (typeof el.close === "function") el.close(); } catch (e) {} el.classList.add("s-hidden", "vv-native-search-suppressed"); el.setAttribute("aria-hidden", "true"); el.style.setProperty("display", "none", "important"); el.style.setProperty("pointer-events", "none", "important"); }); qa(".s-modal-wrapper").forEach(function (el) { if (el.closest && el.closest(".vv-searchbox")) return; if (!el.querySelector(".s-search-input, salla-search, .s-search-modal")) return; el.classList.add("s-hidden"); el.style.setProperty("display", "none", "important"); el.style.setProperty("pointer-events", "none", "important"); }); try { if (window.salla && salla.event) { if (typeof salla.event.dispatch === "function") { salla.event.dispatch("search::close"); } else if (typeof salla.event.emit === "function") { salla.event.emit("search::close"); } } } catch (e) {} } function getStoreBase() { try { if (window.salla && salla.config && salla.config.get) { var url = salla.config.get("store.url") || salla.config.get("url"); if (url) return String(url).replace(/\/$/, ""); } } catch (e) {} return (location.origin || "").replace(/\/$/, ""); } function buildSearchShell() { var existing = q(".vv-searchbox"); if (existing) existing.remove(); var overlay = document.createElement("div"); overlay.className = "vv-searchbox"; overlay.setAttribute("hidden", ""); var backdrop = document.createElement("div"); backdrop.className = "vv-searchbox__backdrop"; var dialog = document.createElement("div"); dialog.className = "vv-searchbox__dialog"; dialog.setAttribute("role", "dialog"); dialog.setAttribute("aria-modal", "true"); dialog.setAttribute("aria-label", "\u0627\u0644\u0628\u062d\u062b \u0641\u064a \u0627\u0644\u0645\u062a\u062c\u0631"); var close = document.createElement("button"); close.type = "button"; close.className = "vv-searchbox__close"; close.setAttribute("aria-label", "\u0625\u063a\u0644\u0627\u0642 \u0627\u0644\u0628\u062d\u062b"); close.innerHTML = ''; var head = document.createElement("div"); head.className = "vv-searchbox__head"; head.innerHTML = '' + "\u0648\u0631\u0648\u062f\u060c \u0647\u062f\u0627\u064a\u0627\u060c \u062a\u0646\u0633\u064a\u0642 \u0645\u0646\u0627\u0633\u0628\u0627\u062a \u0648\u0623\u0643\u062b\u0631" + '
' + "
';
/* Pull store description from the native footer (set by Salla dashboard) */
var nativeDesc = q(".vv-footer-tagline p, .store-footer__inner .vv-footer-tagline p, .store-footer__inner .max-w-sm p", footer);
if (!nativeDesc) {
/* Fallback: try any inside the first column that has the logo */ var firstCol = q(".store-footer__inner .container > div:first-child p", footer); if (firstCol && firstCol.textContent.trim()) nativeDesc = firstCol; } if (nativeDesc && nativeDesc.textContent.trim()) { var descP = document.createElement("p"); descP.className = "vv-footer-desc"; descP.textContent = nativeDesc.textContent.trim(); brand.appendChild(descP); } var social = q("salla-social", footer); if (social) { brand.appendChild(social); } var discovered = collectSallaFooterLinks(); var importantLinks = discovered.filter(function (item) { return !isFooterLegalLink(item); }); var legalLinks = discovered.filter(isFooterLegalLink); if (!importantLinks.length && legalLinks.length) { importantLinks = legalLinks.slice(); legalLinks = []; } var important = makeFooterCol("روابط مهمة"); important.col.classList.add("vv-footer-col--important"); fillLinkList(important.list, importantLinks); setFooterColVisibility(important.col, importantLinks); var legal = makeFooterCol("روابط قانونية"); legal.col.classList.add("vv-footer-col--legal"); fillLinkList(legal.list, legalLinks); setFooterColVisibility(legal.col, legalLinks); var contact = document.createElement("div"); contact.className = "vv-footer-col vv-footer-col--contact"; contact.innerHTML = "