// ======================================== // MEJORAS PLACER PRIVADO - SHOPIFY JS // Archivo: mejoras-placerprivado.js // ======================================== (function() {"use strict"; class PlacerPrivadoShopifyMejoras {constructor() {this.popupQueue = []; this.currentPopup = null; this.popupDelay = 3000; // 3 segundos entre popups this.userPreferences = this.loadUserPreferences(); this.isShopify = window.Shopify !== undefined; this.init();} init() {// Esperar a que el DOM est\e9  completamente cargado if (document.readyState === "loading") {document.addEventListener("DOMContentLoaded",() => this.initializeFeatures());} else {this.initializeFeatures();}} initializeFeatures() {console.log("\1f680  Inicializando mejoras Placer Privado para Shopify"); this.setupPopupManager(); this.setupProductFilters(); this.setupNotificationSystem(); this.setupRestockNotifications(); this.setupProductEnhancements(); this.setupSearchEnhancements(); this.addTrustSignals(); this.setupMobileOptimizations(); this.setupShopifySpecific(); this.addBreadcrumbs(); // Mostrar notificaci\f3n de \e9xito setTimeout(() => {this.showNotification("\2728  Mejoras cargadas correctamente","success");},1000);} // ========================================== // 1 GESTI\d3N INTELIGENTE DE POPUPS // ========================================== setupPopupManager() {// Evitar mostrar popups si el usuario ya los ha visto recientemente if (this.userPreferences.lastPopupShown && Date.now() - this.userPreferences.lastPopupShown < 24 * 60 * 60 * 1000) {console.log("\23f0  Popups omitidos - mostrados recientemente"); return;} // Cola de popups con prioridades espec\ed ficas para Shopify this.popupQueue = [{id: "age-verification",priority: 1,delay: 2000},{id: "newsletter",priority: 2,delay: 8000},{id: "promotion",priority: 3,delay: 15000}]; // Buscar popups existentes en Shopify this.identifyShopifyPopups(); this.processPopupQueue();} identifyShopifyPopups() {// Identificar popups comunes en Shopify const popupSelectors = ["[data-popup]",".popup",".modal",".newsletter-popup",".age-verification",".promotional-popup"]; popupSelectors.forEach(selector => {const popups = document.querySelectorAll(selector); popups.forEach(popup => {if (!popup.dataset.popup) {// Asignar ID autom\e1ticamente if (popup.classList.contains("newsletter") || popup.textContent.toLowerCase().includes("newsletter")) {popup.dataset.popup = "newsletter";} else if (popup.textContent.toLowerCase().includes("18") || popup.textContent.toLowerCase().includes("edad")) {popup.dataset.popup = "age-verification";} else {popup.dataset.popup = "promotion";}} this.enhancePopup(popup);});});} enhancePopup(popup) {// A\f1 adir clases necesarias popup.classList.add("popup-overlay"); // Buscar o crear contenedor let container = popup.querySelector(".popup-container"); if (!container) {const content = popup.innerHTML; popup.innerHTML = `<div class="popup-container">${content}</div>`; container = popup.querySelector(".popup-container");} // A\f1 adir bot\f3n de cerrar si no existe if (!container.querySelector(".popup-close")) {const closeBtn = document.createElement("button"); closeBtn.className = "popup-close"; closeBtn.innerHTML = "\d7"; closeBtn.onclick = () => this.closePopup(popup.dataset.popup); container.appendChild(closeBtn);}} processPopupQueue() {if (this.popupQueue.length === 0 || this.currentPopup) return; const popup = this.popupQueue.shift(); setTimeout(() => {this.showPopup(popup.id);},popup.delay);} showPopup(popupId) {const popup = document.querySelector(`[data-popup="${popupId}"]`); if (!popup) {console.log(`\26a0\fe0f  Popup ${popupId} no encontrado`); this.processPopupQueue(); return;} this.currentPopup = popupId; popup.classList.add("active"); // Auto-cerrar despu\e9s de 30 segundos setTimeout(() => {this.closePopup(popupId);},30000); // Guardar preferencia this.userPreferences.lastPopupShown = Date.now(); this.saveUserPreferences(); console.log(`\2705  Popup ${popupId} mostrado`);} closePopup(popupId) {const popup = document.querySelector(`[data-popup="${popupId}"]`); if (popup) {popup.classList.remove("active");} this.currentPopup = null; // Procesar siguiente popup en la cola setTimeout(() => {this.processPopupQueue();},this.popupDelay); console.log(`\274c  Popup ${popupId} cerrado`);} // ========================================== // 2 SISTEMA DE FILTROS MEJORADO // ========================================== setupProductFilters() {const filterContainer = document.querySelector(".filter-section"); if (!filterContainer) {// Crear contenedor de filtros si no existe this.createFilterSection();} else {// Mejorar filtros existentes this.enhanceExistingFilters(filterContainer);}} createFilterSection() {const collectionPage = document.querySelector(".template-collection, .collection-template"); if (!collectionPage) return; const filterSection = document.createElement("div"); filterSection.className = "filter-section"; const advancedFilters = this.createAdvancedFilters(); filterSection.appendChild(advancedFilters); // Insertar antes de la lista de productos const productGrid = document.querySelector(".product-grid, .collection-grid, .products-grid"); if (productGrid) {productGrid.parentNode.insertBefore(filterSection,productGrid);} this.setupFilterEvents();} createAdvancedFilters() {const filtersHTML = ` <div class="advanced-filters"> <div class="filter-group"> <label class="filter-label">Rango de Precio</label> <div class="price-range"> <input type="range" id="minPrice" min="0" max="200" value="0" class="price-slider"> <input type="range" id="maxPrice" min="0" max="200" value="200" class="price-slider"> <div class="price-display"> <span id="minPriceDisplay">$0</span> - <span id="maxPriceDisplay">$200</span> </div> </div> </div> <div class="filter-group"> <label class="filter-label">Disponibilidad</label> <div class="filter-options"> <label class="filter-option"> <input type="checkbox" class="filter-checkbox" data-filter="availability" value="in-stock"> En Stock </label> <label class="filter-option"> <input type="checkbox" class="filter-checkbox" data-filter="availability" value="out-of-stock"> Agotado </label> </div> </div> <div class="filter-group"> <label class="filter-label">Material</label> <select class="filter-select" data-filter="material"> <option value="">Todos los materiales</option> <option value="silicona">Silicona</option> <option value="encaje">Encaje</option> <option value="saten">Sat\e9n</option> <option value="cuero">Cuero</option> <option value="algodon">Algod\f3n</option> </select> </div> <div class="filter-group"> <label class="filter-label">Categor\ed a</label> <select class="filter-select" data-filter="category"> <option value="">Todas las categor\ed as</option> <option value="lenceria">Lencer\ed a</option> <option value="juguetes">Juguetes</option> <option value="bondage">Bondage</option> <option value="ropa">Ropa</option> </select> </div> </div> `; const container = document.createElement("div"); container.innerHTML = filtersHTML; return container.firstElementChild;} setupFilterEvents() {// Event listeners para filtros document.addEventListener("change",(e) => {if (e.target.matches(".filter-checkbox, .filter-select, .price-slider")) {this.applyFilters();}}); // Event listeners para sliders de precio document.addEventListener("input",(e) => {if (e.target.matches(".price-slider")) {this.updatePriceDisplay();}});} updatePriceDisplay() {const minPrice = document.getElementById("minPrice")?.value || 0; const maxPrice = document.getElementById("maxPrice")?.value || 200; const minDisplay = document.getElementById("minPriceDisplay"); const maxDisplay = document.getElementById("maxPriceDisplay"); if (minDisplay) minDisplay.textContent = `$${minPrice}`; if (maxDisplay) maxDisplay.textContent = `$${maxPrice}`;} applyFilters() {const products = document.querySelectorAll(".product-card, .product-item, .grid-product"); const activeFilters = this.getActiveFilters(); let visibleCount = 0; products.forEach(product => {const shouldShow = this.productMatchesFilters(product,activeFilters); product.style.display = shouldShow ? "block" : "none"; if (shouldShow) visibleCount++;}); this.updateProductCount(visibleCount); console.log(`\1f50d  Filtros aplicados: ${visibleCount} productos visibles`);} getActiveFilters() {const filters = {}; // Precio filters.minPrice = parseFloat(document.getElementById("minPrice")?.value || 0); filters.maxPrice = parseFloat(document.getElementById("maxPrice")?.value || 999); // Disponibilidad filters.availability = Array.from(document.querySelectorAll('.filter-checkbox[data-filter="availability"]:checked')) .map(cb => cb.value); // Material filters.material = document.querySelector('.filter-select[data-filter="material"]')?.value || ""; // Categor\ed a filters.category = document.querySelector('.filter-select[data-filter="category"]')?.value || ""; return filters;} productMatchesFilters(product,filters) {const price = this.extractProductPrice(product); const availability = this.extractProductAvailability(product); const material = this.extractProductMaterial(product); const category = this.extractProductCategory(product); // Filtro de precio if (price < filters.minPrice || price > filters.maxPrice) {return false;} // Filtro de disponibilidad if (filters.availability.length > 0 && !filters.availability.includes(availability)) {return false;} // Filtro de material if (filters.material && !material.includes(filters.material)) {return false;} // Filtro de categor\ed a if (filters.category && !category.includes(filters.category)) {return false;} return true;} // ========================================== // 3 NOTIFICACIONES DE RESTOCK // ========================================== setupRestockNotifications() {const outOfStockProducts = document.querySelectorAll(".product-card.out-of-stock, .product-item.sold-out"); outOfStockProducts.forEach(product => {this.addRestockButton(product);}); // Observar productos que se marquen como agotados din\e1micamente this.observeStockChanges();} addRestockButton(product) {// Evitar duplicar botones if (product.querySelector(".notify-restock-btn")) return; const notifyBtn = document.createElement("button"); notifyBtn.className = "notify-restock-btn"; notifyBtn.textContent = "Notificarme cuando est\e9  disponible"; notifyBtn.onclick = () => this.handleRestockNotification(product); const productInfo = product.querySelector(".product-info, .product-details, .product-content"); if (productInfo) {productInfo.appendChild(notifyBtn);}} observeStockChanges() {const observer = new MutationObserver((mutations) => {mutations.forEach((mutation) => {if (mutation.type === "attributes" && mutation.attributeName === "class") {const target = mutation.target; if (target.classList.contains("out-of-stock") || target.classList.contains("sold-out")) {this.addRestockButton(target);}}});}); const products = document.querySelectorAll(".product-card, .product-item"); products.forEach(product => {observer.observe(product,{attributes: true,attributeFilter: ["class"]});});} handleRestockNotification(product) {const productId = this.extractProductId(product); const productName = this.extractProductName(product); // Crear modal para capturar email this.showEmailCaptureModal(productId,productName);} showEmailCaptureModal(productId,productName) {const modalHTML = ` <div class="popup-overlay active" data-popup="restock-notification"> <div class="popup-container"> <button class="popup-close" onclick="this.closest('.popup-overlay').remove()">\d7</button> <h3>\1f514  Notificaci\f3n de Restock</h3> <p>Te avisaremos cuando <strong>"${productName}"</strong> est\e9  disponible nuevamente.</p> <form class="restock-form"> <input type="email" placeholder="Tu email" required class="restock-email" style="width: 100%; padding: 12px; margin: 10px 0; border: 1px solid #ddd; border-radius: 6px;"> <button type="submit" class="notify-restock-btn" style="margin-top: 10px;">Suscribirme</button> </form> </div> </div> `; const modalContainer = document.createElement("div"); modalContainer.innerHTML = modalHTML; document.body.appendChild(modalContainer.firstElementChild); // Manejar env\edo del formulario const form = document.querySelector(".restock-form"); form.onsubmit = (e) => {e.preventDefault(); const email = form.querySelector(".restock-email").value; if (this.validateEmail(email)) {this.saveRestockNotification(productId,email,productName); this.showNotification(`\2705  Te notificaremos cuando "${productName}" est\e9  disponible`,"success"); document.querySelector('[data-popup="restock-notification"]').remove();}};} saveRestockNotification(productId,email,productName) {let notifications = JSON.parse(localStorage.getItem("restockNotifications") || "[]"); notifications.push({productId,email,productName,date: Date.now(),shopDomain: window.Shopify?.shop || "placerprivado.com"}); localStorage.setItem("restockNotifications",JSON.stringify(notifications)); // Si hay integraci\f3n con Shopify,enviar a webhook if (window.Shopify && window.Shopify.shop) {this.sendRestockNotificationToShopify(productId,email,productName);}} sendRestockNotificationToShopify(productId,email,productName) {// Enviar notificaci\f3n a Shopify (requiere configuraci\f3n del webhook) fetch("/apps/restock-notifications",{method: "POST",headers: {"Content-Type": "application/json",},body: JSON.stringify({product_id: productId,email: email,product_name: productName})}).catch(err => {console.log("\2139\fe0f  Webhook de restock no configurado:",err);});} // ========================================== // 4 SISTEMA DE NOTIFICACIONES // ========================================== setupNotificationSystem() {// Crear contenedor de notificaciones if (!document.getElementById("notification-container")) {const notificationContainer = document.createElement("div"); notificationContainer.id = "notification-container"; notificationContainer.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 100000; pointer-events: none; `; document.body.appendChild(notificationContainer);}} showNotification(message,type = "info",duration = 5000) {const notification = document.createElement("div"); notification.className = `notification ${type}`; notification.style.pointerEvents = "auto"; notification.innerHTML = ` <div class="notification-content"> <span class="notification-message">${message}</span> <button class="notification-close" onclick="this.parentElement.parentElement.remove()">\d7</button> </div> `; const container = document.getElementById("notification-container"); if (container) {container.appendChild(notification); // Mostrar notificaci\f3n setTimeout(() => notification.classList.add("show"),100); // Auto-remover setTimeout(() => {notification.classList.remove("show"); setTimeout(() => notification.remove(),300);},duration);}} // ========================================== // 5 MEJORAS EN PRODUCTOS // ========================================== setupProductEnhancements() {const products = document.querySelectorAll(".product-card, .product-item, .grid-product"); products.forEach(product => {this.enhanceProduct(product);}); // Observar nuevos productos (para p\e1ginas con carga infinita) this.observeNewProducts();} enhanceProduct(product) {this.addQuickView(product); this.addWishlistButton(product); this.enhanceProductImages(product); this.markOutOfStockProducts(product);} markOutOfStockProducts(product) {// Detectar productos agotados en Shopify const soldOutIndicators = [".sold-out",".unavailable",'[data-sold-out="true"]',".product-form__buttons .btn[disabled]"]; const isSoldOut = soldOutIndicators.some(selector => product.querySelector(selector) || product.matches(selector)); if (isSoldOut || product.textContent.toLowerCase().includes("agotado")) {product.classList.add("out-of-stock"); this.addRestockButton(product);}} addQuickView(product) {if (product.querySelector(".quick-view-btn")) return; const quickViewBtn = document.createElement("button"); quickViewBtn.className = "quick-view-btn"; quickViewBtn.innerHTML = "\1f441\fe0f  Vista R\e1pida"; quickViewBtn.onclick = (e) => {e.preventDefault(); e.stopPropagation(); this.openQuickView(product);}; const productImage = product.querySelector(".product-image, .product-media, .product-photo"); if (productImage) {productImage.style.position = "relative"; productImage.appendChild(quickViewBtn);}} addWishlistButton(product) {if (product.querySelector(".wishlist-btn")) return; const wishlistBtn = document.createElement("button"); wishlistBtn.className = "wishlist-btn"; wishlistBtn.innerHTML = "\2661"; wishlistBtn.style.cssText = ` position: absolute; top: 10px; right: 10px; background: rgba(255,255,255,.9); border: none; width: 35px; height: 35px; border-radius: 50%; cursor: pointer; font-size: 18px; transition: all .2s ease; z-index: 5; `; wishlistBtn.onclick = (e) => {e.preventDefault(); e.stopPropagation(); this.toggleWishlist(product,wishlistBtn);}; const productImage = product.querySelector(".product-image, .product-media, .product-photo"); if (productImage) {productImage.style.position = "relative"; productImage.appendChild(wishlistBtn);}} toggleWishlist(product,button) {const productId = this.extractProductId(product); let wishlist = JSON.parse(localStorage.getItem("wishlist") || "[]"); if (wishlist.includes(productId)) {wishlist = wishlist.filter(id => id !== productId); button.innerHTML = "\2661"; button.style.color = "#666"; this.showNotification("Producto eliminado de favoritos","info");} else {wishlist.push(productId); button.innerHTML = "\2665"; button.style.color = "#ff6b6b"; this.showNotification("Producto a\f1 adido a favoritos","success");} localStorage.setItem("wishlist",JSON.stringify(wishlist));} openQuickView(product) {const productData = this.extractProductData(product); const quickViewModal = this.createQuickViewModal(productData); document.body.appendChild(quickViewModal); setTimeout(() => quickViewModal.classList.add("active"),100);} createQuickViewModal(productData) {const modalHTML = ` <div class="popup-overlay" data-popup="quick-view"> <div class="popup-container" style="max-width: 600px;"> <button class="popup-close" onclick="this.closest('.popup-overlay').remove()">\d7</button> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;"> <div class="quick-view-image"> <img src="${productData.image}" alt="${productData.title}" style="width: 100%; border-radius: 8px;"> </div> <div class="quick-view-info"> <h3 style="margin-bottom: 10px;">${productData.title}</h3> <div class="product-price" style="font-size: 24px; color: #ff6b6b; margin-bottom: 15px;">${productData.price}</div> <p style="color: #666; margin-bottom: 20px;">${productData.description}</p> <button class="add-to-cart-btn" onclick="window.location.href='${productData.url}'">Ver Producto Completo</button> </div> </div> </div> </div> `; const container = document.createElement("div"); container.innerHTML = modalHTML; return container.firstElementChild;} observeNewProducts() {const observer = new MutationObserver((mutations) => {mutations.forEach((mutation) => {mutation.addedNodes.forEach((node) => {if (node.nodeType === 1) {// Element node const products = node.matches && node.matches(".product-card, .product-item, .grid-product") ? [node] : node.querySelectorAll && node.querySelectorAll(".product-card, .product-item, .grid-product") || []; products.forEach(product => this.enhanceProduct(product));}});});}); observer.observe(document.body,{childList: true,subtree: true});} // ========================================== // 6 B\daSQUEDA MEJORADA // ========================================== setupSearchEnhancements() {const searchInputs = document.querySelectorAll('input[type="search"], .search-input, .search-form input'); searchInputs.forEach(input => {this.enhanceSearchInput(input);});} enhanceSearchInput(input) {// A\f1 adir autocompletado this.setupSearchAutocomplete(input); // B\fasqueda en tiempo real let searchTimeout; input.addEventListener("input",(e) => {clearTimeout(searchTimeout); searchTimeout = setTimeout(() => {this.performLiveSearch(e.target.value);},300);});} setupSearchAutocomplete(input) {const suggestions = ["lencer\ed a sexy","juguetes para parejas","bondage","vibradores","ropa interior","disfraces","aceites","preservativos","lencer\ed a premium","juguetes para ella","juguetes para \e9l","ropa deportiva sexy","accesorios","lubricantes"]; // Crear datalist si no existe let datalist = document.getElementById("search-suggestions"); if (!datalist) {datalist = document.createElement("datalist"); datalist.id = "search-suggestions"; suggestions.forEach(suggestion => {const option = document.createElement("option"); option.value = suggestion; datalist.appendChild(option);}); document.body.appendChild(datalist);} input.setAttribute("list","search-suggestions");} performLiveSearch(query) {if (query.length < 2) return; // Filtrar productos visibles basado en la b\fasqueda const products = document.querySelectorAll(".product-card, .product-item, .grid-product"); let matchCount = 0; products.forEach(product => {const title = this.extractProductName(product).toLowerCase(); const description = this.extractProductDescription(product).toLowerCase(); const searchText = `${title} ${description}`; const matches = searchText.includes(query.toLowerCase()); product.style.display = matches ? "block" : "none"; if (matches) matchCount++;}); console.log(`\1f50d  B\fasqueda "${query}": ${matchCount} resultados`);} // ========================================== // 7 TRUST SIGNALS // ========================================== addTrustSignals() {// Evitar duplicar trust signals if (document.querySelector(".trust-badges")) return; const trustBadges = document.createElement("div"); trustBadges.className = "trust-badges"; trustBadges.innerHTML = ` <div class="trust-badge"> <span class="trust-badge-icon">\1f512</span> <span>Pago Seguro SSL</span> </div> <div class="trust-badge"> <span class="trust-badge-icon">\1f4e6</span> <span>Env\edo Discreto</span> </div> <div class="trust-badge"> <span class="trust-badge-icon">\2b50</span> <span>+1000 Clientes Satisfechos</span> </div> <div class="trust-badge"> <span class="trust-badge-icon">\1f504</span> <span>Garant\ed a de Devoluci\f3n</span> </div> <div class="trust-badge"> <span class="trust-badge-icon">\1f69a</span> <span>Entrega 24-48h</span> </div> <div class="trust-badge"> <span class="trust-badge-icon">\1f510</span> <span>Privacidad Total</span> </div> `; // Insertar en ubicaciones estrat\e9gicas const insertPoints = [document.querySelector("main"),document.querySelector(".main-content"),document.querySelector(".container"),document.querySelector("#MainContent"),document.body]; const insertPoint = insertPoints.find(point => point !== null); if (insertPoint) {insertPoint.appendChild(trustBadges);}} // ========================================== // 8 BREADCRUMBS // ========================================== addBreadcrumbs() {// Solo a\f1 adir en p\e1ginas de producto y colecci\f3n const isProductPage = document.body.classList.contains("template-product"); const isCollectionPage = document.body.classList.contains("template-collection"); if (!isProductPage && !isCollectionPage) return; if (document.querySelector(".breadcrumb")) return; // Ya existe const breadcrumb = this.createBreadcrumb(); if (breadcrumb) {const insertPoint = document.querySelector("main, .main-content, #MainContent"); if (insertPoint) {insertPoint.insertBefore(breadcrumb,insertPoint.firstChild);}}} createBreadcrumb() {const breadcrumbData = this.getBreadcrumbData(); if (!breadcrumbData.length) return null; const breadcrumb = document.createElement("nav"); breadcrumb.className = "breadcrumb"; const list = document.createElement("ol"); list.className = "breadcrumb-list"; breadcrumbData.forEach((item,index) => {const listItem = document.createElement("li"); listItem.className = "breadcrumb-item"; if (index === breadcrumbData.length - 1) {listItem.textContent = item.title;} else {const link = document.createElement("a"); link.href = item.url; link.textContent = item.title; listItem.appendChild(link);} list.appendChild(listItem);}); breadcrumb.appendChild(list); return breadcrumb;} getBreadcrumbData() {const breadcrumbs = [{title: "Inicio",url: "/"}]; // Detectar tipo de p\e1gina y construir breadcrumbs if (window.location.pathname.includes("/collections/")) {const collectionHandle = window.location.pathname.split("/collections/")[1]?.split("/")[0]; if (collectionHandle) {const collectionTitle = this.getCollectionTitle() || this.formatHandle(collectionHandle); breadcrumbs.push({title: collectionTitle,url: `/collections/${collectionHandle}`});}} if (window.location.pathname.includes("/products/")) {const productTitle = this.getProductTitle(); if (productTitle) {breadcrumbs.push({title: productTitle,url: window.location.pathname});}} return breadcrumbs;} getCollectionTitle() {const titleSelectors = [".collection-title",".page-title","h1",".collection-header h1",".section-header h1"]; for (const selector of titleSelectors) {const element = document.querySelector(selector); if (element && element.textContent.trim()) {return element.textContent.trim();}} return null;} getProductTitle() {const titleSelectors = [".product-title",".product__title","h1.product-single__title",".product-form__title","h1"]; for (const selector of titleSelectors) {const element = document.querySelector(selector); if (element && element.textContent.trim()) {return element.textContent.trim();}} return null;} formatHandle(handle) {return handle.replace(/-/g," ").replace(/\w/g,l => l.toUpperCase());} // ========================================== // 9 OPTIMIZACIONES M\d3VILES // ========================================== setupMobileOptimizations() {if (window.innerWidth <= 768) {this.optimizeForMobile();} window.addEventListener("resize",() => {if (window.innerWidth <= 768) {this.optimizeForMobile();}});} optimizeForMobile() {// Optimizar popups para m\f3vil const popups = document.querySelectorAll(".popup-container"); popups.forEach(popup => {popup.style.width = "95%"; popup.style.maxWidth = "350px"; popup.style.margin = "10px";}); // Optimizar filtros para m\f3vil const filterSection = document.querySelector(".filter-section"); if (filterSection) {filterSection.style.padding = "15px";} // Optimizar notificaciones para m\f3vil const notificationContainer = document.getElementById("notification-container"); if (notificationContainer) {notificationContainer.style.left = "10px"; notificationContainer.style.right = "10px";}} // ========================================== // 10 FUNCIONES ESPEC\cd FICAS DE SHOPIFY // ========================================== setupShopifySpecific() {if (!this.isShopify) return; // Integraci\f3n con carrito de Shopify this.setupShopifyCartIntegration(); // Manejo de variantes de producto this.setupVariantHandling(); // Integraci\f3n con checkout this.setupCheckoutEnhancements();} setupShopifyCartIntegration() {// Interceptar a\f1 adir al carrito document.addEventListener("submit",(e) => {if (e.target.matches('.product-form, form[action*="/cart/add"]')) {this.handleAddToCart(e);}});} handleAddToCart(e) {// Mostrar notificaci\f3n de \e9xito setTimeout(() => {this.showNotification("\2705  Producto a\f1 adido al carrito","success");},500);} setupVariantHandling() {// Manejar cambios de variante const variantSelectors = document.querySelectorAll(".product-form__variants select, .variant-input"); variantSelectors.forEach(selector => {selector.addEventListener("change",() => {this.handleVariantChange();});});} handleVariantChange() {// Actualizar precio y disponibilidad console.log("\1f504  Variante cambiada");} setupCheckoutEnhancements() {// A\f1 adir trust signals en checkout if (window.location.pathname.includes("/checkout")) {this.addCheckoutTrustSignals();}} addCheckoutTrustSignals() {const checkoutTrust = document.createElement("div"); checkoutTrust.innerHTML = ` <div style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px; margin: 15px 0;"> <div style="display: flex; justify-content: center; gap: 20px; flex-wrap: wrap;"> <span style="font-size: 12px; color: #666;">\1f512  Pago Seguro</span> <span style="font-size: 12px; color: #666;">\1f4e6  Env\edo Discreto</span> <span style="font-size: 12px; color: #666;">\1f510  Datos Protegidos</span> </div> </div> `; const orderSummary = document.querySelector(".order-summary, .sidebar"); if (orderSummary) {orderSummary.appendChild(checkoutTrust);}} // ========================================== // 11 UTILIDADES Y EXTRACTORES // ========================================== extractProductId(product) {// M\faltiples formas de extraer ID en Shopify return product.dataset.productId || product.dataset.id || product.querySelector("[data-product-id]")?.dataset.productId || product.querySelector('form input[name="id"]')?.value || Math.random().toString(36).substr(2,9);} extractProductName(product) {const titleSelectors = [".product-title",".product__title",".product-card__title",".grid-product__title","h3","h2",".product-info h3",".product-info h2"]; for (const selector of titleSelectors) {const element = product.querySelector(selector); if (element && element.textContent.trim()) {return element.textContent.trim();}} return "Producto sin nombre";} extractProductPrice(product) {const priceSelectors = [".product-price",".price",".product__price",".grid-product__price",".money"]; for (const selector of priceSelectors) {const element = product.querySelector(selector); if (element) {const priceText = element.textContent.replace(/[^0-9,]/g,""); const price = parseFloat(priceText.replace(",",".")); if (!isNaN(price)) return price;}} return 0;} extractProductAvailability(product) {if (product.classList.contains("out-of-stock") || product.classList.contains("sold-out") || product.querySelector(".sold-out, .unavailable")) {return "out-of-stock";} return "in-stock";} extractProductMaterial(product) {const text = (this.extractProductName(product) + " " + this.extractProductDescription(product)).toLowerCase(); const materials = ["silicona","encaje","saten","cuero","algodon"]; return materials.find(material => text.includes(material)) || "";} extractProductCategory(product) {const text = (this.extractProductName(product) + " " + this.extractProductDescription(product)).toLowerCase(); if (text.includes("lenceria") || text.includes("ropa interior")) return "lenceria"; if (text.includes("juguete") || text.includes("vibrador")) return "juguetes"; if (text.includes("bondage") || text.includes("bdsm")) return "bondage"; if (text.includes("ropa") || text.includes("vestido")) return "ropa"; return "";} extractProductDescription(product) {const descSelectors = [".product-description",".product__description",".product-card__description",".grid-product__description"]; for (const selector of descSelectors) {const element = product.querySelector(selector); if (element && element.textContent.trim()) {return element.textContent.trim();}} return "";} extractProductData(product) {return {id: this.extractProductId(product),title: this.extractProductName(product),price: this.formatPrice(this.extractProductPrice(product)),description: this.extractProductDescription(product).substring(0,150) + "...",image: this.extractProductImage(product),url: this.extractProductUrl(product)};} extractProductImage(product) {const img = product.querySelector("img"); return img ? img.src : "/assets/placeholder.jpg";} extractProductUrl(product) {const link = product.querySelector("a"); return link ? link.href : "#";} formatPrice(price) {return new Intl.NumberFormat("es-ES",{style: "currency",currency: "USD"}).format(price);} updateProductCount(count) {let countElement = document.querySelector(".product-count"); if (!countElement) {countElement = document.createElement("div"); countElement.className = "product-count"; countElement.style.cssText = "text-align: center; margin: 20px 0; font-weight: 600; color: #666;"; const filterSection = document.querySelector(".filter-section"); if (filterSection) {filterSection.appendChild(countElement);}} if (countElement) {countElement.textContent = `${count} productos encontrados`;}} // ========================================== // 12 GESTI\d3N DE PREFERENCIAS // ========================================== loadUserPreferences() {try {return JSON.parse(localStorage.getItem("placerPrivadoPrefs") || "{}");} catch (e) {return {};}} saveUserPreferences() {try {localStorage.setItem("placerPrivadoPrefs",JSON.stringify(this.userPreferences));} catch (e) {console.log("\26a0\fe0f  No se pudieron guardar las preferencias");}} validateEmail(email) {return /^[^s@]+@[^s@]+\.[^s@]+$/.test(email);}} // ========================================== // INICIALIZACI\d3N Y UTILIDADES GLOBALES // ========================================== // Inicializar cuando el DOM est\e9  listo let mejoras; function initializeMejoras() {mejoras = new PlacerPrivadoShopifyMejoras(); window.placerPrivadoMejoras = mejoras;} if (document.readyState === "loading") {document.addEventListener("DOMContentLoaded",initializeMejoras);} else {initializeMejoras();} // Utilidades globales para integraci\f3n window.PlacerPrivadoUtils = {showNotification: (message,type = "info") => {if (window.placerPrivadoMejoras) {window.placerPrivadoMejoras.showNotification(message,type);}},closeAllPopups: () => {document.querySelectorAll(".popup-overlay.active").forEach(popup => {popup.classList.remove("active");});},addToWishlist: (productId) => {let wishlist = JSON.parse(localStorage.getItem("wishlist") || "[]"); if (!wishlist.includes(productId)) {wishlist.push(productId); localStorage.setItem("wishlist",JSON.stringify(wishlist)); window.PlacerPrivadoUtils.showNotification("Producto a\f1 adido a favoritos","success");}},removeFromWishlist: (productId) => {let wishlist = JSON.parse(localStorage.getItem("wishlist") || "[]"); wishlist = wishlist.filter(id => id !== productId); localStorage.setItem("wishlist",JSON.stringify(wishlist)); window.PlacerPrivadoUtils.showNotification("Producto eliminado de favoritos","info");},getWishlist: () => {return JSON.parse(localStorage.getItem("wishlist") || "[]");}}; console.log("\1f389  Mejoras Placer Privado para Shopify cargadas correctamente");})();{}
/*# sourceMappingURL=/cdn/shop/t/11/assets/mejoras-placerprivado.css.map */
