import * as i0 from '@angular/core';
import { input, computed, inject, ChangeDetectionStrategy, ViewEncapsulation, Component, NgModule } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

class HtmlSanitizer {
    /** @hidden */
    constructor() {
        /** @hidden */
        this.tagWhitelist = {};
        /** @hidden */
        this.attributeWhitelist = {};
        /** @hidden */
        this._schemaWhiteList = ['http', 'https', 'ftp', 'mailto'];
        /** @hidden */
        this._uriAttributes = {
            href: true
        };
        /** @hidden */
        this._safeWrapper = {
            iframe: null,
            iframeDoc: null
        };
        this.extendTags();
    }
    /** @hidden */
    get defTagWhitelist() {
        return {
            A: true,
            ABBR: true,
            BLOCKQUOTE: true,
            BR: true,
            CITE: true,
            CODE: true,
            DL: true,
            DT: true,
            DD: true,
            EM: true,
            H1: true,
            H2: true,
            H3: true,
            H4: true,
            H5: true,
            H6: true,
            P: true,
            PRE: true,
            STRONG: true,
            SPAN: true,
            U: true,
            UL: true,
            OL: true,
            LI: true
        };
    }
    /** @hidden */
    get defAttributeWhitelist() {
        return {
            class: true,
            style: true,
            href: true,
            target: '_blank',
            download: true,
            hreflang: true,
            rel: true,
            type: true,
            title: true
        };
    }
    /** @hidden */
    extendTags(customTags) {
        this.tagWhitelist = { ...this.defTagWhitelist, ...customTags, BODY: true };
    }
    /** @hidden */
    extendAttrs(customAttrs) {
        this.attributeWhitelist = { ...this.defAttributeWhitelist, ...customAttrs };
    }
    /** @hidden */
    sanitizeHtml(input) {
        input = input.trim();
        if (input.length === 0) {
            return '';
        }
        this._safeWrapper = this._getSafeWrapper();
        if (!this._safeWrapper?.iframeDoc) {
            return '';
        }
        this._safeWrapper.iframeDoc.body.innerHTML = input;
        const resultElement = this._makeSanitizedCopy(this._safeWrapper.iframeDoc.body);
        this._removeSafeWrapper();
        return resultElement.innerHTML;
    }
    /** @hidden */
    _makeSanitizedCopy(node) {
        let newNode = node;
        if (node.nodeType === Node.TEXT_NODE) {
            newNode = node.cloneNode(true);
        }
        else if (node.nodeType === Node.ELEMENT_NODE && this.tagWhitelist[node.tagName]) {
            newNode = this._extendLinkTarget(this._implementTag(node));
        }
        else {
            newNode = document.createDocumentFragment();
        }
        return newNode;
    }
    /** @hidden */
    _implementTag(node) {
        const newNode = this._safeWrapper?.iframeDoc?.createElement(node.tagName);
        if (!newNode) {
            throw new Error('Failed to create node');
        }
        for (const key in this.attributeWhitelist) {
            if (this.attributeWhitelist[key]) {
                const allowed = this.attributeWhitelist[key];
                const origin = node.getAttribute(key);
                if (key in node || key === 'class') {
                    if (this._uriAttributes[key] && this._validateBySchema(origin)) {
                        continue;
                    }
                    switch (typeof allowed) {
                        case 'string':
                            newNode.setAttribute(key, allowed);
                            break;
                        default:
                            if (allowed && origin) {
                                newNode.setAttribute(key, origin);
                            }
                    }
                }
            }
        }
        for (const child of Array.from(node.childNodes)) {
            const subCopy = this._makeSanitizedCopy(child);
            if (subCopy) {
                newNode.appendChild(subCopy);
            }
        }
        return newNode;
    }
    /** @hidden */
    _extendLinkTarget(node) {
        if (node.tagName === 'A') {
            const hrefAttr = node.getAttribute('href');
            if (!hrefAttr || /^#/.test(hrefAttr)) {
                node.removeAttribute('target');
            }
            else {
                const defTarget = this.attributeWhitelist['target'] || '_blank';
                node.setAttribute('target', defTarget.toString());
            }
        }
        return node;
    }
    /** @hidden */
    _getSafeWrapper() {
        const iframe = document.createElement('iframe');
        if (iframe.sandbox === undefined) {
            console.warn('Your browser does not support sandboxed iframes. Please upgrade to a modern browser.');
            return null;
        }
        iframe.setAttribute('sandbox', 'allow-same-origin');
        iframe.style.setProperty('display', 'none');
        document.body.appendChild(iframe);
        const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
        if (!iframeDoc) {
            console.warn('Could not resolve document from the provided iframe');
            return null;
        }
        if (!iframeDoc?.body) {
            // Instead of using the deprecated .write method, we directly create and append a body element.
            const body = iframeDoc.createElement('body');
            iframeDoc.appendChild(body);
        }
        return { iframe, iframeDoc };
    }
    /** @hidden */
    _removeSafeWrapper() {
        if (this._safeWrapper?.iframe) {
            document.body.removeChild(this._safeWrapper.iframe);
        }
    }
    /** @hidden */
    _validateBySchema(value) {
        return !!value && value.indexOf(':') > -1 && !this._startsWithAny(value, this._schemaWhiteList);
    }
    /** @hidden */
    _startsWithAny(str, substrings) {
        return !!str && substrings.some((value) => str.indexOf(value) === 0);
    }
}

/**
 * Formatted-text component renders sanitized HTML content with configurable link behavior.
 *
 * This component accepts HTML text input and renders it after sanitization.
 * Only safe HTML tags are preserved; potentially dangerous content is stripped.
 *
 * @example
 * ```html
 * <fd-formatted-text
 *   [htmlText]="'<p>Hello <a href=\'#\'>World</a></p>'"
 *   [convertedLinksDefaultTarget]="'_blank'"
 *   [height]="'200px'">
 * </fd-formatted-text>
 * ```
 *
 * @selector fd-formatted-text
 * @exportAs fd-formatted-text
 */
class FormattedTextComponent {
    constructor() {
        /**
         * HTML text content to be formatted and rendered.
         */
        this.htmlText = input(...(ngDevMode ? [undefined, { debugName: "htmlText" }] : []));
        /**
         * Target attribute for anchor tags in the formatted content.
         * @default '_blank'
         */
        this.convertedLinksDefaultTarget = input('_blank', ...(ngDevMode ? [{ debugName: "convertedLinksDefaultTarget" }] : []));
        /**
         * Height style for component.
         */
        this.height = input(...(ngDevMode ? [undefined, { debugName: "height" }] : []));
        /**
         * Width style for component.
         */
        this.width = input(...(ngDevMode ? [undefined, { debugName: "width" }] : []));
        /**
         * Computed formatted and sanitized HTML content.
         * Automatically updates when htmlText or convertedLinksDefaultTarget changes.
         * @hidden
         */
        this.formattedText = computed(() => {
            const rawText = this.htmlText();
            // Early return for empty content
            if (!rawText || !rawText.trim()) {
                return '';
            }
            this._htmlSanitizer.extendAttrs({
                target: this.convertedLinksDefaultTarget()
            });
            const text = this._htmlSanitizer.sanitizeHtml(rawText);
            return this._domSanitizer.bypassSecurityTrustHtml(text.trim());
        }, ...(ngDevMode ? [{ debugName: "formattedText" }] : []));
        /** @hidden */
        this._htmlSanitizer = new HtmlSanitizer();
        /** @hidden */
        this._domSanitizer = inject(DomSanitizer);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FormattedTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.0", type: FormattedTextComponent, isStandalone: true, selector: "fd-formatted-text", inputs: { htmlText: { classPropertyName: "htmlText", publicName: "htmlText", isSignal: true, isRequired: false, transformFunction: null }, convertedLinksDefaultTarget: { classPropertyName: "convertedLinksDefaultTarget", publicName: "convertedLinksDefaultTarget", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.fd-formatted-text-with-height": "height()", "class.fd-formatted-text-with-width": "width()", "style.height": "height()", "style.width": "width()", "innerHTML": "formattedText()" } }, exportAs: ["fd-formatted-text"], ngImport: i0, template: ``, isInline: true, styles: ["fd-formatted-text{display:block}fd-formatted-text.fd-formatted-text-with-height{overflow-y:auto}fd-formatted-text.fd-formatted-text-with-width{overflow-x:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FormattedTextComponent, decorators: [{
            type: Component,
            args: [{ selector: 'fd-formatted-text', exportAs: 'fd-formatted-text', template: ``, host: {
                        '[class.fd-formatted-text-with-height]': 'height()',
                        '[class.fd-formatted-text-with-width]': 'width()',
                        '[style.height]': 'height()',
                        '[style.width]': 'width()',
                        '[innerHTML]': 'formattedText()'
                    }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["fd-formatted-text{display:block}fd-formatted-text.fd-formatted-text-with-height{overflow-y:auto}fd-formatted-text.fd-formatted-text-with-width{overflow-x:auto}\n"] }]
        }], propDecorators: { htmlText: [{ type: i0.Input, args: [{ isSignal: true, alias: "htmlText", required: false }] }], convertedLinksDefaultTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "convertedLinksDefaultTarget", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }] } });

/**
 * @deprecated
 * Import `FormattedTextComponent` directly as a standalone component.
 * This module will be removed in a future version.
 */
class FormattedTextModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FormattedTextModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: FormattedTextModule, imports: [FormattedTextComponent], exports: [FormattedTextComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FormattedTextModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FormattedTextModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [FormattedTextComponent],
                    imports: [FormattedTextComponent]
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { FormattedTextComponent, FormattedTextModule };
//# sourceMappingURL=fundamental-ngx-core-formatted-text.mjs.map
