diff options
| author | Roger Rehnelt <noreply@maennchen1.de> | 2026-10-01 19:31:19 +0200 |
|---|---|---|
| committer | Roger Rehnelt <noreply@maennchen1.de> | 2026-10-01 19:31:19 +0200 |
| commit | e48e3fc448c19d85e65c7267b230232addf156aa (patch) | |
| tree | 93f663ae228ddaabfce2c4eeb997df2cbc1f597e /views/mods/mod_productview/productview.js | |
| parent | 576c216136740d1da2f9b19616818c610ca57a16 (diff) | |
Produktansichten: Modul aus ShopThemeOne im Plugin
[wpsg_productview] und [wpsg_productview_filter] kommen ins Plugin; Seite, Sortierung, Ansicht und Filter stehen in der Adresse, Ajax übernimmt nur diese Werte. Ein gleichnamiges Modul im Theme bricht den Shop nicht mehr ab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'views/mods/mod_productview/productview.js')
| -rw-r--r-- | views/mods/mod_productview/productview.js | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/views/mods/mod_productview/productview.js b/views/mods/mod_productview/productview.js new file mode 100644 index 0000000..5879d68 --- /dev/null +++ b/views/mods/mod_productview/productview.js @@ -0,0 +1,148 @@ +/** + * wpShopGermany, Modul Produktansichten: Seiten, Sortierung, Ansicht und Filter ohne Neuladen + * + * Ohne JavaScript funktionieren Links und Formulare über die Adresse. Mit JavaScript holt das Skript die Ansicht per + * Ajax (wpsg_mod=wpsg_mod_productview, wpsg_action=refresh) und schreibt den Zustand in die Adresse. Die Attribute des + * Shortcodes kommen signiert aus dem Feld wpsg_atts; der Server übernimmt nur Seite, Sortierung, Ansicht und Filter. + */ + +( function() { + + 'use strict'; + + function flatten( prefix, value, params ) { + + if ( value !== null && typeof value === 'object' ) { + + Object.keys( value ).forEach( function( k ) { flatten( prefix + '[' + k + ']', value[ k ], params ); } ); + + } else if ( value !== undefined && value !== null ) { + + params.append( prefix, String( value ) ); + + } + + return params; + + } + + function filterValues() { + + const form = document.getElementById( 'wpsg_mod_productview_filter' ); + const values = {}; + + if ( !form ) return values; + + new FormData( form ).forEach( function( v, name ) { + + const m = name.match( /^pvf\[(\w+)\](?:\[(\d+)\])?/ ); + if ( !m || v === '' ) return; + + if ( m[ 2 ] ) { + + values[ m[ 1 ] ] = values[ m[ 1 ] ] || {}; + values[ m[ 1 ] ][ m[ 2 ] ] = values[ m[ 1 ] ][ m[ 2 ] ] ? values[ m[ 1 ] ][ m[ 2 ] ] + ',' + v : v; + + } else values[ m[ 1 ] ] = v; + + } ); + + return values; + + } + + function refresh( box, change, url ) { + + const field = box.querySelector( 'input[name="wpsg_atts"]' ); + if ( !field ) return false; + + const atts = JSON.parse( field.value ); + Object.assign( atts, change ); + + const params = flatten( 'wpsg_atts', atts, new URLSearchParams( { wpsg_mod: 'wpsg_mod_productview', wpsg_action: 'refresh' } ) ); + + box.classList.add( 'is-loading' ); + box.setAttribute( 'aria-busy', 'true' ); + + fetch( box.getAttribute( 'data-wpsg-pv-url' ) + ( box.getAttribute( 'data-wpsg-pv-url' ).indexOf( '?' ) === -1 ? '?' : '&' ) + params.toString(), { credentials: 'same-origin' } ) + .then( function( r ) { if ( !r.ok ) throw new Error( r.status ); return r.text(); } ) + .then( function( html ) { + + const tmp = document.createElement( 'div' ); + tmp.innerHTML = html; + + const fresh = tmp.querySelector( '.wpsg_productview' ); + if ( !fresh ) throw new Error( 'leer' ); + + fresh.querySelectorAll( '.wpsg-pv__order-submit' ).forEach( function( b ) { b.hidden = true; } ); + box.replaceWith( fresh ); + if ( url ) history.pushState( { wpsgPv: true }, '', url ); + + } ) + .catch( function() { if ( url ) location.href = url; } ); + + return true; + + } + + document.addEventListener( 'click', function( e ) { + + const a = e.target.closest( '.wpsg-pv__pager a, .wpsg-pv__modes a' ); + if ( !a ) return; + + const box = a.closest( '.wpsg_productview' ); + const change = a.hasAttribute( 'data-page' ) ? { page: a.getAttribute( 'data-page' ) } : { mode: a.getAttribute( 'data-mode' ) }; + + if ( refresh( box, change, a.href ) ) e.preventDefault(); + + } ); + + document.addEventListener( 'change', function( e ) { + + const select = e.target.closest( '.wpsg-pv__order select' ); + + if ( select ) { + + const form = select.form; + const url = form.action + ( form.action.indexOf( '?' ) === -1 ? '?' : '&' ) + new URLSearchParams( new FormData( form ) ).toString(); + + refresh( select.closest( '.wpsg_productview' ), { order: select.value, page: 1 }, url ); + return; + + } + + if ( e.target.closest( '#wpsg_mod_productview_filter' ) ) applyFilter(); + + } ); + + document.addEventListener( 'submit', function( e ) { + + if ( e.target.id !== 'wpsg_mod_productview_filter' ) return; + + if ( applyFilter() ) e.preventDefault(); + + } ); + + function applyFilter() { + + const form = document.getElementById( 'wpsg_mod_productview_filter' ); + const box = document.querySelector( '.wpsg_productview' ); + if ( !form || !box ) return false; + + const params = new URLSearchParams(); + new FormData( form ).forEach( function( v, name ) { if ( v !== '' && !/^pv-/.test( name ) ) params.append( name, v ); } ); + + // Sortierung und Ansicht aus der aktuellen Adresse behalten, die Seite beginnt neu + new URLSearchParams( location.search ).forEach( function( v, name ) { if ( /^pv-.*(order|mode)$/.test( name ) ) params.append( name, v ); } ); + + return refresh( box, { filter: filterValues(), page: 1 }, form.action + ( form.action.indexOf( '?' ) === -1 ? '?' : '&' ) + params.toString() ); + + } + + // Zurück und Vor: den Stand aus der Adresse neu laden + window.addEventListener( 'popstate', function( e ) { if ( e.state && e.state.wpsgPv ) location.reload(); } ); + + // Mit JavaScript sortiert die Auswahl sofort + document.querySelectorAll( '.wpsg-pv__order-submit' ).forEach( function( b ) { b.hidden = true; } ); + +} )(); |
