woocommerce-paypal-payments/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js

371 lines
8.9 KiB
JavaScript

import UpdateCart from '../Helper/UpdateCart';
import SingleProductActionHandler from '../ActionHandler/SingleProductActionHandler';
import { hide, show } from '../Helper/Hiding';
import BootstrapHelper from '../Helper/BootstrapHelper';
import { loadPaypalJsScript } from '../Helper/ScriptLoading';
import { getPlanIdFromVariation } from '../Helper/Subscriptions';
import SimulateCart from '../Helper/SimulateCart';
import { strRemoveWord, strAddWord, throttle } from '../Helper/Utils';
import merge from 'deepmerge';
import { debounce } from '../../../../../ppcp-blocks/resources/js/Helper/debounce';
class SingleProductBootstap {
constructor( gateway, renderer, errorHandler ) {
this.gateway = gateway;
this.renderer = renderer;
this.errorHandler = errorHandler;
this.mutationObserver = new MutationObserver(
this.handleChange.bind( this )
);
this.formSelector = 'form.cart';
// Prevent simulate cart being called too many times in a burst.
this.simulateCartThrottled = throttle(
this.simulateCart.bind( this ),
this.gateway.simulate_cart.throttling || 5000
);
this.debouncedHandleChange = debounce(
this.handleChange.bind( this ),
100
);
this.renderer.onButtonsInit(
this.gateway.button.wrapper,
() => {
this.handleChange();
},
true
);
this.subscriptionButtonsLoaded = false;
}
form() {
return document.querySelector( this.formSelector );
}
handleChange() {
this.subscriptionButtonsLoaded = false;
if ( ! this.shouldRender() ) {
this.renderer.disableSmartButtons( this.gateway.button.wrapper );
hide( this.gateway.button.wrapper, this.formSelector );
return;
}
this.render();
this.renderer.enableSmartButtons( this.gateway.button.wrapper );
show( this.gateway.button.wrapper );
this.handleButtonStatus();
}
handleButtonStatus( simulateCart = true ) {
BootstrapHelper.handleButtonStatus( this, {
formSelector: this.formSelector,
} );
if ( simulateCart ) {
this.simulateCartThrottled();
}
}
init() {
const form = this.form();
if ( ! form ) {
return;
}
jQuery( document ).on( 'change', this.formSelector, () => {
this.debouncedHandleChange();
} );
this.mutationObserver.observe( form, {
childList: true,
subtree: true,
} );
const addToCartButton = form.querySelector(
'.single_add_to_cart_button'
);
if ( addToCartButton ) {
new MutationObserver(
this.handleButtonStatus.bind( this )
).observe( addToCartButton, { attributes: true } );
}
jQuery( document ).on( 'ppcp_should_show_messages', ( e, data ) => {
if ( ! this.shouldRender() ) {
data.result = false;
}
} );
if ( ! this.shouldRender() ) {
return;
}
this.render();
this.handleChange();
}
shouldRender() {
return this.form() !== null && ! this.isWcsattSubscriptionMode();
}
shouldEnable() {
const form = this.form();
const addToCartButton = form
? form.querySelector( '.single_add_to_cart_button' )
: null;
return (
BootstrapHelper.shouldEnable( this ) &&
! this.priceAmountIsZero() &&
( null === addToCartButton ||
! addToCartButton.classList.contains( 'disabled' ) )
);
}
priceAmount( returnOnUndefined = 0 ) {
const priceText = [
() =>
document.querySelector(
'form.cart ins .woocommerce-Price-amount'
)?.innerText,
() =>
document.querySelector( 'form.cart .woocommerce-Price-amount' )
?.innerText,
() => {
const priceEl = document.querySelector(
'.product .woocommerce-Price-amount'
);
// variable products show price like 10.00 - 20.00 here
// but the second price also can be the suffix with the price incl/excl tax
if ( priceEl ) {
const allPriceElements = Array.from(
priceEl.parentElement.querySelectorAll(
'.woocommerce-Price-amount'
)
).filter(
( el ) =>
! el.parentElement.classList.contains(
'woocommerce-price-suffix'
)
);
if ( allPriceElements.length === 1 ) {
return priceEl.innerText;
}
}
return null;
},
]
.map( ( f ) => f() )
.find( ( val ) => val );
if ( typeof priceText === 'undefined' ) {
return returnOnUndefined;
}
if ( ! priceText ) {
return 0;
}
return parseFloat(
priceText.replace( /,/g, '.' ).replace( /([^\d,\.\s]*)/g, '' )
);
}
priceAmountIsZero() {
const price = this.priceAmount( -1 );
// if we can't find the price in the DOM we want to return true so the button is visible.
if ( price === -1 ) {
return false;
}
return ! price || price === 0;
}
isWcsattSubscriptionMode() {
// Check "All products for subscriptions" plugin.
return (
document.querySelector(
'.wcsatt-options-product:not(.wcsatt-options-product--hidden) .subscription-option input[type="radio"]:checked'
) !== null ||
document.querySelector(
'.wcsatt-options-prompt-label-subscription input[type="radio"]:checked'
) !== null
); // grouped
}
variations() {
if ( ! this.hasVariations() ) {
return null;
}
return [
...document
.querySelector( 'form.cart' )
?.querySelectorAll( "[name^='attribute_']" ),
].map( ( element ) => {
return {
value: element.value,
name: element.name,
};
} );
}
hasVariations() {
return document
.querySelector( 'form.cart' )
?.classList.contains( 'variations_form' );
}
render() {
const actionHandler = new SingleProductActionHandler(
this.gateway,
new UpdateCart(
this.gateway.ajax.change_cart.endpoint,
this.gateway.ajax.change_cart.nonce
),
this.form(),
this.errorHandler
);
if (
! this.gateway.vaultingEnabled &&
[ 'subscription', 'variable-subscription' ].includes(
this.gateway.productType
) &&
this.gateway.manualRenewalEnabled !== '1'
) {
return;
}
if (
PayPalCommerceGateway.data_client_id.has_subscriptions &&
PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled
) {
const buttonWrapper = document.getElementById(
'ppc-button-ppcp-gateway'
);
buttonWrapper.innerHTML = '';
const subscription_plan =
this.variations() !== null
? getPlanIdFromVariation( this.variations() )
: PayPalCommerceGateway.subscription_plan_id;
if ( ! subscription_plan ) {
return;
}
if ( this.subscriptionButtonsLoaded ) {
return;
}
loadPaypalJsScript(
{
clientId: PayPalCommerceGateway.client_id,
currency: PayPalCommerceGateway.currency,
intent: 'subscription',
vault: true,
},
actionHandler.subscriptionsConfiguration( subscription_plan ),
this.gateway.button.wrapper
);
this.subscriptionButtonsLoaded = true;
return;
}
this.renderer.render( actionHandler.configuration() );
}
simulateCart() {
if ( ! this.gateway.simulate_cart.enabled ) {
return;
}
const actionHandler = new SingleProductActionHandler(
null,
null,
this.form(),
this.errorHandler
);
const hasSubscriptions =
PayPalCommerceGateway.data_client_id.has_subscriptions &&
PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled;
const products = hasSubscriptions
? actionHandler.getSubscriptionProducts()
: actionHandler.getProducts();
new SimulateCart(
this.gateway.ajax.simulate_cart.endpoint,
this.gateway.ajax.simulate_cart.nonce
).simulate( ( data ) => {
jQuery( document.body ).trigger( 'ppcp_product_total_updated', [
data.total,
] );
let newData = {};
if ( typeof data.button.is_disabled === 'boolean' ) {
newData = merge( newData, {
button: { is_disabled: data.button.is_disabled },
} );
}
if ( typeof data.messages.is_hidden === 'boolean' ) {
newData = merge( newData, {
messages: { is_hidden: data.messages.is_hidden },
} );
}
if ( newData ) {
BootstrapHelper.updateScriptData( this, newData );
}
if ( this.gateway.single_product_buttons_enabled !== '1' ) {
return;
}
let enableFunding = this.gateway.url_params[ 'enable-funding' ];
let disableFunding = this.gateway.url_params[ 'disable-funding' ];
for ( const [ fundingSource, funding ] of Object.entries(
data.funding
) ) {
if ( funding.enabled === true ) {
enableFunding = strAddWord( enableFunding, fundingSource );
disableFunding = strRemoveWord(
disableFunding,
fundingSource
);
} else if ( funding.enabled === false ) {
enableFunding = strRemoveWord(
enableFunding,
fundingSource
);
disableFunding = strAddWord(
disableFunding,
fundingSource
);
}
}
if (
enableFunding !== this.gateway.url_params[ 'enable-funding' ] ||
disableFunding !== this.gateway.url_params[ 'disable-funding' ]
) {
this.gateway.url_params[ 'enable-funding' ] = enableFunding;
this.gateway.url_params[ 'disable-funding' ] = disableFunding;
jQuery( this.gateway.button.wrapper ).trigger(
'ppcp-reload-buttons'
);
}
this.handleButtonStatus( false );
}, products );
}
}
export default SingleProductBootstap;