import { __esDecorate, __runInitializers } from "tslib";
import { html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
let MarkedHighlight = (() => {
    let _classSuper = LitElement;
    let _markdown_decorators;
    let _markdown_initializers = [];
    let _markdown_extraInitializers = [];
    let _breaks_decorators;
    let _breaks_initializers = [];
    let _breaks_extraInitializers = [];
    let _pedantic_decorators;
    let _pedantic_initializers = [];
    let _pedantic_extraInitializers = [];
    let _renderer_decorators;
    let _renderer_initializers = [];
    let _renderer_extraInitializers = [];
    let _sanitize_decorators;
    let _sanitize_initializers = [];
    let _sanitize_extraInitializers = [];
    let _sanitizer_decorators;
    let _sanitizer_initializers = [];
    let _sanitizer_extraInitializers = [];
    let _disableRemoteSanitization_decorators;
    let _disableRemoteSanitization_initializers = [];
    let _disableRemoteSanitization_extraInitializers = [];
    return class MarkedHighlight extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _markdown_decorators = [property({ type: String })];
            _breaks_decorators = [property({ type: Boolean, reflect: true })];
            _pedantic_decorators = [property({ type: Boolean, reflect: true })];
            _renderer_decorators = [property({ type: Object })];
            _sanitize_decorators = [property({ type: Boolean, reflect: true })];
            _sanitizer_decorators = [property({ type: Object })];
            _disableRemoteSanitization_decorators = [property({ type: Boolean, reflect: true })];
            __esDecorate(this, null, _markdown_decorators, { kind: "accessor", name: "markdown", static: false, private: false, access: { has: obj => "markdown" in obj, get: obj => obj.markdown, set: (obj, value) => { obj.markdown = value; } }, metadata: _metadata }, _markdown_initializers, _markdown_extraInitializers);
            __esDecorate(this, null, _breaks_decorators, { kind: "accessor", name: "breaks", static: false, private: false, access: { has: obj => "breaks" in obj, get: obj => obj.breaks, set: (obj, value) => { obj.breaks = value; } }, metadata: _metadata }, _breaks_initializers, _breaks_extraInitializers);
            __esDecorate(this, null, _pedantic_decorators, { kind: "accessor", name: "pedantic", static: false, private: false, access: { has: obj => "pedantic" in obj, get: obj => obj.pedantic, set: (obj, value) => { obj.pedantic = value; } }, metadata: _metadata }, _pedantic_initializers, _pedantic_extraInitializers);
            __esDecorate(this, null, _renderer_decorators, { kind: "accessor", name: "renderer", static: false, private: false, access: { has: obj => "renderer" in obj, get: obj => obj.renderer, set: (obj, value) => { obj.renderer = value; } }, metadata: _metadata }, _renderer_initializers, _renderer_extraInitializers);
            __esDecorate(this, null, _sanitize_decorators, { kind: "accessor", name: "sanitize", static: false, private: false, access: { has: obj => "sanitize" in obj, get: obj => obj.sanitize, set: (obj, value) => { obj.sanitize = value; } }, metadata: _metadata }, _sanitize_initializers, _sanitize_extraInitializers);
            __esDecorate(this, null, _sanitizer_decorators, { kind: "accessor", name: "sanitizer", static: false, private: false, access: { has: obj => "sanitizer" in obj, get: obj => obj.sanitizer, set: (obj, value) => { obj.sanitizer = value; } }, metadata: _metadata }, _sanitizer_initializers, _sanitizer_extraInitializers);
            __esDecorate(this, null, _disableRemoteSanitization_decorators, { kind: "accessor", name: "disableRemoteSanitization", static: false, private: false, access: { has: obj => "disableRemoteSanitization" in obj, get: obj => obj.disableRemoteSanitization, set: (obj, value) => { obj.disableRemoteSanitization = value; } }, metadata: _metadata }, _disableRemoteSanitization_initializers, _disableRemoteSanitization_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #markdown_accessor_storage = __runInitializers(this, _markdown_initializers, void 0);
        /**
         * The markdown source to be rendered by this element.
         * @attribute
         */
        get markdown() { return this.#markdown_accessor_storage; }
        set markdown(value) { this.#markdown_accessor_storage = value; }
        #breaks_accessor_storage = (__runInitializers(this, _markdown_extraInitializers), __runInitializers(this, _breaks_initializers, void 0));
        /**
         * Enable GFM line breaks (regular newlines instead of two spaces for
         * breaks)
         * @attribute
         */
        get breaks() { return this.#breaks_accessor_storage; }
        set breaks(value) { this.#breaks_accessor_storage = value; }
        #pedantic_accessor_storage = (__runInitializers(this, _breaks_extraInitializers), __runInitializers(this, _pedantic_initializers, void 0));
        /**
         * Conform to obscure parts of markdown.pl as much as possible. Don't fix
         * any of the original markdown bugs or poor behavior.
         * @attribute
         */
        get pedantic() { return this.#pedantic_accessor_storage; }
        set pedantic(value) { this.#pedantic_accessor_storage = value; }
        #renderer_accessor_storage = (__runInitializers(this, _pedantic_extraInitializers), __runInitializers(this, _renderer_initializers, void 0));
        /**
         * Function used to customize a renderer based on the [API specified in the
         * Marked
         * library](https://github.com/chjj/marked#overriding-renderer-methods).
         * It takes one argument: a marked renderer object, which is mutated by the
         * function.
         */
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        get renderer() { return this.#renderer_accessor_storage; }
        set renderer(value) { this.#renderer_accessor_storage = value; }
        #sanitize_accessor_storage = (__runInitializers(this, _renderer_extraInitializers), __runInitializers(this, _sanitize_initializers, void 0));
        /**
         * Sanitize the output. Ignore any HTML that has been input.
         * @attribute
         */
        get sanitize() { return this.#sanitize_accessor_storage; }
        set sanitize(value) { this.#sanitize_accessor_storage = value; }
        #sanitizer_accessor_storage = (__runInitializers(this, _sanitize_extraInitializers), __runInitializers(this, _sanitizer_initializers, void 0));
        /**
         * Function used to customize a sanitize behavior.
         * It takes one argument: element String without text Contents.
         *
         * e.g. `<div>` `<a href="/">` `</p>'.
         * Note: To enable this function, must set `sanitize` to true.
         * WARNING: If you are using this option to un-trusted text, you must to
         * prevent XSS Attacks.
         */
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        get sanitizer() { return this.#sanitizer_accessor_storage; }
        set sanitizer(value) { this.#sanitizer_accessor_storage = value; }
        #disableRemoteSanitization_accessor_storage = (__runInitializers(this, _sanitizer_extraInitializers), __runInitializers(this, _disableRemoteSanitization_initializers, void 0));
        /**
         * If true, disables the default sanitization of any markdown received by
         * a request and allows fetched un-sanitized markdown
         *
         * e.g. fetching markdown via `src` that has HTML.
         * Note: this value overrides `sanitize` if a request is made.
         * @attribute
         */
        get disableRemoteSanitization() { return this.#disableRemoteSanitization_accessor_storage; }
        set disableRemoteSanitization(value) { this.#disableRemoteSanitization_accessor_storage = value; }
        /**
         * @returns A reference to the output element.
         */
        get outputElement() {
            const slot = this.shadowRoot?.querySelector('slot[name="markdown-html"]');
            if (!slot) {
                return null;
            }
            const nodes = slot.assignedNodes();
            const child = nodes.find((node) => node.nodeType === 1 && node.getAttribute('slot') === 'markdown-html');
            return child || this.shadowRoot?.querySelector('#content');
        }
        willUpdate(cp) {
            super.willUpdate(cp);
            if (cp.has('markdown') ||
                cp.has('sanitizer') ||
                cp.has('sanitize') ||
                cp.has('renderer') ||
                cp.has('pedantic') ||
                cp.has('breaks')) {
                this.renderMarkdown();
            }
        }
        markdownElement = __runInitializers(this, _disableRemoteSanitization_extraInitializers);
        attachedValue = false;
        firstUpdated() {
            if (this.markdown) {
                this.renderMarkdown();
                return;
            }
        }
        connectedCallback() {
            super.connectedCallback();
            this.attachedValue = true;
            this.processScript();
            this.renderMarkdown();
        }
        disconnectedCallback() {
            super.disconnectedCallback();
            this.attachedValue = false;
        }
        async processScript() {
            // Use the Markdown from the first `<script>` descendant whose MIME type
            // starts with "text/markdown". Script elements beyond the first are
            // ignored.
            const script = this.querySelector('[type="text/markdown"]');
            if (!script) {
                return;
            }
            this.markdownElement = script;
            if (script.src) {
                this.requestMarkdown(script.src);
            }
            const content = script.textContent;
            if (content && content.trim() !== '') {
                this.markdown = this.unindent(content);
            }
            const observer = new MutationObserver(this.scriptAttributeHandler.bind(this));
            observer.observe(script, { attributes: true });
        }
        /**
         * Renders `markdown` into this element's DOM.
         *
         * This is automatically called whenever the `markdown` property is changed.
         *
         * The only case where you should be calling this is if you are providing
         * markdown via `<script type="text/markdown">` after this element has been
         * constructed (or updating that markdown).
         * @event markedrendercomplete
         */
        renderMarkdown() {
            if (!this.attachedValue) {
                return;
            }
            const node = this.outputElement;
            if (!node) {
                return;
            }
            if (!this.markdown) {
                node.innerHTML = '';
                return;
            }
            const renderer = new marked.Renderer();
            if (this.renderer) {
                this.renderer(renderer);
            }
            const data = this.markdown;
            const opts = {
                renderer,
                breaks: this.breaks,
                pedantic: this.pedantic,
                gfm: true,
            };
            // eslint-disable-next-line no-misleading-character-class
            let out = marked(data.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, ''), opts);
            if (this.sanitize) {
                if (this.sanitizer) {
                    out = this.sanitizer(out);
                }
                else {
                    const result = DOMPurify.sanitize(out);
                    if (typeof result === 'string') {
                        out = result;
                    }
                    else {
                        // @ts-expect-error Sometimes the result is an object!
                        out = result.toString();
                    }
                }
            }
            node.innerHTML = out;
            this.dispatchEvent(new Event('markedrendercomplete'));
        }
        /**
         * Un-indents the markdown source that will be rendered.
         */
        unindent(text) {
            if (!text) {
                return '';
            }
            const lines = text.replace(/\t/g, '  ').split('\n');
            const indent = lines.reduce((prev, line) => {
                if (/^\s*$/.test(line)) {
                    return prev; // Completely ignore blank lines.
                }
                const match = line.match(/^(\s*)/);
                if (!match) {
                    return prev;
                }
                const lineIndent = match[0].length;
                if (prev === null) {
                    return lineIndent;
                }
                return lineIndent < prev ? lineIndent : prev;
            }, null);
            if (indent === null) {
                return text;
            }
            return lines.map((l) => l.substring(indent)).join('\n');
        }
        /**
         * Fired when the XHR finishes loading
         */
        async requestMarkdown(url) {
            try {
                const response = await fetch(url, {
                    headers: { accept: 'text/markdown' },
                });
                const { status } = response;
                if (status === 0 || (status >= 200 && status < 300)) {
                    this.sanitize = !this.disableRemoteSanitization;
                    this.markdown = await response.text();
                    this.dispatchEvent(new Event('markedloaded'));
                }
                else {
                    throw new Error('Unable to download the data');
                }
            }
            catch (e) {
                this.handleError(e);
            }
        }
        /**
         * Fired when an error is received while fetching remote markdown content.
         */
        handleError(e) {
            const evt = new CustomEvent('markedloaderror', {
                composed: true,
                bubbles: true,
                cancelable: true,
                detail: e,
            });
            this.dispatchEvent(evt);
            if (!evt.defaultPrevented) {
                this.markdown = 'Failed loading markdown source';
            }
        }
        scriptAttributeHandler(mutation) {
            if (mutation[0].attributeName !== 'src') {
                return;
            }
            const elm = this.markdownElement;
            if (elm) {
                this.requestMarkdown(elm.src);
            }
        }
        render() {
            return html `<slot name="markdown-html"><div id="content"></div></slot>`;
        }
    };
})();
// import { PrismHighlighter } from './PrismHighlighter.js';
/**
Element wrapper for the [marked](https://github.com/chjj/marked) library.

`<marked-highlight>` accepts Markdown source and renders it to a child
element with the class `markdown-html`. This child element can be styled
as you would a normal DOM element. If you do not provide a child element
with the `markdown-html` class, the Markdown source will still be rendered,
but to a shadow DOM child that cannot be styled.

### Markdown Content

The Markdown source can be specified several ways:

#### Use the `markdown` attribute to bind markdown

```html
<marked-highlight markdown="`Markdown` is _awesome_!">
  <div slot="markdown-html"></div>
</marked-highlight>
```
#### Use `<script type="text/markdown">` element child to inline markdown

```html
<marked-highlight>
  <div slot="markdown-html"></div>
  <script type="text/markdown">
    Check out my markdown!
    We can even embed elements without fear of the HTML parser mucking up their
    textual representation:
  </script>
</marked-highlight>
```
#### Use `<script type="text/markdown" src="URL">` element child to specify remote markdown

```html
<marked-highlight>
  <div slot="markdown-html"></div>
  <script type="text/markdown" src="../guidelines.md"></script>
</marked-highlight>
```

Note that the `<script type="text/markdown">` approach is *static*. Changes to
the script content will *not* update the rendered markdown!

Though, you can data bind to the `src` attribute to change the markdown.

```html
<marked-highlight>
  <div slot="markdown-html"></div>
  <script type="text/markdown" src$="[[source]]"></script>
</marked-highlight>
<script>
  ...
  this.source = '../guidelines.md';
</script>
```

### Styling

If you are using a child with the `markdown-html` class, you can style it
as you would a regular DOM element:

```css
[slot="markdown-html"] p {
  color: red;
}
[slot="markdown-html"] td:first-child {
  padding-left: 24px;
}
```

@fires markedrendercomplete - Event dispatched when the element renders the output.
@fires markedloaded - Event dispatched when the element loads the script from the URL.
@fires markedloaderror - An event dispatched when an error ocurred when downloading
                         the script content. The detail has the original error.
 */
export default MarkedHighlight;
//# sourceMappingURL=MarkedHighlight.js.map