"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const HTMLElement_js_1 = __importDefault(require("../html-element/HTMLElement.cjs"));
const PropertySymbol = __importStar(require("../../PropertySymbol.cjs"));
const Event_js_1 = __importDefault(require("../../event/Event.cjs"));
const SubmitEvent_js_1 = __importDefault(require("../../event/events/SubmitEvent.cjs"));
const HTMLFormControlsCollection_js_1 = __importDefault(require("./HTMLFormControlsCollection.cjs"));
const BrowserFrameNavigator_js_1 = __importDefault(require("../../browser/utilities/BrowserFrameNavigator.cjs"));
const QuerySelector_js_1 = __importDefault(require("../../query-selector/QuerySelector.cjs"));
const RadioNodeList_js_1 = __importDefault(require("./RadioNodeList.cjs"));
const WindowBrowserContext_js_1 = __importDefault(require("../../window/WindowBrowserContext.cjs"));
const ClassMethodBinder_js_1 = __importDefault(require("../../ClassMethodBinder.cjs"));
const Node_js_1 = __importDefault(require("../node/Node.cjs"));
const Element_js_1 = __importDefault(require("../element/Element.cjs"));
const EventTarget_js_1 = __importDefault(require("../../event/EventTarget.cjs"));
/**
 * HTML Form Element.
 *
 * Reference:
 * https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement.
 */
class HTMLFormElement extends HTMLElement_js_1.default {
    // Internal properties.
    [PropertySymbol.elements] = null;
    [PropertySymbol.proxy];
    // Events
    onformdata = null;
    onreset = null;
    onsubmit = null;
    /**
     * Constructor.
     */
    constructor() {
        super();
        const methodBinder = new ClassMethodBinder_js_1.default(this, [
            HTMLFormElement,
            HTMLElement_js_1.default,
            Element_js_1.default,
            Node_js_1.default,
            EventTarget_js_1.default
        ]);
        const proxy = new Proxy(this, {
            get: (target, property) => {
                if (property === 'length') {
                    return target[PropertySymbol.getFormControlItems]().length;
                }
                if (property in target || typeof property === 'symbol') {
                    methodBinder.bind(property);
                    return target[property];
                }
                const index = Number(property);
                if (!isNaN(index)) {
                    return target[PropertySymbol.getFormControlItems]()[index];
                }
                return target[PropertySymbol.getFormControlNamedItem](property) || undefined;
            },
            set(target, property, newValue) {
                methodBinder.bind(property);
                if (typeof property === 'symbol') {
                    target[property] = newValue;
                    return true;
                }
                const index = Number(property);
                if (isNaN(index)) {
                    target[property] = newValue;
                }
                return true;
            },
            deleteProperty(target, property) {
                if (typeof property === 'symbol') {
                    delete target[property];
                    return true;
                }
                const index = Number(property);
                if (isNaN(index)) {
                    delete target[property];
                }
                return true;
            },
            ownKeys(target) {
                return Object.keys(target[PropertySymbol.getFormControlItems]());
            },
            has(target, property) {
                if (property in target) {
                    return true;
                }
                if (typeof property === 'symbol') {
                    return false;
                }
                const items = target[PropertySymbol.getFormControlItems]();
                const index = Number(property);
                if (!isNaN(index) && index >= 0 && index < items.length) {
                    return true;
                }
                property = String(property);
                for (let i = 0; i < items.length; i++) {
                    const item = items[i];
                    const name = item[PropertySymbol.attributes][PropertySymbol.namedItems].get('id')?.[PropertySymbol.value] ||
                        item[PropertySymbol.attributes][PropertySymbol.namedItems].get('name')?.[PropertySymbol.value];
                    if (name && name === property) {
                        return true;
                    }
                }
                return false;
            },
            defineProperty(target, property, descriptor) {
                methodBinder.preventBinding(property);
                if (!descriptor.value) {
                    Object.defineProperty(target, property, descriptor);
                    return true;
                }
                const index = Number(descriptor.value);
                if (isNaN(index)) {
                    Object.defineProperty(target, property, descriptor);
                    return true;
                }
                return false;
            },
            getOwnPropertyDescriptor(target, property) {
                if (property in target) {
                    return Object.getOwnPropertyDescriptor(target, property);
                }
                const items = target[PropertySymbol.getFormControlItems]();
                const index = Number(property);
                if (!isNaN(index) && index >= 0 && index < items.length) {
                    return {
                        value: items[index],
                        writable: false,
                        enumerable: true,
                        configurable: true
                    };
                }
                for (let i = 0; i < items.length; i++) {
                    const item = items[i];
                    const name = item[PropertySymbol.attributes][PropertySymbol.namedItems].get('id')?.[PropertySymbol.value] ||
                        item[PropertySymbol.attributes][PropertySymbol.namedItems].get('name')?.[PropertySymbol.value];
                    if (name && name === property) {
                        return {
                            value: item,
                            writable: false,
                            enumerable: true,
                            configurable: true
                        };
                    }
                }
            }
        });
        this[PropertySymbol.proxy] = proxy;
        this[PropertySymbol.formNode] = proxy;
        return proxy;
    }
    /**
     * Returns elements.
     *
     * @returns Elements.
     */
    get elements() {
        if (!this[PropertySymbol.elements]) {
            this[PropertySymbol.elements] = new HTMLFormControlsCollection_js_1.default(PropertySymbol.illegalConstructor, this);
        }
        return this[PropertySymbol.elements];
    }
    /**
     * Returns length.
     *
     * @returns Length.
     */
    get length() {
        return this[PropertySymbol.getFormControlItems]().length;
    }
    /**
     * Returns name.
     *
     * @returns Name.
     */
    get name() {
        return this.getAttribute('name') || '';
    }
    /**
     * Sets name.
     *
     * @param name Name.
     */
    set name(name) {
        this.setAttribute('name', name);
    }
    /**
     * Returns method.
     *
     * @returns Method.
     */
    get method() {
        return this.getAttribute('method') || 'get';
    }
    /**
     * Sets method.
     *
     * @param method Method.
     */
    set method(method) {
        this.setAttribute('method', method);
    }
    /**
     * Returns target.
     *
     * @returns Target.
     */
    get target() {
        return this.getAttribute('target') || '';
    }
    /**
     * Sets target.
     *
     * @param target Target.
     */
    set target(target) {
        this.setAttribute('target', target);
    }
    /**
     * Returns action.
     *
     * @returns Action.
     */
    get action() {
        if (!this.hasAttribute('action')) {
            return this[PropertySymbol.ownerDocument].location.href;
        }
        try {
            return new URL(this.getAttribute('action'), this[PropertySymbol.ownerDocument].location.href)
                .href;
        }
        catch (e) {
            return '';
        }
    }
    /**
     * Sets action.
     *
     * @param action Action.
     */
    set action(action) {
        this.setAttribute('action', action);
    }
    /**
     * Returns encoding.
     *
     * @returns Encoding.
     */
    get encoding() {
        return this.getAttribute('encoding') || '';
    }
    /**
     * Sets encoding.
     *
     * @param encoding Encoding.
     */
    set encoding(encoding) {
        this.setAttribute('encoding', encoding);
    }
    /**
     * Returns enctype.
     *
     * @returns Enctype.
     */
    get enctype() {
        return this.getAttribute('enctype') || '';
    }
    /**
     * Sets enctype.
     *
     * @param enctype Enctype.
     */
    set enctype(enctype) {
        this.setAttribute('enctype', enctype);
    }
    /**
     * Returns autocomplete.
     *
     * @returns Autocomplete.
     */
    get autocomplete() {
        return this.getAttribute('autocomplete') || '';
    }
    /**
     * Sets autocomplete.
     *
     * @param autocomplete Autocomplete.
     */
    set autocomplete(autocomplete) {
        this.setAttribute('autocomplete', autocomplete);
    }
    /**
     * Returns accept charset.
     *
     * @returns Accept charset.
     */
    get acceptCharset() {
        return this.getAttribute('acceptcharset') || '';
    }
    /**
     * Sets accept charset.
     *
     * @param acceptCharset Accept charset.
     */
    set acceptCharset(acceptCharset) {
        this.setAttribute('acceptcharset', acceptCharset);
    }
    /**
     * Returns no validate.
     *
     * @returns No validate.
     */
    get noValidate() {
        return this.getAttribute('novalidate') !== null;
    }
    /**
     * Sets no validate.
     *
     * @param noValidate No validate.
     */
    set noValidate(noValidate) {
        if (!noValidate) {
            this.removeAttribute('novalidate');
        }
        else {
            this.setAttribute('novalidate', '');
        }
    }
    /**
     * Submits form. No submit event is raised. In particular, the form's "submit" event handler is not run.
     */
    submit() {
        this.#submit();
    }
    /**
     * Submits form, reports validity and raises submit event.
     *
     * @param [submitter] Submitter.
     */
    requestSubmit(submitter) {
        const noValidate = submitter?.formNoValidate || this.noValidate;
        if (noValidate || this.checkValidity()) {
            const event = new SubmitEvent_js_1.default('submit', {
                bubbles: true,
                cancelable: true,
                submitter: submitter || this[PropertySymbol.proxy]
            });
            this.dispatchEvent(event);
            if (!event.defaultPrevented) {
                this.#submit(submitter);
            }
        }
    }
    /**
     * Resets form.
     */
    reset() {
        for (const element of this[PropertySymbol.getFormControlItems]()) {
            switch (element[PropertySymbol.tagName]) {
                case 'TEXTAREA':
                    element[PropertySymbol.value] = null;
                    break;
                case 'INPUT':
                    element[PropertySymbol.value] = null;
                    element[PropertySymbol.checked] = null;
                    break;
                case 'OUTPUT':
                    element.textContent = element[PropertySymbol.defaultValue];
                    break;
                case 'SELECT':
                    let hasSelectedAttribute = false;
                    for (const option of element.options) {
                        if (option.hasAttribute('selected')) {
                            hasSelectedAttribute = true;
                            option.selected = true;
                            break;
                        }
                    }
                    if (!hasSelectedAttribute && element.options.length > 0) {
                        element.options[0].selected = true;
                    }
                    break;
            }
        }
        this.dispatchEvent(new Event_js_1.default('reset', { bubbles: true, cancelable: true }));
    }
    /**
     * Checks validity.
     *
     * @returns "true" if validation does'nt fail.
     */
    checkValidity() {
        const radioValidationState = {};
        let isFormValid = true;
        for (const element of this[PropertySymbol.getFormControlItems]()) {
            if (element[PropertySymbol.tagName] === 'INPUT' && element.type === 'radio' && element.name) {
                if (!radioValidationState[element.name]) {
                    radioValidationState[element.name] = true;
                    if (!element.checkValidity()) {
                        isFormValid = false;
                    }
                }
            }
            else if (!element.checkValidity()) {
                isFormValid = false;
            }
        }
        return isFormValid;
    }
    /**
     * Reports validity.
     *
     * @returns "true" if validation does'nt fail.
     */
    reportValidity() {
        return this.checkValidity();
    }
    /**
     * @override
     */
    [PropertySymbol.cloneNode](deep = false) {
        return super[PropertySymbol.cloneNode](deep);
    }
    /**
     * Returns form control items.
     *
     * @returns Form control items.
     */
    [PropertySymbol.getFormControlItems]() {
        const elements = (QuerySelector_js_1.default.querySelectorAll(this, 'input,select,textarea,button,fieldset,object,output')[PropertySymbol.items].slice());
        if (this[PropertySymbol.isConnected]) {
            const id = this[PropertySymbol.attributes][PropertySymbol.namedItems].get('id')?.[PropertySymbol.value];
            if (id) {
                for (const element of (QuerySelector_js_1.default.querySelectorAll(this[PropertySymbol.ownerDocument], `input[form="${id}"],select[form="${id}"],textarea[form="${id}"],button[form="${id}"],fieldset[form="${id}"],object[form="${id}"],output[form="${id}"]`)[PropertySymbol.items])) {
                    if (!elements.includes(element)) {
                        elements.push(element);
                    }
                }
            }
        }
        return elements;
    }
    /**
     * Returns form control named item.
     *
     * @param name
     * @returns Form control named item.
     */
    [PropertySymbol.getFormControlNamedItem](name) {
        const items = this[PropertySymbol.getFormControlItems]();
        const namedItems = [];
        name = String(name);
        for (const item of items) {
            if (item[PropertySymbol.attributes][PropertySymbol.namedItems].get('id')?.[PropertySymbol.value] === name ||
                item[PropertySymbol.attributes][PropertySymbol.namedItems].get('name')?.[PropertySymbol.value] === name) {
                namedItems.push(item);
            }
        }
        if (!namedItems.length) {
            return null;
        }
        if (namedItems.length === 1) {
            return namedItems[0];
        }
        return new RadioNodeList_js_1.default(PropertySymbol.illegalConstructor, namedItems);
    }
    /**
     * Submits form.
     *
     * @param browserFrame Browser frame. Injected by the constructor.
     * @param [submitter] Submitter.
     */
    #submit(submitter) {
        const action = submitter?.hasAttribute('formaction')
            ? submitter?.formAction || this.action
            : this.action;
        const browserFrame = new WindowBrowserContext_js_1.default(this[PropertySymbol.window]).getBrowserFrame();
        if (!browserFrame) {
            return;
        }
        if (!action) {
            // The URL is invalid when the action is empty.
            // This is what Chrome does when the URL is invalid.
            this[PropertySymbol.ownerDocument].location.hash = '#blocked';
            return;
        }
        const method = submitter?.formMethod || this.method;
        const formData = new this[PropertySymbol.window].FormData(this);
        let targetFrame;
        switch (submitter?.formTarget || this.target) {
            default:
            case '_self':
                targetFrame = browserFrame;
                break;
            case '_top':
                targetFrame = browserFrame.page.mainFrame;
                break;
            case '_parent':
                targetFrame = browserFrame.parentFrame ?? browserFrame;
                break;
            case '_blank':
                const newPage = browserFrame.page.context.newPage();
                targetFrame = newPage.mainFrame;
                targetFrame[PropertySymbol.openerFrame] = browserFrame;
                break;
        }
        if (method === 'get') {
            const url = new URL(action);
            for (const [key, value] of formData) {
                if (typeof value === 'string') {
                    url.searchParams.append(key, value);
                }
            }
            BrowserFrameNavigator_js_1.default.navigate({
                windowClass: (this[PropertySymbol.ownerDocument][PropertySymbol.defaultView].constructor),
                frame: targetFrame,
                url: url.href,
                goToOptions: {
                    referrer: browserFrame.page.mainFrame.window.location.origin
                }
            });
            return;
        }
        BrowserFrameNavigator_js_1.default.navigate({
            windowClass: (this[PropertySymbol.ownerDocument][PropertySymbol.defaultView].constructor),
            frame: targetFrame,
            method: method,
            url: action,
            formData,
            goToOptions: {
                referrer: browserFrame.page.mainFrame.window.location.origin
            }
        });
    }
}
exports.default = HTMLFormElement;
//# sourceMappingURL=HTMLFormElement.cjs.map