/* JNP Home SEO Copy v25.0.18 — separate IIFE so it still runs if hamburger early-returns */ (function () { if ( window.__jnpHomeSeoCopyV18 ) return; window.__jnpHomeSeoCopyV18 = true; if ( ! document.body || ! ( document.body.classList.contains( 'home' ) || document.body.classList.contains( 'page-id-1993' ) ) ) { return; } var TITLE = 'Custom Photo Merch & Apparel in Bali | J&P Bali'; var DESC = 'Custom photo merch in Bali: upload a trip photo, we print premium apparel and deliver to your hotel. Group kits, corporate merch, and affiliate partnerships — J&P Bali.'; var MAP = [ [ 'Turn Your Bali Memories Into Wearable Art', 'Custom Photo Merch Delivered to Your Bali Hotel' ], [ 'Custom Photo Merch • Group Apparel • Affiliate Partnerships', 'J&P Bali · Photo Merch · Hotel Delivery' ], [ 'CUSTOM PHOTO MERCH • GROUP APPAREL • AFFILIATE PARTNERSHIPS', 'J&P BALI · PHOTO MERCH · HOTEL DELIVERY' ], [ 'Send us your favorite photo from Bali. We turn it into premium apparel and deliver it to your hotel before you fly home.', 'Upload a trip photo — we print premium apparel and deliver it to your hotel before you fly home.', ], [ 'Welcome to J&P Bali—the official heartbeat of custom apparel and affiliate solutions. Driven by our core philosophy, DHB (Double Helix Brothers), we believe at the most fundamental level, we are all part of the same human tribe, completely wired together.', 'J&P Bali makes custom photo apparel and merch in Bali — from trip souvenirs to crew kits and partner branding.', ], [ 'Born on the streets of Bali, we blend premium apparel production with forward-thinking tech solutions—turning your adventure into wearables that tell your story.', 'Born in Bali: premium apparel production that turns your adventure into wearables that tell your story.', ], [ 'Upload a photo from your trip and we turn it into premium wearable art — t-shirts, hoodies, mugs, canvas prints, and more — delivered to your hotel.', 'Turn a trip photo into t-shirts, hoodies, mugs, and prints — delivered to your hotel.', ], [ 'Premium gear for trips, hens/bucks weeks, and action sports crews—from concept to final print.', 'Premium gear for trips, hens/bucks weeks, and crews — from concept to print.', ], [ 'Clean, professional uniforms and promotional merch to elevate your brand identity.', 'Professional uniforms and promo merch for teams and brands.', ], [ 'Pull-up banners, flyers, business cards, and display setups for local businesses and launches.', 'Banners, flyers, cards, and display kits for local businesses.', ], [ 'Coming soon: ID-registered proximity tags built into our apparel, bridging digital data and real-world connection.', 'Coming soon: proximity tags built into apparel.', ], [ 'Got a photo, an event, or a brand in mind?', 'Ready for custom Bali merch?' ], [ 'Turn it into wearable art, or become a partner and earn on every referral.', 'Order photo merch or become a partner.' ], [ "Questions about your order or a custom quote? We're here to help.", 'Need a quote or order help? Message us.' ], [ 'Questions about your order or a custom quote? We’re here to help.', 'Need a quote or order help? Message us.' ], [ 'One Tribe. Same Vibe. Premium apparel, corporate kits, and affiliate branding — built in Bali.', 'Premium apparel & partner branding — built in Bali.', ], [ 'Custom Crew Apparel & Merch', 'Custom Crew Apparel' ], [ 'Corporate & Business Team Kits', 'Corporate Team Kits' ], [ 'Affiliate Branding Solutions', 'Affiliate Branding' ], [ 'Wearable Tech — “Mood Mode”', 'Mood Mode Tech' ], [ 'Wearable Tech — "Mood Mode"', 'Mood Mode Tech' ], [ 'Premium gear for trips, hens/bucks weeks, and crews — from concept to print.', 'Premium gear for trips, tours, and milestones.' ], [ 'Professional uniforms and promo merch for teams and brands.', 'Professional uniforms and brand identity.' ], [ 'Banners, flyers, cards, and display kits for local businesses.', 'Banners, cards, and display kits for launches.' ], [ 'Coming soon: proximity tags built into apparel.', 'Wearable tech bridging digital and real life.' ], [ 'See All Services', 'See all services →' ], ]; function setMeta() { document.title = TITLE; var desc = document.querySelector( 'meta[name="description"]' ); if ( desc ) desc.setAttribute( 'content', DESC ); var ogt = document.querySelector( 'meta[property="og:title"]' ); if ( ogt ) ogt.setAttribute( 'content', TITLE ); var ogd = document.querySelector( 'meta[property="og:description"]' ); if ( ogd ) ogd.setAttribute( 'content', DESC ); } function replaceInTextNodes( root ) { var walker = document.createTreeWalker( root, NodeFilter.SHOW_TEXT, null ); var nodes = []; var n; while ( ( n = walker.nextNode() ) ) nodes.push( n ); for ( var i = 0; i < nodes.length; i++ ) { var node = nodes[ i ]; var parent = node.parentElement; if ( ! parent ) continue; var tag = parent.tagName; if ( tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT' ) continue; var val = node.nodeValue; if ( ! val || ! val.trim() ) continue; var next = val; for ( var j = 0; j < MAP.length; j++ ) { if ( next.indexOf( MAP[ j ][ 0 ] ) !== -1 ) next = next.split( MAP[ j ][ 0 ] ).join( MAP[ j ][ 1 ] ); } if ( next !== val ) node.nodeValue = next; } } function promoteHeroH1() { var heroes = [ 'Custom Photo Merch Delivered to Your Bali Hotel', 'Turn Your Bali Memories Into Wearable Art' ]; var headings = document.querySelectorAll( 'h2.elementor-heading-title, h2' ); for ( var i = 0; i < headings.length; i++ ) { var el = headings[ i ]; var t = ( el.textContent || '' ).replace( /\s+/g, ' ' ).trim(); if ( heroes.indexOf( t ) === -1 ) continue; if ( el.tagName === 'H1' ) return; var h1 = document.createElement( 'h1' ); for ( var a = 0; a < el.attributes.length; a++ ) { h1.setAttribute( el.attributes[ a ].name, el.attributes[ a ].value ); } while ( el.firstChild ) h1.appendChild( el.firstChild ); el.parentNode.replaceChild( h1, el ); return; } } function run() { setMeta(); replaceInTextNodes( document.body ); promoteHeroH1(); var logoImg = document.querySelector( '.jnp-logo__img' ); if ( logoImg && ! logoImg.getAttribute( 'alt' ) ) { logoImg.setAttribute( 'alt', 'Jay & Patrick Bali logo' ); } } if ( document.readyState === 'loading' ) document.addEventListener( 'DOMContentLoaded', run ); else run(); setTimeout( run, 400 ); setTimeout( run, 1200 ); })(); /* Sitewide SEO + UX v25.0.59 — contacts cards + footer restore */ (function () { if ( window.__jnpSitewideUxV40 ) return; window.__jnpSitewideUxV40 = true; function promotePageH1() { if ( document.querySelector( 'h1' ) ) return; var selectors = [ '.elementor-element-abh1 .elementor-heading-title', '.elementor-element-svh1 .elementor-heading-title', '.elementor-element-galh1 .elementor-heading-title', '.elementor-element-cth1 .elementor-heading-title', '.woocommerce-products-header__title', '.elementor-location-single h2.elementor-heading-title', '.elementor h2.elementor-heading-title', 'main h2.elementor-heading-title', 'main h2', '.l-section--content h2', ]; for ( var i = 0; i < selectors.length; i++ ) { var el = document.querySelector( selectors[ i ] ); if ( ! el ) continue; if ( el.closest( '.elementor-location-footer, footer, #jnp-mobile-menu, .jnp-mobile-menu, .jnp-svc-steps' ) ) continue; var h1 = document.createElement( 'h1' ); for ( var a = 0; a < el.attributes.length; a++ ) { h1.setAttribute( el.attributes[ a ].name, el.attributes[ a ].value ); } while ( el.firstChild ) h1.appendChild( el.firstChild ); el.parentNode.replaceChild( h1, el ); return; } } function polishAboutWhatWeDo() { if ( ! document.body.classList.contains( 'page-id-1729' ) && location.pathname.indexOf( '/about-us' ) === -1 ) { return; } var existing = Array.prototype.filter.call( document.querySelectorAll( 'a[href*="/services"]' ), function ( a ) { return /see all services/i.test( a.textContent || '' ); } ); if ( existing.length ) { existing.forEach( function ( a ) { a.classList.add( 'jnp-about-services-btn' ); a.removeAttribute( 'style' ); a.textContent = 'See all services →'; } ); return; } var row = document.querySelector( '.elementor-element-abpillarsrow' ); if ( ! row || document.querySelector( '.jnp-about-services-link' ) ) return; var wrap = document.createElement( 'div' ); wrap.className = 'jnp-about-services-link'; wrap.innerHTML = 'See all services →'; row.parentNode.insertBefore( wrap, row.nextSibling ); } function shortenAboutCopy() { if ( ! document.body.classList.contains( 'page-id-1729' ) && location.pathname.indexOf( '/about-us' ) === -1 ) { return; } var intro = document.querySelector( '.elementor-element-abintrotext p' ); if ( intro && ( intro.textContent || '' ).length > 90 ) { intro.textContent = 'We make premium custom apparel in Bali — and we care about the people who wear it.'; } document.querySelectorAll( '.elementor-element-abphilotext p, .elementor-element-abphilo p' ).forEach( function ( p, idx ) { var t = ( p.textContent || '' ).replace( /\s+/g, ' ' ).trim(); if ( t.length < 80 ) return; if ( /welcome to j&p|heartbeat|affiliate solutions/i.test( t ) ) { p.textContent = 'J&P Bali — custom apparel and partner kits, built in Bali.'; } } ); var bodyPs = document.querySelectorAll( '.elementor-element-abphilobody p, .elementor-element-abphilotext p' ); if ( bodyPs[ 0 ] && ( bodyPs[ 0 ].textContent || '' ).length > 100 ) { bodyPs[ 0 ].textContent = 'Born in Bali: we turn trips and teams into premium wearables you actually use.'; } if ( bodyPs[ 1 ] && ( bodyPs[ 1 ].textContent || '' ).length > 100 ) { bodyPs[ 1 ].textContent = 'Crew gear, affiliate kits, and wearable tech — made to bring people together.'; } // Hide redundant eyebrow that repeats the closing line var eyebrow = document.querySelector( '.elementor-element-abheyebrow' ); var close = document.querySelector( '.elementor-element-abclosetitle' ); if ( eyebrow && close ) { eyebrow.style.display = 'none'; } } function shortenServicesCopy() { if ( ! document.body.classList.contains( 'page-id-2407' ) && location.pathname.indexOf( '/services' ) === -1 ) { return; } document.querySelectorAll( '.jnp-svc-card' ).forEach( function ( card ) { var title = ( ( card.querySelector( '.jnp-svc-title' ) || {} ).textContent || '' ).toLowerCase(); var p = card.querySelector( '.jnp-svc-desc' ); if ( ! p || ( p.textContent || '' ).length < 120 ) return; if ( /mood|wearable|tech/.test( title ) ) { p.textContent = 'Wearable tech in development — bridging digital identity and real-world meetups.'; } else if ( /affiliate|branding/.test( title ) ) { p.textContent = 'Banners, print, and display kits for launches and local partners.'; } else if ( /corporate|business/.test( title ) ) { p.textContent = 'Clean uniforms and promo merch that keep your brand consistent.'; } else if ( /crew|merch|apparel/.test( title ) ) { p.textContent = 'Custom gear for trips, tours, and group milestones — designed and printed in Bali.'; } } ); var bulk = document.querySelector( '.elementor-element-svbulkt p' ); if ( bulk && ( bulk.textContent || '' ).length > 160 ) { bulk.textContent = 'Tour operators: wholesale pricing, priority slots, and hotel/pickup delivery for recurring groups — plus affiliate options.'; } } function syncMobileMenuA11y() { var menu = document.getElementById( 'jnp-mobile-menu' ); if ( ! menu ) return; var desktop = window.innerWidth >= 1025; var open = menu.classList.contains( 'is-open' ); menu.setAttribute( 'aria-hidden', desktop || ! open ? 'true' : 'false' ); if ( 'inert' in menu ) menu.inert = desktop || ! open; menu.querySelectorAll( 'a, button' ).forEach( function ( el ) { if ( desktop || ! open ) el.setAttribute( 'tabindex', '-1' ); else el.removeAttribute( 'tabindex' ); } ); } function fixLogoAlt() { var logoImg = document.querySelector( '.jnp-logo__img' ); if ( logoImg && ! logoImg.getAttribute( 'alt' ) ) { logoImg.setAttribute( 'alt', 'Jay & Patrick Bali logo' ); } } function fixContactsForm() { if ( ! document.body.classList.contains( 'page-id-51' ) && location.pathname.indexOf( '/contacts' ) === -1 ) { return; } var forms = document.querySelectorAll( '.jnp-contact-form' ); if ( ! forms.length ) { return; } var primary = forms[ 0 ]; for ( var i = 1; i < forms.length; i++ ) { forms[ i ].remove(); } if ( primary.id !== 'jnp-contact-form' ) { primary.id = 'jnp-contact-form'; } /* Desired order: hero → contact cards → quote CTA → message form */ var ctctawrap = document.querySelector( '.elementor-element-ctctawrap' ); var page = document.querySelector( '.elementor-51' ); var host = ( ctctawrap && ctctawrap.parentNode ) || page; if ( ! host || ! ctctawrap ) { return; } if ( primary.previousElementSibling !== ctctawrap || primary.parentNode !== host ) { if ( ctctawrap.nextSibling ) { host.insertBefore( primary, ctctawrap.nextSibling ); } else { host.appendChild( primary ); } } } function restoreContactsFooter() { if ( ! document.body.classList.contains( 'page-id-51' ) && location.pathname.indexOf( '/contacts' ) === -1 ) { return; } var heading = document.querySelector( '.elementor-element-8ec8d7d .elementor-heading-title' ); if ( heading && /prefer whatsapp/i.test( heading.textContent || '' ) ) { heading.textContent = "Questions about your order or a custom quote? We're here to help."; } [ '.elementor-element-77c593f', '.elementor-element-4af58de', '.elementor-element-e24886e', '.elementor-element-9d746ab', '.elementor-element-fce776e', '.elementor-element-da3d79a', '.elementor-element-169f691', ].forEach( function ( sel ) { var el = document.querySelector( sel ); if ( ! el ) return; el.style.removeProperty( 'display' ); el.style.removeProperty( 'visibility' ); el.style.removeProperty( 'height' ); el.style.removeProperty( 'overflow' ); } ); } function run() { fixLogoAlt(); promotePageH1(); shortenAboutCopy(); shortenServicesCopy(); polishAboutWhatWeDo(); syncMobileMenuA11y(); fixContactsForm(); restoreContactsFooter(); } if ( document.readyState === 'loading' ) document.addEventListener( 'DOMContentLoaded', run ); else run(); setTimeout( run, 500 ); setTimeout( restoreContactsFooter, 800 ); window.addEventListener( 'resize', syncMobileMenuA11y ); })(); (function () { /** * Sole hamburger binder. Sets V10 flag immediately so the mu-plugin footer * (which used to boot twice and stack document listeners) exits early. */ window.__jnpHamburgerFixV10 = true; if ( window.__jnpHamburgerFixV11 ) { return; } window.__jnpHamburgerFixV11 = true; var bound = false; function boot() { if ( bound ) { return; } var header = document.getElementById( 'jnp-header' ); var ham = document.querySelector( '.jnp-hamburger' ); var menu = document.getElementById( 'jnp-mobile-menu' ); if ( header ) { var toggleScrolled = function () { if ( window.scrollY > 10 ) { header.classList.add( 'is-scrolled' ); } else { header.classList.remove( 'is-scrolled' ); } }; toggleScrolled(); window.addEventListener( 'scroll', toggleScrolled, { passive: true } ); } if ( ! ham || ! menu || ! ham.parentNode ) { return; } bound = true; var fresh = ham.cloneNode( true ); ham.parentNode.replaceChild( fresh, ham ); ham = fresh; if ( menu.parentElement !== document.body ) { document.body.appendChild( menu ); } ham.setAttribute( 'type', 'button' ); if ( ! ham.getAttribute( 'aria-expanded' ) ) { ham.setAttribute( 'aria-expanded', 'false' ); } var ignoreUntil = 0; var lockY = 0; function syncMenuInteractive( open ) { menu.setAttribute( 'aria-hidden', open ? 'false' : 'true' ); if ( 'inert' in menu ) { menu.inert = ! open; } menu.querySelectorAll( 'a, button' ).forEach( function ( el ) { if ( open ) { el.removeAttribute( 'tabindex' ); } else { el.setAttribute( 'tabindex', '-1' ); } } ); } function pointInRect( x, y, r ) { return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom; } function setOpen( open ) { ham.setAttribute( 'aria-expanded', open ? 'true' : 'false' ); ham.setAttribute( 'aria-label', open ? 'Close menu' : 'Open menu' ); menu.classList.toggle( 'is-open', open ); syncMenuInteractive( open ); document.body.classList.toggle( 'jnp-menu-open', open ); if ( open ) { lockY = window.scrollY || 0; document.body.style.position = 'fixed'; document.body.style.top = '-' + lockY + 'px'; document.body.style.left = '0'; document.body.style.right = '0'; document.body.style.width = '100%'; document.body.style.overflow = 'hidden'; } else { document.body.style.position = ''; document.body.style.top = ''; document.body.style.left = ''; document.body.style.right = ''; document.body.style.width = ''; document.body.style.overflow = ''; window.scrollTo( 0, lockY ); } } syncMenuInteractive( menu.classList.contains( 'is-open' ) ); ham.addEventListener( 'click', function ( e ) { e.preventDefault(); e.stopPropagation(); if ( e.stopImmediatePropagation ) { e.stopImmediatePropagation(); } var open = ham.getAttribute( 'aria-expanded' ) === 'true'; setOpen( ! open ); if ( ! open ) { ignoreUntil = Date.now() + 500; } }, true ); document.addEventListener( 'click', function ( e ) { if ( Date.now() < ignoreUntil ) { return; } if ( ! menu.classList.contains( 'is-open' ) ) { return; } var t = e.target; if ( t && ( menu.contains( t ) || ham.contains( t ) ) ) { return; } /* If menu was left inert, hit-testing skips it — treat geometry as inside. */ var x = e.clientX; var y = e.clientY; if ( typeof x === 'number' && ( pointInRect( x, y, menu.getBoundingClientRect() ) || pointInRect( x, y, ham.getBoundingClientRect() ) ) ) { return; } setOpen( false ); }, true ); document.addEventListener( 'keydown', function ( e ) { if ( e.key === 'Escape' && menu.classList.contains( 'is-open' ) ) { setOpen( false ); ham.focus(); } } ); window.addEventListener( 'resize', function () { if ( window.innerWidth > 1024 && menu.classList.contains( 'is-open' ) ) { setOpen( false ); } }, { passive: true } ); } if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', boot ); } else { boot(); } })(); (function () { var WA_HREF = 'https://wa.me/6287740098436?text=' + encodeURIComponent( 'Hi J&P Bali! I would like to ask about custom trip apparel.' ); var PAYMENT_LINE = 'Payment: PayPal, Credit Card, Bank Transfer (IDR/AUD), or Cash on Delivery'; function rewriteWhatsApp() { document.querySelectorAll( 'a[href*="wa.me"], a[href*="api.whatsapp.com"], a[href*="whatsapp.com"], a.jnp-wa' ).forEach( function ( a ) { var href = a.getAttribute( 'href' ) || ''; if ( href.indexOf( '6287740098436' ) !== -1 ) { return; } var textMatch = href.match( /[?&]text=([^&]*)/ ); a.setAttribute( 'href', textMatch ? ( 'https://wa.me/6287740098436?text=' + textMatch[ 1 ] ) : WA_HREF ); } ); document.querySelectorAll( 'a[href^="tel:"]' ).forEach( function ( a ) { var href = ( a.getAttribute( 'href' ) || '' ).replace( /\s/g, '' ); if ( /6281234567890|1234567890/.test( href ) ) { a.setAttribute( 'href', 'tel:+6287740098436' ); } } ); } function rewritePaymentLine() { document.querySelectorAll( '.elementor-icon-list-text, .jnp-payment-note' ).forEach( function ( el ) { var t = ( el.textContent || '' ).replace( /\s+/g, ' ' ).trim(); if ( /^Payment:/i.test( t ) && t !== PAYMENT_LINE ) { el.textContent = PAYMENT_LINE; } } ); } function blockSyntheticGalleryClicks() { document.addEventListener( 'click', function ( e ) { var raw = e.target; var el = raw && raw.nodeType === 1 ? raw : ( raw && raw.parentElement ); var modal = el && el.closest && el.closest( '.js-product-modal, .woocommerce-product-gallery__image a' ); if ( ! modal ) { return; } if ( e.isTrusted ) { return; } e.preventDefault(); e.stopImmediatePropagation(); }, true ); } function improveCartLinks() { document.querySelectorAll( 'a[href*="/cart"]' ).forEach( function ( link ) { var text = ( link.textContent || '' ).replace( /\s+/g, ' ' ).trim(); var countMatch = text.match( /^(\d+)$/ ); if ( ! countMatch ) { return; } var count = countMatch[ 1 ]; var label = count === '1' ? 'Cart, 1 item' : 'Cart, ' + count + ' items'; link.setAttribute( 'aria-label', label ); } ); var floatBtn = document.querySelector( '.jnp-float-cart__btn' ); if ( floatBtn ) { var countEl = floatBtn.querySelector( '.jnp-float-cart__count' ); var n = countEl ? parseInt( countEl.getAttribute( 'data-count' ) || countEl.textContent || '0', 10 ) : 0; if ( n > 0 ) { floatBtn.setAttribute( 'aria-label', n === 1 ? 'Open shopping cart, 1 item' : 'Open shopping cart, ' + n + ' items' ); } } } function goToColorSlide( slug ) { var gal = document.querySelector( '.woocommerce-product-gallery' ); if ( ! gal || ! slug ) { return; } var imgs = gal.querySelectorAll( '.woocommerce-product-gallery__image' ); var i; for ( i = 0; i < imgs.length; i++ ) { var im = imgs[ i ].querySelector( 'img' ); var src = im ? ( im.getAttribute( 'src' ) || '' ) : ''; var hay = src.toLowerCase(); if ( hay.indexOf( '-' + slug + '.' ) > -1 || hay.indexOf( '-' + slug + '-' ) > -1 ) { var dots = document.querySelectorAll( '.c-product__slider [aria-label^="Slide"], .js-single-product-carousel .dot' ); if ( dots[ i ] ) { dots[ i ].click(); } break; } } } function bindColorSwatches() { var wrap = document.querySelector( '[data-jnp-color-swatch]' ); if ( ! wrap ) { return; } wrap.querySelectorAll( '.jnp-color-swatch__dot' ).forEach( function ( dot, index ) { dot.setAttribute( 'role', 'button' ); dot.setAttribute( 'tabindex', '0' ); if ( ! dot.getAttribute( 'aria-label' ) ) { var slug = dot.getAttribute( 'data-color-slug' ) || ( 'Color ' + ( index + 1 ) ); dot.setAttribute( 'aria-label', 'Show ' + slug.replace( /-/g, ' ' ) + ' product image' ); } function activateSwatch() { wrap.querySelectorAll( '.jnp-color-swatch__dot' ).forEach( function ( other ) { other.classList.remove( 'is-selected' ); other.setAttribute( 'aria-pressed', 'false' ); } ); dot.classList.add( 'is-selected' ); dot.setAttribute( 'aria-pressed', 'true' ); goToColorSlide( dot.getAttribute( 'data-color-slug' ) ); } dot.addEventListener( 'click', activateSwatch ); dot.addEventListener( 'keydown', function ( e ) { if ( e.key === 'Enter' || e.key === ' ' ) { e.preventDefault(); activateSwatch(); } } ); } ); } function syncHotelFields() { var search = document.getElementById( 'jnp_hotel_search' ); var name = document.getElementById( 'jnp_hotel_name' ); var form = document.querySelector( 'form.checkout' ); if ( ! search || ! name ) { return; } function copy() { if ( search.value && ! name.value ) { name.value = search.value; } } search.addEventListener( 'change', copy ); search.addEventListener( 'blur', copy ); if ( form ) { form.addEventListener( 'submit', copy ); } } function closeCartOnCheckout() { if ( ! document.body.classList.contains( 'woocommerce-checkout' ) ) { return; } var root = document.getElementById( 'jnp-float-cart' ); if ( ! root ) { return; } root.classList.remove( 'is-open' ); var panel = root.querySelector( '.jnp-float-cart__panel' ); var btn = root.querySelector( '.jnp-float-cart__btn' ); if ( panel ) { panel.setAttribute( 'hidden', 'hidden' ); } if ( btn ) { btn.setAttribute( 'aria-expanded', 'false' ); } } function hideHotelDupes() { if ( ! document.body.classList.contains( 'woocommerce-checkout' ) ) { return; } var nameField = document.getElementById( 'jnp_hotel_name_field' ); if ( nameField ) { nameField.style.display = 'none'; } Array.prototype.forEach.call( document.querySelectorAll( '.form-row, p.form-row' ), function ( row ) { var lab = row.querySelector( 'label' ); if ( ! lab ) { return; } var t = ( lab.textContent || '' ).replace( /\s+/g, ' ' ).trim(); if ( ! /Hotel\s*\/\s*Villa/i.test( t ) ) { return; } if ( row.querySelector( '#jnp_hotel_search' ) ) { return; } row.style.display = 'none'; } ); } function hideMugDesignStyle() { if ( ! /mug/i.test( location.pathname + ' ' + document.body.className ) ) { return; } var sel = document.querySelector( '[name="attribute_design-style"], [name="attribute_pa_design-style"]' ); if ( ! sel ) { return; } if ( sel.tagName === 'SELECT' && ! sel.value ) { var i; for ( i = 0; i < sel.options.length; i++ ) { if ( sel.options[ i ].value ) { sel.value = sel.options[ i ].value; sel.dispatchEvent( new Event( 'change', { bubbles: true } ) ); break; } } } var row = sel.closest( 'tr' ); if ( row ) { row.style.display = 'none'; } } function isPhotoMerchProduct() { var body = document.body; if ( ! body || ! body.classList.contains( 'single-product' ) ) { return false; } if ( body.classList.contains( 'product_cat-photo-upload-enabled' ) || body.classList.contains( 'product-tag-photo-merch' ) ) { return true; } if ( document.querySelector( 'a[href*="photo-upload"], .posted_in a[href*="photo-upload"]' ) ) { return true; } if ( document.querySelector( '[name="attribute_design-style"], [name="attribute_pa_design-style"]' ) ) { return true; } return /custom-trip-tee|jp-bali-mug|oversized-tshirt|oversized-hoodie/.test( location.pathname ); } function injectUploadAfterPayBanner() { if ( ! isPhotoMerchProduct() ) { return; } if ( document.querySelector( '.jnp-upload-after-pay' ) ) { return; } var banner = document.createElement( 'div' ); banner.className = 'jnp-upload-after-pay'; banner.setAttribute( 'role', 'note' ); banner.innerHTML = 'Photos upload after payment' + 'No upload field here. After checkout you get a private link (email / WhatsApp) to send your trip photos.'; var anchors = [ 'form.cart .variations', 'form.variations_form .variations', 'form.cart .single_variation_wrap', 'form.cart .quantity', '.c-product__summary form.cart', '.summary form.cart', '.c-product__summary .price', '.summary .price', ]; var i; for ( i = 0; i < anchors.length; i++ ) { var el = document.querySelector( anchors[ i ] ); if ( ! el || ! el.parentNode ) { continue; } el.parentNode.insertBefore( banner, el ); return; } } function relocateExpressCheckout() { var payment = document.getElementById( 'payment' ); if ( ! payment ) { return; } var anchor = payment.querySelector( '.jnp-checkout-step-badge--pay' ) || payment.firstChild; var selectors = [ '.wc-stripe-express-checkout', '#wc-stripe-express-checkout-element', '.wc-stripe-express-checkout-button-separator', ]; selectors.forEach( function ( sel ) { document.querySelectorAll( sel ).forEach( function ( el ) { var mount = el.closest( '.wc-stripe-express-checkout' ) || el; if ( mount && ! payment.contains( mount ) ) { payment.insertBefore( mount, anchor ); } } ); } ); } function relocateCheckoutChrome() { if ( ! document.body.classList.contains( 'woocommerce-checkout' ) ) { return; } var col1 = document.querySelector( '.c-cart__col-1--checkout' ) || document.querySelector( '.c-cart__col-1' ); var colTarget = col1 && ( col1.querySelector( '.js-sticky-sidebar-nearby' ) || col1 ); var progress = document.querySelector( '.jnp-checkout-progress' ); if ( progress && colTarget && ! colTarget.contains( progress ) ) { colTarget.insertBefore( progress, colTarget.firstChild ); } var payNote = document.querySelector( '.jnp-checkout-payment-note' ); var payment = document.getElementById( 'payment' ); if ( payNote && payment && ! payment.contains( payNote ) ) { payment.insertBefore( payNote, payment.firstChild ); } relocateExpressCheckout(); var fieldWrapper = document.querySelector( '.woocommerce-billing-fields__field-wrapper' ); var step2Badge = document.querySelector( '.jnp-checkout-step-badge[data-jnp-step="2"]' ); if ( fieldWrapper && step2Badge && ! fieldWrapper.contains( step2Badge ) ) { fieldWrapper.insertBefore( step2Badge, fieldWrapper.firstChild ); } } var jnpPaymentHome = null; function getCheckoutStep() { var step = parseInt( document.body.getAttribute( 'data-jnp-checkout-step' ), 10 ); return step >= 1 && step <= 3 ? step : 1; } function setSectionHidden( el, hidden ) { if ( ! el ) { return; } if ( hidden ) { el.setAttribute( 'hidden', 'hidden' ); el.setAttribute( 'aria-hidden', 'true' ); el.classList.add( 'jnp-step-hidden' ); el.style.setProperty( 'display', 'none', 'important' ); } else { el.removeAttribute( 'hidden' ); el.setAttribute( 'aria-hidden', 'false' ); el.classList.remove( 'jnp-step-hidden' ); el.style.removeProperty( 'display' ); } } function ensureStep3Shell() { var col1 = document.querySelector( '.c-cart__col-1--checkout' ) || document.querySelector( '.c-cart__col-1' ); if ( ! col1 ) { return null; } var shell = col1.querySelector( '.jnp-checkout-step3-shell' ); if ( shell ) { return shell; } shell = document.createElement( 'div' ); shell.className = 'jnp-checkout-step3-shell'; var badge = document.createElement( 'div' ); badge.className = 'jnp-checkout-step-badge jnp-checkout-step-badge--pay'; badge.setAttribute( 'data-jnp-step', '3' ); badge.innerHTML = 'Step 3 of 3 — Payment' + 'Choose how you want to pay, accept the terms, then place your order.'; shell.appendChild( badge ); col1.appendChild( shell ); return shell; } function relocatePaymentForStep( step ) { var payment = document.getElementById( 'payment' ); if ( ! payment ) { return; } if ( ! jnpPaymentHome ) { jnpPaymentHome = payment.parentElement; } if ( step === 3 ) { var shell = ensureStep3Shell(); if ( shell && ! shell.contains( payment ) ) { shell.appendChild( payment ); } setSectionHidden( payment, false ); return; } if ( jnpPaymentHome && ! jnpPaymentHome.contains( payment ) ) { jnpPaymentHome.appendChild( payment ); } } function applyCheckoutStepVisibility( step ) { var billing = document.querySelector( '.woocommerce-billing-fields' ); var chWrap = document.querySelector( '.jnp-ch-wrap' ); var fieldWrapper = billing && billing.querySelector( '.woocommerce-billing-fields__field-wrapper' ); var additional = document.querySelector( '.woocommerce-additional-fields' ); var payment = document.getElementById( 'payment' ); var col2 = document.querySelector( '.c-cart__col-1 .col-2' ); var billingTitle = billing && billing.querySelector( 'h3' ); setSectionHidden( chWrap, step !== 1 ); setSectionHidden( fieldWrapper, step !== 2 ); if ( billing ) { Array.prototype.forEach.call( billing.children, function ( child ) { if ( child.classList.contains( 'jnp-ch-wrap' ) || child.classList.contains( 'woocommerce-billing-fields__field-wrapper' ) || child.classList.contains( 'jnp-checkout-anchor' ) ) { return; } if ( child.classList.contains( 'jnp-checkout-nav' ) ) { var navStep = parseInt( child.getAttribute( 'data-jnp-step' ), 10 ); setSectionHidden( child, navStep !== step ); return; } if ( child.classList.contains( 'jnp-checkout-step-badge' ) ) { var badgeStep = parseInt( child.getAttribute( 'data-jnp-step' ), 10 ); setSectionHidden( child, badgeStep !== step ); return; } setSectionHidden( child, step !== 2 ); } ); } document.querySelectorAll( '.jnp-checkout-nav[data-jnp-step]' ).forEach( function ( nav ) { var navStep = parseInt( nav.getAttribute( 'data-jnp-step' ), 10 ); setSectionHidden( nav, navStep !== step ); } ); setSectionHidden( additional, step !== 2 ); setSectionHidden( col2, step !== 2 ); /* Show payment method radios from step 2; Place Order stays step 3 via CSS. */ setSectionHidden( payment, step === 1 ); document.querySelectorAll( '.jnp-checkout-step-badge--pay' ).forEach( function ( badge ) { setSectionHidden( badge, step !== 3 ); } ); /* Keep Place Order + terms only on step 3 even when #payment is visible on step 2. */ var placeOrder = document.getElementById( 'place_order' ); var terms = document.querySelector( '.woocommerce-terms-and-conditions-wrapper' ); setSectionHidden( placeOrder, step !== 3 ); setSectionHidden( terms, step !== 3 ); removeLegacyAnchorNavs(); if ( billingTitle ) { billingTitle.textContent = 'How can we reach you?'; setSectionHidden( billingTitle, step !== 2 ); } if ( step !== 3 ) { document.querySelectorAll( '.wc-stripe-express-checkout, #wc-stripe-express-checkout-element, .wc-stripe-express-checkout-button-separator, .ppc-button-wrapper, .c-cart__col-2 .separator, .c-cart__sidebar .separator, .woocommerce-checkout-payment .separator' ).forEach( function ( el ) { setSectionHidden( el, true ); } ); } else { document.querySelectorAll( '.wc-stripe-express-checkout, #wc-stripe-express-checkout-element, .wc-stripe-express-checkout-button-separator' ).forEach( function ( el ) { setSectionHidden( el, false ); } ); relocateExpressCheckout(); } relocatePaymentForStep( step ); var step3Shell = document.querySelector( '.jnp-checkout-step3-shell' ); setSectionHidden( step3Shell, step !== 3 ); } function setCheckoutStep( step ) { document.body.setAttribute( 'data-jnp-checkout-step', String( step ) ); var progressItems = document.querySelectorAll( '.jnp-checkout-progress__item' ); Array.prototype.forEach.call( progressItems, function ( item, index ) { var num = index + 1; item.classList.toggle( 'is-current', num === step ); item.classList.toggle( 'is-done', num < step ); } ); applyCheckoutStepVisibility( step ); } function clearCheckoutNavError( nav ) { if ( ! nav ) { return; } var err = nav.querySelector( '.jnp-checkout-nav__error' ); if ( err ) { err.remove(); } } function showCheckoutNavError( nav, message ) { clearCheckoutNavError( nav ); if ( ! nav || ! message ) { return; } var err = document.createElement( 'p' ); err.className = 'jnp-checkout-nav__error'; err.setAttribute( 'role', 'alert' ); err.textContent = message; nav.insertBefore( err, nav.firstChild ); } function validateHotelStep() { var wrap = document.querySelector( '.jnp-ch-wrap' ); if ( ! wrap ) { return true; } var search = document.getElementById( 'jnp_hotel_search' ); var name = document.getElementById( 'jnp_hotel_name' ); var date = document.getElementById( 'jnp_checkout_date' ); if ( search && name && search.value && ! name.value ) { name.value = search.value; } if ( search && ! ( search.value || '' ).trim() ) { search.focus(); search.reportValidity && search.reportValidity(); return false; } if ( date && ! date.value ) { date.focus(); date.reportValidity && date.reportValidity(); return false; } return validateVisibleFields( wrap ); } function validateVisibleFields( container ) { if ( ! container ) { return true; } var fields = container.querySelectorAll( 'input, select, textarea' ); for ( var i = 0; i < fields.length; i++ ) { var field = fields[ i ]; if ( field.type === 'hidden' || field.disabled ) { continue; } if ( field.offsetParent === null && field.type !== 'radio' && field.type !== 'checkbox' ) { continue; } if ( typeof field.checkValidity === 'function' && ! field.checkValidity() ) { field.reportValidity(); field.focus(); return false; } } return true; } function isLegacyAnchorNav( nav ) { return nav && nav.querySelector( 'a[href^="#jnp-step"]' ); } function removeLegacyAnchorNavs() { document.querySelectorAll( '.jnp-checkout-nav--step1, .jnp-checkout-nav--step2' ).forEach( function ( nav ) { if ( isLegacyAnchorNav( nav ) ) { nav.remove(); } } ); } function ensureCheckoutNav( container, step ) { if ( ! container ) { return null; } var existing = container.querySelector( ':scope > .jnp-checkout-nav[data-jnp-step="' + step + '"]' ); if ( existing && isLegacyAnchorNav( existing ) ) { existing.remove(); existing = null; } if ( existing ) { return existing; } var nav = document.createElement( 'div' ); nav.className = 'jnp-checkout-nav'; nav.setAttribute( 'data-jnp-step', String( step ) ); if ( step > 1 ) { var back = document.createElement( 'button' ); back.type = 'button'; back.className = 'jnp-checkout-nav__btn jnp-checkout-nav__btn--back jnp-checkout-nav--back'; back.textContent = step === 2 ? 'Back to hotel' : 'Back to contact'; back.addEventListener( 'click', function () { clearCheckoutNavError( nav ); setCheckoutStep( step - 1 ); window.scrollTo( { top: 0, behavior: 'smooth' } ); } ); nav.appendChild( back ); } if ( step < 3 ) { var next = document.createElement( 'button' ); next.type = 'button'; next.className = 'jnp-checkout-nav__btn jnp-checkout-nav--continue'; next.textContent = step === 1 ? 'Continue to contact' : 'Continue to payment'; next.addEventListener( 'click', function () { clearCheckoutNavError( nav ); var valid = step === 1 ? validateHotelStep() : validateVisibleFields( document.querySelector( '.woocommerce-billing-fields' ) ); if ( ! valid ) { return; } setCheckoutStep( step + 1 ); window.scrollTo( { top: 0, behavior: 'smooth' } ); } ); nav.appendChild( next ); } container.appendChild( nav ); return nav; } function initCheckoutWizard() { if ( ! document.body.classList.contains( 'woocommerce-checkout' ) ) { return; } if ( document.body.classList.contains( 'woocommerce-order-received' ) ) { return; } var firstRun = document.body.dataset.jnpCheckoutWizard !== '1'; if ( firstRun ) { document.body.classList.add( 'jnp-checkout-wizard' ); document.body.dataset.jnpCheckoutWizard = '1'; removeLegacyAnchorNavs(); relocateCheckoutChrome(); ensureCheckoutNav( document.querySelector( '.jnp-ch-wrap' ), 1 ); ensureCheckoutNav( document.querySelector( '.woocommerce-billing-fields' ), 2 ); } var payment = document.getElementById( 'payment' ); if ( payment ) { var payNav = ensureCheckoutNav( payment, 3 ); var placeOrderBtn = document.getElementById( 'place_order' ); if ( payNav && placeOrderBtn && placeOrderBtn.parentNode && payNav.parentNode !== placeOrderBtn.parentNode ) { placeOrderBtn.parentNode.insertBefore( payNav, placeOrderBtn ); } else if ( payNav && placeOrderBtn && placeOrderBtn.parentNode && payNav.nextElementSibling !== placeOrderBtn ) { placeOrderBtn.parentNode.insertBefore( payNav, placeOrderBtn ); } } setCheckoutStep( getCheckoutStep() || 1 ); if ( window.jQuery && document.body.dataset.jnpCheckoutAjaxBound !== '1' ) { document.body.dataset.jnpCheckoutAjaxBound = '1'; window.jQuery( document.body ).on( 'updated_checkout', function () { initCheckoutWizard(); } ); } } function fixOrderReceivedContrast() { if ( ! document.body.classList.contains( 'woocommerce-order-received' ) ) { return; } var valueSelectors = [ '.c-order__details-value', '.woocommerce-order-overview li', '.woocommerce-table td', '.woocommerce-table .woocommerce-Price-amount', '.woocommerce-table .amount', '.woocommerce-orders-table td', ]; valueSelectors.forEach( function ( sel ) { document.querySelectorAll( sel ).forEach( function ( el ) { el.style.setProperty( 'color', '#ffffff', 'important' ); el.style.setProperty( '-webkit-text-fill-color', '#ffffff', 'important' ); el.style.setProperty( 'opacity', '1', 'important' ); } ); } ); document.querySelectorAll( '.c-order__details-title, .woocommerce-table th' ).forEach( function ( el ) { el.style.setProperty( 'color', '#b3b3b3', 'important' ); el.style.setProperty( '-webkit-text-fill-color', '#b3b3b3', 'important' ); } ); document.querySelectorAll( '.c-order__details-item--total .c-order__details-value, .order-total .amount, tr.order-total td' ).forEach( function ( el ) { el.style.setProperty( 'color', '#d4af37', 'important' ); el.style.setProperty( '-webkit-text-fill-color', '#d4af37', 'important' ); } ); } function initTouristCheckout() { if ( ! document.body.classList.contains( 'woocommerce-checkout' ) ) { return; } if ( document.body.classList.contains( 'woocommerce-order-received' ) ) { fixOrderReceivedContrast(); return; } initCheckoutWizard(); var wrap = document.querySelector( '.jnp-ch-wrap' ); if ( wrap && ! wrap.querySelector( '.jnp-checkout-step-badge[data-jnp-step="1"]' ) ) { var stepBadge = document.createElement( 'p' ); stepBadge.className = 'jnp-checkout-step-badge'; stepBadge.setAttribute( 'data-jnp-step', '1' ); stepBadge.textContent = 'Step 1 of 3'; var title = wrap.querySelector( '.jnp-ch-title' ); if ( title && title.parentNode ) { title.parentNode.insertBefore( stepBadge, title ); } else { wrap.insertBefore( stepBadge, wrap.firstChild ); } } var search = document.getElementById( 'jnp_hotel_search' ); var list = document.querySelector( '.jnp-ch-list' ); if ( search ) { if ( list && ! list.id ) { list.id = 'jnp_hotel_autocomplete'; } search.setAttribute( 'role', 'combobox' ); search.setAttribute( 'aria-autocomplete', 'list' ); search.setAttribute( 'aria-haspopup', 'listbox' ); if ( list ) { search.setAttribute( 'aria-controls', list.id ); list.setAttribute( 'role', 'listbox' ); } function syncExpanded() { var open = list && list.classList.contains( 'is-open' ); search.setAttribute( 'aria-expanded', open ? 'true' : 'false' ); } search.addEventListener( 'input', syncExpanded ); search.addEventListener( 'focus', syncExpanded ); search.addEventListener( 'blur', function () { window.setTimeout( syncExpanded, 200 ); } ); if ( window.MutationObserver && list ) { new MutationObserver( syncExpanded ).observe( list, { attributes: true, attributeFilter: [ 'class' ], } ); } syncExpanded(); } var progressItems = document.querySelectorAll( '.jnp-checkout-progress__item' ); if ( progressItems.length ) { Array.prototype.forEach.call( progressItems, function ( item, index ) { item.style.cursor = 'pointer'; item.addEventListener( 'click', function () { var targetStep = index + 1; var current = getCheckoutStep(); if ( targetStep < current ) { setCheckoutStep( targetStep ); window.scrollTo( { top: 0, behavior: 'smooth' } ); } } ); } ); } } rewriteWhatsApp(); rewritePaymentLine(); blockSyntheticGalleryClicks(); bindColorSwatches(); syncHotelFields(); closeCartOnCheckout(); hideHotelDupes(); hideMugDesignStyle(); injectUploadAfterPayBanner(); initTouristCheckout(); fixOrderReceivedContrast(); improveCartLinks(); if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', function () { rewriteWhatsApp(); rewritePaymentLine(); bindColorSwatches(); syncHotelFields(); closeCartOnCheckout(); hideHotelDupes(); hideMugDesignStyle(); injectUploadAfterPayBanner(); initTouristCheckout(); fixOrderReceivedContrast(); improveCartLinks(); } ); } window.setTimeout( function () { rewriteWhatsApp(); rewritePaymentLine(); relocateCheckoutChrome(); hideMugDesignStyle(); injectUploadAfterPayBanner(); initTouristCheckout(); fixOrderReceivedContrast(); }, 800 ); if ( window.MutationObserver ) { var waTimer = null; new MutationObserver( function () { window.clearTimeout( waTimer ); waTimer = window.setTimeout( rewriteWhatsApp, 200 ); } ).observe( document.documentElement, { childList: true, subtree: true } ); } })();