Source: listeners/choose-your-delivery.js

import Listener from "./listener";

/**
 * 'choose_your_delivery' - triggered when the user clicks on a delivery option on the product page.
 * Supports the new and old PDP widgets.
 * @class
 * @extends Listener
 */
export class ChooseYourDeliveryListener extends Listener {
    eventName = "choose_your_delivery";

    /**
     * Depending on the product page, the scent buttons may have different selectors.
     * This array contains all the selectors we're interested in.
     * @type {string[]} - CSS selectors for the scent buttons on the page.
    */
    selectors = [
        '.op-sub-select', // new PDP
        '.cof-sub-select', // old PDP
        'input[name="purchase_type"]' // Power Pebbles
    ];

    constructor(productName) {
        super();
        this.productName = productName;
    }

    listen() {
        const buttons = this.getOptionButtons();
        for (const btn of buttons) {
            btn.addEventListener('click',
                (event) => {
                    // Ignore the automated click events dispatched by other scripts
                    if (event.isTrusted) this.trigger(btn.option);
                }
            );
        }
    }

    /**
     * Gets the option buttons on the page.
     * Uses the selectors array to find the buttons.
     * Uses the `createOptionButton` method to create the button instances.
     * @memberof ChooseYourDeliveryListener
     * @returns {DeliveryOptionButton[]} - An array of DeliveryOptionButton instances.
     */
    getOptionButtons() {
        for (const selector of this.selectors) {
            const buttonElements = document.querySelectorAll(selector);
            const buttons = Array.from(buttonElements).map(
                btn => this.createOptionButton(selector, btn)
            );
            if (buttons.length > 0) return buttons;
        }
        return [];
    }

    /**
     * Abstract factory method for creating DeliveryOptionButton instances.
     * This allows the subclasses to create different types of OptionButtons. (ie Scent, Delivery)
     * @param {string} selector - The selector used to find the button.
     * @param {HTMLElement} element - The button element.
     * @returns {DeliveryOptionButton} - An instance of DeliveryOptionButton.
     */
    createOptionButton(selector, element) {
        switch (selector) {
            case '.cof-sub-select':
                return new DeliveryButtonOldVariant(element);
            case 'input[name="purchase_type"]':
                return new DeliveryButtonPebbles(element);
            default:
                return new DeliveryOptionButton(element);
        }
    }

    createPayload(delivery) {
        return {
            selected_item: this.productName,
            selected_item2: delivery
        };
    }
}

/**
 * Wrapper class for the delivery option buttons.
 * Exposes the option property which is used to create the payload.
 * @class
 * @param {HTMLElement} btn - The button element.
 */
export class DeliveryOptionButton {
    constructor(btn) {
        this.btn = btn;
    }

    /**
     * The option value for the button.
     * @type {string}
     */
    get option() {
        return this.btn.dataset.subtype === 'sub'
            ? 'subscription'
            : 'onetime';
    }

    /**
     * Wrapper for the native addEventListener method.
     * @param  {...any} args - Arguments to pass to the addEventListener method.
     */
    addEventListener(...args) {
        this.btn.addEventListener(...args);
    }
}

/**
 * Variation on the {@link DeliveryOptionButton} that supports the Power Pebbles product page.
 * @class
 * @param {HTMLElement} btn - The button element.
 * @extends DeliveryOptionButton
 */
export class DeliveryButtonPebbles extends DeliveryOptionButton {
    get option() {
        return this.btn.value === 'onetime'
            ? 'onetime'
            : 'subscription';
    }
}

/**
 * Variation on the {@link DeliveryOptionButton} that supports the old PDP widget.
 * Useful for backward compatibility between product page variations.
 * @class
 * @param {HTMLElement} btn - The button element.
 * @extends DeliveryOptionButton
 */
class DeliveryButtonOldVariant extends DeliveryOptionButton {
    get option() {
        const isSubOption = this.btn.classList.contains('cof-sub');
        return isSubOption ? 'subscription' : 'onetime';
    }
}