{"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":"iWAQA,IAAM,GAAA,EAAA,EAAA,sBAAA,EAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,EAAA,yBAAA,EAAA,EAAA,EAAA,mBAAA,EAAN,KAAiB,CACb,aAAc,CAAA,OAAA,eAAA,KAAA,EAAA,CAAA,MAAA,CAAA,CAAA,4HA4Td,UAA2B,CACvB,IAAM,EAAc,SAAS,cAAc,wBAAwB,EAC7D,EAAc,SAAS,cACzB,4BACJ,EAEA,GAAI,CAAC,GAAe,CAAC,EACjB,OAMJ,IAAM,EAFF,IAAI,IAAI,OAAO,SAAS,IAAI,CAAC,CAAC,aAAa,IAAI,UAAU,IACzD,QAGA,EAAiB,GACjB,EAAY,QAAU,OAE1B,EAAY,QAAU,EACtB,EAAY,MAAQ,EAAqB,OAAS,QAClD,EAAiB,CAAkB,EAEnC,EAAY,iBAAiB,aAAgB,CACzC,EAAY,MAAQ,EAAY,QAAU,OAAS,QACnD,EAAiB,EAAY,OAAO,CACxC,CAAC,CACL,CAAA,CAAA,EArVI,KAAK,KAAO,SAAS,cAAc,oBAAoB,EAEvD,KAAK,QAAU,CACX,kBAAmB,SAAU,EAAK,CAAC,EACnC,yBAA0B,GAC1B,sBAAuB,EAC3B,CACJ,CAEA,MAAO,CACH,EAAA,KAAA,CAAA,CAAA,CAAA,EAAA,CAAA,EACA,EAAA,KAAA,CAAA,CAAA,CAAA,EAAA,CAAA,CACJ,CAiIA,SAAS,EAAO,CACR,KAAK,KAAO,KAAK,IAAI,WAAa,GAClC,KAAK,IAAI,MAAM,EAGnB,KAAK,YAAY,EAEjB,KAAK,IAAM,IAAI,eACf,KAAK,IAAI,KAAK,OAAQ,KAAK,KAAK,aAAa,QAAQ,EAAG,EAAK,EAC7D,KAAK,IAAI,iBAAiB,mBAAoB,gBAAgB,EAC9D,KAAK,IAAI,iBACL,eACA,mCACJ,EAEA,KAAK,IAAI,WAAe,CACpB,GAAI,CACA,IAAI,EAAW,KAAK,MAAM,KAAK,IAAI,QAAQ,EAC3C,QAAQ,IAAI,CAAQ,EACf,EAAS,QAMV,aAAa,WAAW,UAAU,EAClC,EAAiB,EAAK,IANtB,EAAM,eAAe,EACrB,KAAK,eAAe,EAAS,QAAQ,OAAO,EAC5C,KAAK,UAAU,EAAS,OAAO,EAC/B,KAAK,aAAa,EAK1B,OAAS,EAAO,CACZ,EAAM,eAAe,EACrB,QAAQ,IAAI,CAAK,EACjB,KAAK,QAAQ,kBAAkB,KAAK,KAAM,KAAK,GAAG,CACtD,CACJ,EAEA,IAAI,EAAO,IAAI,SAAS,KAAK,IAAI,EAKjC,OAJA,EAAK,IAAI,SAAU,UAAU,EAE7B,KAAK,IAAI,KAAK,IAAI,gBAAgB,CAAI,CAAC,EAEhC,EACX,CAOA,UAAU,EAAQ,CACd,IAAK,IAAM,KAAS,EAAO,MAAO,CAC9B,IAAM,EAAQ,EAAO,MAAM,GAC3B,KAAK,SAAS,EAAO,CAAK,CAC9B,CAEA,IAAI,EAAiB,KAAK,QAAQ,yBAC5B,qBACA,uBACN,WAAW,KAAK,EAAe,CAAC,KAAK,IAAI,EAAG,EAAE,CAClD,CAQA,SAAS,EAAO,EAAS,CACrB,IAAI,EAAO,KAAK,KAAK,cAAc,IAAM,EAAQ,OAAO,EAExD,GAAI,CAAC,EACD,OAIA,EAAK,cAAc,UAAU,SACzB,mCACJ,GAEA,EAAK,cAAc,cAAc,UAAU,IACvC,kCACJ,EAGJ,EAAK,UAAU,IAAI,aAAa,EAEhC,IAAI,EAAS,EAAK,cAAc,SAAS,EACpC,IACD,EAAS,SAAS,cAAc,GAAG,EACnC,EAAO,UAAU,IAAI,OAAO,EAC5B,EAAK,YAAY,CAAM,GAG3B,EAAO,UAAY,CACvB,CAKA,aAAc,CACV,IAAI,EAAQ,SAAS,eAAe,mBAAmB,EACvD,EAAM,MAAM,QAAU,OAEtB,IAAI,EAAS,KAAK,KAAK,iBAAiB,SAAS,EACjD,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAO,OAAQ,IAAS,CAChD,IAAM,EAAU,EAAO,GACvB,EAAQ,YAAc,EAC1B,CAEA,IAAI,EAAS,KAAK,KAAK,iBAAiB,cAAc,EACtD,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAO,OAAQ,IAEvC,EADuB,EAChB,CAAC,UAAU,OAAO,aAAa,CAE9C,CAKA,eAAgB,CACR,KAAK,QACL,KAAK,OAAO,aAAa,WAAY,EAAI,CAEjD,CAKA,cAAe,CACP,KAAK,QACL,KAAK,OAAO,gBAAgB,UAAU,CAE9C,CAKA,sBAAuB,CACnB,SAAS,cAAc,iBAAiB,CAAC,CAAC,eAAe,CACrD,SAAU,QACd,CAAC,CACL,CAKA,oBAAqB,CACjB,SAAS,cAAc,cAAc,CAAC,CAAC,eAAe,CAClD,SAAU,QACd,CAAC,EAEG,KAAK,QAAQ,uBACb,SACK,cAAc,cAAc,CAAC,CAC7B,cAAc,yBAAyB,CAAC,CACxC,MAAM,CAEnB,CAOA,eAAe,EAAS,CACpB,IAAI,EAAQ,SAAS,eAAe,mBAAmB,EACvD,EAAM,MAAM,QAAU,QACtB,EAAM,cAAc,UAAU,CAAC,CAAC,UAAY,CAChD,CAkCJ,GAAA,SAAA,GApU4B,CAAA,IAAA,EAKpB,SAJyB,iBACrB,sCAGE,CAAC,CAAC,QAAQ,GAAU,CACtB,EAAO,iBAAiB,QAAS,GAAK,CAE9B,EAAE,OAAO,UAAU,SACf,mCACJ,EAEA,EAAE,OAAO,UAAU,OACf,mCACJ,EAEA,EAAE,OAAO,UAAU,IAAI,mCAAmC,CAElE,CAAC,CACL,CAAC,GAMD,EAAA,SAJiC,cAC7B,4CAGU,IAAC,MAAf,EAAiB,iBAAiB,QAAS,GAAA,EACvC,KAAA,CAAA,CAAA,CAAA,EAAA,CAA8B,CAAC,CACnC,EAEA,KAAK,KAAK,iBAAiB,SAAU,GAAK,CACtC,KAAK,cAAc,EACnB,KAAK,SAAS,CAAC,CACnB,CAAC,EAED,IAAI,EAAO,KAAK,IAAI,EAEpB,EAAA,KAAA,CAAA,CAAA,CAAA,EAAA,CAAA,CACJ,CAAA,SAAA,GAMkB,CAKd,GAJuB,SAAS,cAAc,oBAE7B,CAAC,EAAE,cAAc,sBAAsB,GAAK,KAGzD,OAGJ,IAAM,EAAoB,SAAS,cAC/B,yBACJ,EACM,EAAsB,SAAS,cACjC,wBACJ,EAEA,GAAI,CAAC,GAAqB,CAAC,EACvB,OAIJ,IAAM,EAAW,KAAK,MAAM,aAAa,QAAQ,UAAU,CAAC,GAAK,CAAA,EAE7D,EAAS,SAAW,GACpB,KAAK,eACD,sEACJ,EAIJ,IAAM,EAAc,GAAmB,QAAQ,aAC/C,GAAI,EACA,GAAI,CACA,IAAM,EAAW,KAAK,MAAM,CAAW,EACvC,IAAK,IAAM,KAAU,EAAU,CAC3B,IAAM,EAAU,KAAK,MAAM,EAAO,KAAK,CAAC,EAAE,OACtC,GAAW,EAAS,KACpB,EAAO,SAAW,EAAS,GAEnC,CACJ,MAAQ,CAER,CAIJ,IAAM,EAAa,EACf,EACA,CAAC,oBAAqB,uBAAuB,EAC7C,CACI,sBAAuB,EAC3B,CACJ,EAEA,GAAmB,YAAY,CAAU,EAEzC,IAAI,EAAiB,CAAA,EAErB,EAAS,QAAQ,GAAU,CACvB,EAAe,KAAK,KAAK,MAAM,EAAO,KAAK,CAAC,CAAC,MAAM,CACvD,CAAC,EAEG,IACA,EAAoB,MAAQ,EAAe,KAAK,GAAG,EAE3D,CAAA,SAAA,EAEyB,EAAG,CAAA,IAAA,EACxB,EAAE,eAAe,GAMjB,EAAA,SAJ8B,cAC1B,kCAGO,IAAC,MAAZ,EAAc,UAAU,OAAO,YAAY,CAC/C,CAgNJ,OAAO,KAAK,WAAa,OAAO,KAAK,YAAc,CAAC,EACpD,OAAO,KAAK,WAAa,IAAI,EAC7B,OAAO,KAAK,WAAW,KAAK"}