{"version":3,"file":"swatch-form.js","names":["#renderSwatches","#initialiseSwatchForm","#handleDetailChangeClick","#syncBrochureState"],"sources":["../resources/javascript/swatch-form.js"],"sourcesContent":["import { renderSwatchGrid } from './swatch/render-swatch-grid';\nimport { Loqate } from './form/loqate';\nimport { getBrochureOptin, setBrochureOptin } from './swatch/local-storage';\n\n/**\n * Class representing a form for managing swatches.\n * Handles swatch form initialization, rendering swatches, and form interactions.\n */\nclass SwatchForm {\n    constructor() {\n        this.form = document.querySelector('[data-swatch-form]');\n\n        this.options = {\n            onValidationError: function (xhr) {},\n            afterErrorsScrollToField: false,\n            afterErrorsFocusField: false,\n        };\n    }\n\n    init() {\n        this.#renderSwatches();\n        this.#initialiseSwatchForm();\n    }\n\n    /**\n     * Initializes the swatch form by setting up event listeners.\n     * @private\n     */\n    #initialiseSwatchForm() {\n        const buttons = document.querySelectorAll(\n            '.swatch-form-option-select__checkbox'\n        );\n\n        buttons.forEach(button => {\n            button.addEventListener('click', e => {\n                if (\n                    e.target.classList.contains(\n                        'swatch-form-option-select--chosen'\n                    )\n                ) {\n                    e.target.classList.remove(\n                        'swatch-form-option-select--chosen'\n                    );\n                } else {\n                    e.target.classList.add('swatch-form-option-select--chosen');\n                }\n            });\n        });\n\n        const detailChangeBtn = document.querySelector(\n            '.js-checkout-section__change-detail button'\n        );\n\n        detailChangeBtn?.addEventListener('click', e =>\n            this.#handleDetailChangeClick(e)\n        );\n\n        this.form.addEventListener('submit', e => {\n            this.disableSubmit();\n            this.validate(e);\n        });\n\n        new Loqate(this.form);\n\n        this.#syncBrochureState();\n    }\n\n    /**\n     * Renders swatches in the swatch form.\n     * @private\n     */\n    #renderSwatches() {\n        const swatchGridForm = document.querySelector('.swatch-grid--form');\n        const hasSwatches =\n            swatchGridForm?.querySelector('.swatch-grid__swatch') ?? null;\n\n        if (hasSwatches) {\n            return;\n        }\n\n        const swatchesContainer = document.querySelector(\n            '[data-swatch-form-list]'\n        );\n        const swatchUrlnamesInput = document.querySelector(\n            '[data-swatch-urlnames]'\n        );\n\n        if (!swatchesContainer || !swatchUrlnamesInput) {\n            return;\n        }\n\n        // Retrieves swatches from local storage or defaults to an empty array\n        const swatches = JSON.parse(localStorage.getItem('swatches')) ?? [];\n\n        if (swatches.length === 0) {\n            this.addBannerError(\n                'No swatches found. <a href=\"/swatches\" class=\"link\">Add Swatches</a>'\n            );\n        }\n\n        // Enrich swatches with image URLs from server-side data attribute\n        const imageMapRaw = swatchesContainer?.dataset.swatchImages;\n        if (imageMapRaw) {\n            try {\n                const imageMap = JSON.parse(imageMapRaw);\n                for (const swatch of swatches) {\n                    const urlname = JSON.parse(swatch.value)?.colour;\n                    if (urlname && imageMap[urlname]) {\n                        swatch.imageUrl = imageMap[urlname];\n                    }\n                }\n            } catch {\n                // ignore parse errors\n            }\n        }\n\n        // Generates a swatch grid and appends it to the swatch container\n        const swatchGrid = renderSwatchGrid(\n            swatches,\n            ['swatch-grid--form', 'swatch-grid--no-hover'],\n            {\n                addSwatchCategoryName: true,\n            }\n        );\n\n        swatchesContainer?.appendChild(swatchGrid);\n\n        let swatchUrlNames = [];\n\n        swatches.forEach(swatch => {\n            swatchUrlNames.push(JSON.parse(swatch.value).colour);\n        });\n\n        if (swatchUrlnamesInput) {\n            swatchUrlnamesInput.value = swatchUrlNames.join(',');\n        }\n    }\n\n    #handleDetailChangeClick(e) {\n        e.preventDefault();\n\n        const detailsInput = document.querySelector(\n            '.checkout-address-form__customer'\n        );\n\n        detailsInput?.classList.remove('d3r-hidden');\n    }\n\n    /**\n     * Validate the form using javascript\n     */\n    validate(event) {\n        if (this.xhr && this.xhr.readyState < 4) {\n            this.xhr.abort();\n        }\n\n        this.clearErrors();\n\n        this.xhr = new XMLHttpRequest();\n        this.xhr.open('POST', this.form.getAttribute('action'), false);\n        this.xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n        this.xhr.setRequestHeader(\n            'Content-Type',\n            'application/x-www-form-urlencoded'\n        );\n\n        this.xhr.onload = () => {\n            try {\n                let response = JSON.parse(this.xhr.response);\n                console.log(response);\n                if (!response.result) {\n                    event.preventDefault();\n                    this.addBannerError(response.payload.message);\n                    this.addErrors(response.payload);\n                    this.enableSubmit();\n                } else {\n                    localStorage.removeItem('swatches');\n                    setBrochureOptin(false);\n                }\n            } catch (error) {\n                event.preventDefault();\n                console.log(error);\n                this.options.onValidationError.call(this, this.xhr);\n            }\n        };\n\n        let form = new FormData(this.form);\n        form.set('action', 'validate');\n\n        this.xhr.send(new URLSearchParams(form));\n\n        return false;\n    }\n\n    /**\n     * Add errors to the form\n     *\n     * @param {object} errors\n     */\n    addErrors(errors) {\n        for (const field in errors.order) {\n            const error = errors.order[field];\n            this.addError(field, error);\n        }\n\n        let scrollFunction = this.options.afterErrorsScrollToField\n            ? 'scrollToFieldError'\n            : 'scrollToMessageError';\n        setTimeout(this[scrollFunction].bind(this), 10);\n    }\n\n    /**\n     * Add an error message to a field\n     *\n     * @param {string} field\n     * @param {string} message\n     */\n    addError(field, message) {\n        let wrap = this.form.querySelector('#' + field + '_wrap');\n\n        if (!wrap) {\n            return;\n        }\n\n        if (\n            wrap.parentElement.classList.contains(\n                'swatch-form-address-form__address'\n            )\n        ) {\n            wrap.parentElement.parentElement.classList.add(\n                'swatch-form-address-form--manual'\n            );\n        }\n\n        wrap.classList.add('field-error');\n\n        let errorP = wrap.querySelector('p.error');\n        if (!errorP) {\n            errorP = document.createElement('p');\n            errorP.classList.add('error');\n            wrap.appendChild(errorP);\n        }\n\n        errorP.innerHTML = message;\n    }\n\n    /**\n     * Ensure all errors are purged from the view\n     */\n    clearErrors() {\n        let error = document.getElementById('swatch-form-error');\n        error.style.display = 'none';\n\n        var errors = this.form.querySelectorAll('p.error');\n        for (let index = 0; index < errors.length; index++) {\n            const element = errors[index];\n            element.textContent = '';\n        }\n\n        var fields = this.form.querySelectorAll('.field-error');\n        for (let index = 0; index < fields.length; index++) {\n            const element = fields[index];\n            element.classList.remove('field-error');\n        }\n    }\n\n    /**\n     * Disable the submit button\n     */\n    disableSubmit() {\n        if (this.submit) {\n            this.submit.setAttribute('disabled', true);\n        }\n    }\n\n    /**\n     * Enable the submit button\n     */\n    enableSubmit() {\n        if (this.submit) {\n            this.submit.removeAttribute('disabled');\n        }\n    }\n\n    /**\n     * Scroll to the generic error\n     */\n    scrollToMessageError() {\n        document.querySelector('.message--error').scrollIntoView({\n            behavior: 'smooth',\n        });\n    }\n\n    /**\n     * Scroll to the first field error\n     */\n    scrollToFieldError() {\n        document.querySelector('.field-error').scrollIntoView({\n            behavior: 'smooth',\n        });\n\n        if (this.options.afterErrorsFocusField) {\n            document\n                .querySelector('.field-error')\n                .querySelector('input, textarea, select')\n                .focus();\n        }\n    }\n\n    /**\n     * Add message to page header\n     *\n     * @param {string} message\n     */\n    addBannerError(message) {\n        let error = document.getElementById('swatch-form-error');\n        error.style.display = 'block';\n        error.querySelector('.message').innerHTML = message;\n    }\n\n    /**\n     * Syncs between the basket brochure state hidden input field,\n     * relies on default CSS behaviour to change checkbox state\n     * @private\n     */\n    #syncBrochureState = () => {\n        const targetInput = document.querySelector('[data-swatch-checkbox]');\n        const submitInput = document.querySelector(\n            '[data-form-brochure-optin]'\n        );\n\n        if (!targetInput || !submitInput) {\n            return;\n        }\n\n        const hasBrochureQuery =\n            new URL(window.location.href).searchParams.get('brochure') ===\n            'true';\n        const isBrochureSelected =\n            hasBrochureQuery ||\n            getBrochureOptin() ||\n            submitInput.value === 'true';\n\n        targetInput.checked = isBrochureSelected;\n        submitInput.value = isBrochureSelected ? 'true' : 'false';\n        setBrochureOptin(isBrochureSelected);\n\n        targetInput.addEventListener('change', () => {\n            submitInput.value = targetInput.checked ? 'true' : 'false';\n            setBrochureOptin(targetInput.checked);\n        });\n    };\n}\n\nwindow.Loaf.SwatchForm = window.Loaf.SwatchForm || {};\nwindow.Loaf.SwatchForm = new SwatchForm();\nwindow.Loaf.SwatchForm.init();\n"],"mappings":"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"}