import * as i0 from '@angular/core';
import { EventEmitter, Output, Input, Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import * as i1 from '@ckeditor/ckeditor5-angular';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
import { Plugin, ButtonView, ClassicEditor, Essentials, Paragraph, Bold, Italic, Underline, FontSize, FontColor, FontBackgroundColor, Alignment, Widget, toWidget, GeneralHtmlSupport, FontFamily, Heading, List, Table, TableToolbar, TableProperties, TableCellProperties, TableColumnResize, PasteFromOffice, PageBreak, Undo, Subscript, Superscript, Image, ImageUpload, ImageToolbar, ImageCaption, ImageResize, ImageStyle, ImageBlock, Indent, IndentBlock } from 'ckeditor5';
import { Subscription, Subject, throttleTime } from 'rxjs';
import { SdResolveMaybeAsync, hslToHex, rgbToHex, SdUtilities } from '@sd-angular/core/utilities';
import { v4 } from 'uuid';

class PageNumberPlugin extends Plugin {
    init() {
        const editor = this.editor;
        // 1. Đăng ký nút "Số trang hiện tại"
        editor.ui.componentFactory.add('pageNumber', locale => {
            const view = new ButtonView(locale);
            view.set({
                label: 'Số trang hiện tại',
                icon: '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M16 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H4V4h12v12zM6 6h2v2H6zm0 4h2v2H6zm0 4h2v2H6zm4-8h4v2h-4zm0 4h4v2h-4zm0 4h4v2h-4z"/></svg>', // Icon đơn giản
                tooltip: true
            });
            // Khi bấm nút -> Chèn HTML đánh dấu
            view.on('execute', () => {
                editor.model.change(writer => {
                    const viewFragment = editor.data.processor.toView(
                    // Đây là HTML chuẩn để Word hiểu là số trang (dùng cho cách xuất MHTML/.doc)
                    // Hoặc là marker để Backend replace
                    '<span class="page-number-marker" style="mso-field-code: PAGE; background: #eee; border: 1px dashed #999; padding: 0 4px;">[PAGE]</span> ');
                    const modelFragment = editor.data.toModel(viewFragment);
                    editor.model.insertContent(modelFragment);
                });
            });
            return view;
        });
        // 2. Đăng ký nút "Tổng số trang"
        editor.ui.componentFactory.add('totalPages', locale => {
            const view = new ButtonView(locale);
            view.set({
                label: 'Tổng số trang',
                icon: '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>',
                tooltip: true
            });
            view.on('execute', () => {
                editor.model.change(writer => {
                    const viewFragment = editor.data.processor.toView('<span class="total-page-marker" style="mso-field-code: NUMPAGES; background: #eee; border: 1px dashed #999; padding: 0 4px;">[TOTAL]</span> ');
                    const modelFragment = editor.data.toModel(viewFragment);
                    editor.model.insertContent(modelFragment);
                });
            });
            return view;
        });
    }
}

class SdHeaderFooterBuilder {
    #editor;
    Editor = ClassicEditor;
    #model;
    set model(value) {
        if (this.#model !== value) {
            this.#model = value;
        }
    }
    modelChange = new EventEmitter();
    config = {
        licenseKey: 'GPL', // Hoặc key thương mại nếu có
        // 1. PLUGIN RÚT GỌN (Bỏ Table, List, PageBreak...)
        plugins: [
            Essentials,
            Paragraph,
            Bold,
            Italic,
            Underline,
            FontSize,
            FontColor,
            FontBackgroundColor,
            Alignment,
            PageNumberPlugin, // <--- Plugin số trang
        ],
        // 2. TOOLBAR ĐƠN GIẢN
        toolbar: {
            items: [
                'undo',
                'redo',
                '|',
                'fontSize',
                'fontColor',
                '|',
                'bold',
                'italic',
                'underline',
                '|',
                'alignment', // Căn trái/phải/giữa (Quan trọng cho Header/Footer)
                '|',
                'pageNumber',
                'totalPages', // <--- 2 nút mới
            ],
            shouldNotGroupWhenFull: true,
        },
        // Cấu hình alignment
        alignment: {
            options: ['left', 'center', 'right'],
        },
    };
    onReady(editor) {
        this.#editor = editor;
        this.#editor.setData(this.#model || '');
        this.#editor.model.document.on('change:data', () => {
            this.#model = this.#editor.getData();
            this.modelChange.next(this.#model);
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdHeaderFooterBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdHeaderFooterBuilder, isStandalone: true, selector: "sd-header-footer-builder", inputs: { model: "model" }, outputs: { modelChange: "modelChange" }, ngImport: i0, template: `
    <div class="header-footer-editor-wrapper">
      <ckeditor [editor]="Editor" [config]="config" (ready)="onReady($event)"> </ckeditor>
    </div>
  `, isInline: true, styles: [":host ::ng-deep .header-footer-editor-wrapper .ck-editor__editable_inline{min-height:100px;max-height:200px;padding:10px 20px;border:1px solid #ddd}:host ::ng-deep .page-number-marker,:host ::ng-deep .total-page-marker{font-size:11px;color:#555;cursor:default;-webkit-user-select:none;user-select:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableWatchdog", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdHeaderFooterBuilder, decorators: [{
            type: Component,
            args: [{ selector: 'sd-header-footer-builder', standalone: true, imports: [CommonModule, CKEditorModule], template: `
    <div class="header-footer-editor-wrapper">
      <ckeditor [editor]="Editor" [config]="config" (ready)="onReady($event)"> </ckeditor>
    </div>
  `, styles: [":host ::ng-deep .header-footer-editor-wrapper .ck-editor__editable_inline{min-height:100px;max-height:200px;padding:10px 20px;border:1px solid #ddd}:host ::ng-deep .page-number-marker,:host ::ng-deep .total-page-marker{font-size:11px;color:#555;cursor:default;-webkit-user-select:none;user-select:none}\n"] }]
        }], propDecorators: { model: [{
                type: Input
            }], modelChange: [{
                type: Output
            }] } });

class HeadingPlugin extends Plugin {
    init() {
        const editor = this.editor;
        editor.conversion.for('editingDowncast').markerToHighlight({
            model: 'highlightMarker',
            view: {
                classes: 'ck-heading-highlight',
            },
        });
    }
}

class CommentPlugin extends Plugin {
    init() {
        const editor = this.editor;
        // 1. MODEL MARKER -> VIEW CSS
        editor.conversion.for('downcast').markerToHighlight({
            model: 'comment',
            view: data => {
                return {
                    classes: 'ck-comment-marker',
                    attributes: {
                        'data-comment-id': data.markerName,
                    },
                };
            },
        });
        // 2. ĐĂNG KÝ UI COMPONENT: 'addCommentBtn'
        editor.ui.componentFactory.add('addCommentBtn', locale => {
            const view = new ButtonView(locale);
            // Lấy config từ Angular
            const config = editor.config;
            const getOption = config.get('getOption');
            const option = getOption?.();
            // Ẩn button nếu không có onAddComment
            if (!option?.onAddComment) {
                view.set({
                    label: '',
                    isVisible: false,
                });
                return view;
            }
            view.set({
                label: 'Thêm bình luận',
                icon: '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M18 13v6l-4-4H4a2 2 0 01-2-2V4a2 2 0 012-2h14a2 2 0 012 2v9zM5 7h10v2H5V7zm0 4h10v2H5v-2z"/></svg>',
                tooltip: true,
                isEnabled: false,
            });
            // Logic Enable/Disable: Dựa theo Selection
            const selection = editor.model.document.selection;
            // Lắng nghe sự kiện change selection để bật/tắt nút
            this.listenTo(selection, 'change', () => {
                // Enable khi có bôi đen text (không phải collapsed)
                view.isEnabled = !selection.isCollapsed;
            });
            // Logic Execute: Khi bấm nút
            this.listenTo(view, 'execute', () => {
                const range = selection.getFirstRange();
                if (!range || !option?.onAddComment)
                    return;
                // Helper lấy text thuần từ range
                let selectedText = '';
                for (const item of range.getItems()) {
                    if (item.is('$text') || item.is('$textProxy')) {
                        selectedText += item.data;
                    }
                }
                // BẮN EVENT RA NGOÀI - KHÔNG TỰ ADD MARKER
                // Angular component sẽ xử lý logic (mở modal, validation, etc.)
                // và gọi lại hàm addComment() nếu cần
                option.onAddComment({ range, selectedText });
            });
            return view;
        });
        // 3. Xử lý sự kiện Copy (Clipboard Output)
        this.listenTo(editor.editing.view.document, 'clipboardOutput', (_, data) => {
            const isCopyOrCut = data.method === 'copy' || data.method === 'cut';
            // Nếu không phải hành động copy hoặc cut thì thoát hàm
            if (!isCopyOrCut) {
                return;
            }
            const content = data.content;
            editor.editing.view.change(writer => {
                // Tạo range bao quanh toàn bộ nội dung clipboard
                const range = writer.createRangeIn(content);
                // Mảng chứa các item cần xử lý
                const itemsToClean = [];
                // 1. Duyệt qua để tìm các thẻ có class ck-comment-marker
                for (const item of range.getItems()) {
                    if (item.is('element') && item.hasClass('ck-comment-marker')) {
                        itemsToClean.push(item);
                    }
                }
                // 2. Thực hiện xóa Class và Attribute
                for (const item of itemsToClean) {
                    // Xóa class 'ck-comment-marker'
                    writer.removeClass('ck-comment-marker', item);
                    // Xóa thuộc tính 'data-comment-id'
                    writer.removeAttribute('data-comment-id', item);
                }
            });
        });
    }
}

class VariablePlugin extends Plugin {
    // Khai báo dependencies nếu cần (VD: Widget)
    static get requires() {
        return [Widget];
    }
    init() {
        const editor = this.editor;
        const schema = editor.model.schema;
        const conversion = editor.conversion;
        const editingView = editor.editing.view;
        // 1. Định nghĩa Schema (Model)
        schema.register('variable', {
            inheritAllFrom: '$inlineObject',
            allowWhere: '$text',
            isInline: true,
            isObject: true,
            allowAttributes: ['id', 'uuid', 'value', 'display'],
        });
        // 2. Model -> HTML
        conversion.for('downcast').elementToElement({
            model: 'variable',
            view: (modelItem, { writer: viewWriter }) => {
                const id = modelItem.getAttribute('id');
                const uuid = modelItem.getAttribute('uuid');
                const display = modelItem.getAttribute('display');
                const value = modelItem.getAttribute('value');
                const widgetElement = viewWriter.createContainerElement('span', {
                    class: 'variable-widget',
                    'data-id': id,
                    'data-uuid': uuid,
                    'data-value': value,
                    'data-display': display,
                });
                const innerText = viewWriter.createText(`{{${display}}}`);
                viewWriter.insert(viewWriter.createPositionAt(widgetElement, 0), innerText);
                return toWidget(widgetElement, viewWriter);
            },
        });
        // 3. HTML -> Model
        conversion.for('upcast').elementToElement({
            // NOTE: Khi bổ sung thêm attribute vào element variable, dev nên bổ sung thêm "[atribute]: true" vào view
            // Để:
            // - Nếu lọc chính xác sẽ không sinh ra thẻ span thừa bọc ngoài
            // - Nếu chưa bổ sung thì sẽ sinh ra thẻ <span> bọc ngoài kèm [atribute] chưa lọc
            view: {
                name: 'span',
                classes: 'variable-widget ck-widget',
                attributes: {
                    'data-id': true,
                    'data-uuid': true,
                    'data-value': true,
                    'data-display': true,
                    contenteditable: true,
                },
            },
            model: (viewElement, { writer: modelWriter }) => {
                return modelWriter.createElement('variable', {
                    id: viewElement.getAttribute('data-id'),
                    uuid: viewElement.getAttribute('data-uuid'),
                    value: viewElement.getAttribute('data-value'),
                    display: viewElement.getAttribute('data-display'),
                });
            },
        });
        // 4. Xử lý sự kiện Drop
        this.listenTo(editingView.document, 'drop', async (evt, data) => {
            const dataTransfer = data.dataTransfer;
            const jsonData = dataTransfer.getData('ck-variable');
            if (!jsonData)
                return;
            // data.dropRange là vị trí con chuột trên View khi thả
            const viewRange = data.dropRange;
            const modelRange = editor.editing.mapper.toModelRange(viewRange);
            evt.stop();
            try {
                let variable = JSON.parse(jsonData);
                const config = editor.config;
                const getOption = config.get('getOption');
                const option = getOption?.();
                if (option?.onDropVariable) {
                    const result = await SdResolveMaybeAsync(option.onDropVariable(variable, 0));
                    // * Hỗ trợ dữ liệu có sẵn sẽ chỉ cần nhận vào boolean có cho phép thả hay không?
                    if (typeof result === 'boolean') {
                        if (!result) {
                            throw new Error('Không cho phép thêm variable vào văn bản');
                        }
                    }
                    else {
                        // * Hỗ trợ dữ liệu lấy từ API (Kiểm tra xem result có đúng định dạng interface SdDocumentBuilderVariable hay không?)
                        if (this.#isSdDocumentBuilderVariableResult(result)) {
                            variable = result;
                        }
                        else {
                            throw new Error('Dữ liệu variable không hợp lệ');
                        }
                    }
                }
                editor.model.change(writer => {
                    // 4.1. Chèn biến
                    const variableElem = writer.createElement('variable', {
                        id: variable.id,
                        uuid: v4(),
                        value: variable.value,
                        display: variable.display,
                    });
                    editor.model.insertContent(variableElem, modelRange);
                    // 4.2. Đặt con trỏ ra sau biến
                    writer.setSelection(variableElem, 'after');
                });
            }
            catch (e) {
                // Đặt con trỏ ngay tại vị trí lỗi
                if (modelRange) {
                    editor.model.change(writer => {
                        writer.setSelection(modelRange);
                    });
                }
                console.error(e);
            }
            finally {
                // 5. Dọn dẹp drop-target dù thành công hay lỗi
                editor.model.change(writer => {
                    for (const marker of editor.model.markers) {
                        if (marker.name.startsWith('drop-target')) {
                            writer.removeMarker(marker);
                        }
                    }
                });
            }
        });
        // 5. Lắng nghe sự kiện bàn phím
        let isNavigating = false;
        this.editor.editing.view.document.on('keydown', (evt, data) => {
            // Mã phím mũi tên: 37 (Left), 38 (Up), 39 (Right), 40 (Down)
            const isArrowKey = data.keyCode >= 37 && data.keyCode <= 40;
            if (isArrowKey) {
                isNavigating = true;
            }
            else {
                isNavigating = false;
            }
        }, { priority: 'high' });
        // 6. Lắng nghe sự kiện Click chuột
        this.editor.editing.view.document.on('mousedown', () => {
            isNavigating = true;
        });
        this.listenTo(editor.model.document.selection, 'change:range', () => {
            // Nếu không phải là hành động click hoặc mũi tên thì thoát hàm.
            if (!isNavigating) {
                return;
            }
            const model = editor.model;
            const selection = model.document.selection;
            if (!selection.isCollapsed)
                return;
            const position = selection.getFirstPosition();
            const nodeBefore = position?.nodeBefore;
            if (!position)
                return;
            // Kiểm tra: Node đứng trước con trỏ là variable
            if (nodeBefore && nodeBefore.is('element', 'variable')) {
                // Lấy node ngay sau variable để kiểm tra
                const nextNode = nodeBefore.nextSibling;
                // Logic: Nếu phía sau KHÔNG CÓ GÌ hoặc KHÔNG PHẢI LÀ TEXT
                if (!nextNode || !nextNode.is('$text')) {
                    model.change(writer => {
                        // Chèn thêm con trỏ variable
                        writer.insertText('\u00A0', nodeBefore, 'after');
                        // Lấy vị trí ngay sau variable (lúc này đang là đầu của text node mới)
                        const posAfterVariable = writer.createPositionAfter(nodeBefore);
                        // Dịch chuyển vị trí đó sang phải 1 đơn vị (bỏ qua ký tự vừa thêm)
                        const targetPos = posAfterVariable.getShiftedBy(1);
                        // 3. Đặt con trỏ vào vị trí đã tính toán
                        writer.setSelection(targetPos);
                    });
                }
            }
        });
        // 7. Handle xóa variable
        this.editor.editing.view.document.on('keydown', (evt, data) => {
            // Mã phím 8 là Backspace, 46 là Delete
            const btnBackspace = data.keyCode === 8;
            const btnDelete = data.keyCode === 46;
            if (btnBackspace || btnDelete) {
                const selection = editor.model.document.selection;
                const model = editor.model;
                // CASE 1: Nếu con trỏ đang nhấp nháy (Collapsed)
                if (selection.isCollapsed) {
                    const position = selection.getFirstPosition();
                    // Với Backspace ta kiểm tra nodeBefore, với Delete ta kiểm tra nodeAfter
                    const targetNode = data.keyCode === 8 ? position?.nodeBefore : position?.nodeAfter;
                    if (targetNode && targetNode.is('element', 'variable')) {
                        data.preventDefault();
                        evt.stop();
                        model.change(writer => {
                            // Chọn bao quanh Variable đó
                            writer.setSelection(targetNode, 'on');
                        });
                        return;
                    }
                }
                // CASE 2: Nếu đang có một vùng chọn (đã được highlight từ lần bấm trước)
                else {
                    const selectedElement = selection.getSelectedElement();
                    // Nếu phần tử đang được chọn chính là variable
                    if (selectedElement && selectedElement.is('element', 'variable')) {
                        // Cho phép hành động mặc định diễn ra (CKEditor sẽ tự xóa phần tử đang được chọn)
                        // Hoặc chủ động xóa để chắc chắn:
                        data.preventDefault();
                        evt.stop();
                        model.change(writer => {
                            writer.remove(selectedElement);
                        });
                    }
                }
            }
        }, { priority: 'highest' });
        // 8. Xử lý sự kiện Copy (Clipboard Output)
        // Chỉ set thêm text/plain fallback, không thay đổi HTML content
        this.listenTo(editor.editing.view.document, 'clipboardOutput', (evt, data) => {
            const isCopyOrCut = data.method === 'copy' || data.method === 'cut';
            if (!isCopyOrCut)
                return;
            // Set thêm plain text fallback cho external apps
            const dataTransfer = data.dataTransfer;
            const content = data.content;
            // Lấy tất cả text từ content (bao gồm cả variable dạng {{text}})
            let plainText = '';
            const viewRange = editor.editing.view.createRangeIn(content);
            for (const item of viewRange.getItems()) {
                if (item.is('$text') || item.is('element', 'span')) {
                    const itemAny = item;
                    if (item.is('$text') && itemAny.data) {
                        plainText += itemAny.data;
                    }
                    else if (item.is('element', 'span') && item.hasClass('variable-widget')) {
                        const display = item.getAttribute('data-display');
                        if (display)
                            plainText += `{{${display}}}`;
                    }
                }
            }
            if (plainText) {
                dataTransfer.setData('text/plain', plainText);
            }
            // HTML content giữ nguyên - CKEditor sẽ tự xử lý
        }, { priority: 'low' });
        // 9. Xử lý sự kiện Paste (Clipboard Input)
        // Nếu paste từ external source (chỉ có text, không có HTML variable)
        // thì chuyển {{text}} thành variable widget
        this.listenTo(editor.editing.view.document, 'clipboardInput', (evt, data) => {
            const dataTransfer = data.dataTransfer;
            // Nếu có HTML chứa variable-widget thì để CKEditor xử lý (upcast converter)
            const html = dataTransfer.getData('text/html');
            if (html && html.includes('variable-widget')) {
                return; // Để CKEditor upcast converter xử lý
            }
            // Chỉ xử lý nếu chỉ có plain text với pattern {{text}}
            let text = dataTransfer.getData('text/plain');
            if (!text)
                return;
            // Kiểm tra có chứa pattern {{text}} không (không cần id, value)
            const variablePattern = /\{\{([^}]+)\}\}/g;
            if (!variablePattern.test(text)) {
                return;
            }
            // Reset lastIndex sau khi test
            variablePattern.lastIndex = 0;
            evt.stop();
            editor.model.change(writer => {
                const selection = editor.model.document.selection;
                const position = selection.getFirstPosition();
                if (!position)
                    return;
                // Tách text thành các phần: normal text và variables
                let lastIndex = 0;
                let match;
                const fragments = [];
                while ((match = variablePattern.exec(text)) !== null) {
                    // Thêm text trước variable
                    if (match.index > lastIndex) {
                        fragments.push({
                            type: 'text',
                            content: text.slice(lastIndex, match.index),
                        });
                    }
                    // Thêm variable
                    const display = match[1];
                    fragments.push({
                        type: 'variable',
                        content: match[0],
                        display,
                    });
                    lastIndex = match.index + match[0].length;
                }
                // Thêm text còn lại sau variable cuối cùng
                if (lastIndex < text.length) {
                    fragments.push({
                        type: 'text',
                        content: text.slice(lastIndex),
                    });
                }
                // Chèn từng fragment vào document
                let currentPosition = position;
                for (const fragment of fragments) {
                    if (fragment.type === 'text' && fragment.content) {
                        const textNode = writer.createText(fragment.content);
                        writer.insert(textNode, currentPosition);
                        currentPosition = writer.createPositionAfter(textNode);
                    }
                    else if (fragment.type === 'variable' && fragment.display) {
                        const variableElem = writer.createElement('variable', {
                            id: v4(),
                            uuid: v4(),
                            value: fragment.display,
                            display: fragment.display,
                        });
                        writer.insert(variableElem, currentPosition);
                        currentPosition = writer.createPositionAfter(variableElem);
                    }
                }
                // Đặt con trỏ sau nội dung vừa paste
                writer.setSelection(currentPosition);
            });
        });
    }
    #isSdDocumentBuilderVariableResult = (obj) => {
        return (obj !== null &&
            obj !== undefined &&
            typeof obj === 'object' &&
            !Array.isArray(obj) &&
            typeof obj.id === 'string' &&
            typeof obj.value === 'string' &&
            typeof obj.display === 'string');
    };
}

class TableFitPlugin extends Plugin {
    init() {
        const editor = this.editor;
        // Can thiệp vào quá trình convert từ View (HTML Paste) sang Model
        editor.conversion.for('upcast').add(dispatcher => {
            dispatcher.on('element:table', (evt, data) => {
                if (!data.modelRange)
                    return;
                for (const item of data.modelRange.getItems()) {
                    if (item.is('element', 'table')) {
                        editor.model.change(writer => {
                            this._applyTableDefaults(writer, item);
                            this._applyCellBorders(writer, item);
                        });
                    }
                }
            }, { priority: 'low' });
        });
        const findInnerTable = (viewElement) => {
            if (!viewElement)
                return null;
            if (viewElement.name === 'table')
                return viewElement;
            for (const child of viewElement.getChildren()) {
                if (child.name === 'table')
                    return child;
                const found = findInnerTable(child);
                if (found)
                    return found;
            }
            return null;
        };
        editor.conversion.for('downcast').add(dispatcher => {
            dispatcher.on('attribute:tableWidth:table', (evt, data, conversionApi) => {
                const viewWriter = conversionApi.writer;
                const viewElement = conversionApi.mapper.toViewElement(data.item);
                if (!viewElement)
                    return;
                const innerTable = findInnerTable(viewElement);
                if (!innerTable)
                    return;
                viewWriter.setStyle('border-collapse', 'collapse', innerTable);
                viewWriter.setStyle('margin', '0', innerTable);
                viewWriter.setStyle('width', '100%', innerTable);
                viewWriter.setStyle('width', '100%', viewElement);
            });
            dispatcher.on('insert:table', (evt, data, conversionApi) => {
                const viewWriter = conversionApi.writer;
                const viewElement = conversionApi.mapper.toViewElement(data.item);
                if (!viewElement)
                    return;
                const innerTable = findInnerTable(viewElement);
                if (!innerTable)
                    return;
                viewWriter.setStyle('border-collapse', 'collapse', innerTable);
                viewWriter.setStyle('margin', '0', innerTable);
                viewWriter.setStyle('width', '100%', innerTable);
                viewWriter.setStyle('width', '100%', viewElement);
            });
        });
        // Lắng nghe lệnh insertTable
        const insertTableCommand = editor.commands.get('insertTable');
        if (insertTableCommand) {
            this.listenTo(insertTableCommand, 'execute', () => {
                editor.model.change(writer => {
                    const position = editor.model.document.selection.getFirstPosition();
                    if (!position)
                        return;
                    const tableElement = position.findAncestor('table');
                    if (tableElement) {
                        this._applyTableDefaults(writer, tableElement);
                        writer.setAttribute('tableBorderColor', '#000000', tableElement);
                        writer.setAttribute('tableBorderStyle', 'solid', tableElement);
                        writer.setAttribute('tableBorderWidth', '1pt', tableElement);
                        this._applyCellBorders(writer, tableElement);
                    }
                });
            });
        }
        // Listen for row/column commands
        const tableCommands = [
            'insertTableRowAbove',
            'insertTableRowBelow',
            'insertTableColumnLeft',
            'insertTableColumnRight',
            'resizeTableRow',
            'resizeTableColumn',
            'setTableColumnWidth',
            'tableColumnWidth'
        ];
        tableCommands.forEach(cmdName => {
            const cmd = editor.commands.get(cmdName);
            if (cmd) {
                this.listenTo(cmd, 'execute', () => {
                    editor.model.change(writer => {
                        const position = editor.model.document.selection.getFirstPosition();
                        if (!position)
                            return;
                        const tableElement = position.findAncestor('table');
                        if (tableElement) {
                            this._applyTableDefaults(writer, tableElement);
                            this._applyCellBorders(writer, tableElement);
                        }
                    });
                });
            }
        });
        // Setup style preservation on model change
        this._setupStylePreservationOnModelChange();
    }
    /**
     * Apply default table width
     */
    _applyTableDefaults(writer, tableElement) {
        if (!tableElement)
            return;
        writer.setAttribute('tableWidth', '100%', tableElement);
    }
    /**
     * Apply default borders to all cells in a table
     */
    _applyCellBorders(writer, tableElement) {
        if (!tableElement)
            return;
        for (const row of tableElement.getChildren()) {
            for (const cell of row.getChildren()) {
                if (!cell.getAttribute('tableCellBorderColor')) {
                    writer.setAttribute('tableCellBorderColor', '#000000', cell);
                }
                if (!cell.getAttribute('tableCellBorderStyle')) {
                    writer.setAttribute('tableCellBorderStyle', 'solid', cell);
                }
                if (!cell.getAttribute('tableCellBorderWidth')) {
                    writer.setAttribute('tableCellBorderWidth', '1pt', cell);
                }
                if (!cell.getAttribute('tableCellPadding')) {
                    writer.setAttribute('tableCellPadding', '0.4em', cell);
                }
            }
        }
    }
    /**
     * Setup listener to preserve cell/table styles when model changes
     */
    _setupStylePreservationOnModelChange() {
        const editor = this.editor;
        // Use listenTo for proper cleanup via destroy()
        this.listenTo(editor.model.document, 'change', (evt, batch) => {
            if (batch?.isLocal === false)
                return;
            const changes = editor.model.document.differ.getChanges();
            const tablesToFix = this._findTablesNeedingFix(changes);
            if (tablesToFix.size > 0) {
                editor.model.enqueueChange(() => {
                    editor.model.change(writer => {
                        for (const table of tablesToFix) {
                            this._applyTableDefaults(writer, table);
                            this._applyCellBorders(writer, table);
                        }
                    });
                });
            }
        });
    }
    /**
     * Find tables that need border fixes from model changes
     */
    _findTablesNeedingFix(changes) {
        const tablesToFix = new Set();
        for (const change of changes) {
            if (change.type === 'attribute') {
                const attrKey = change.attributeKey;
                if (attrKey && (attrKey.includes('table') || attrKey.includes('column') || attrKey.includes('width'))) {
                    const element = change.item;
                    if (element) {
                        const parentTable = this._findParentTable(element);
                        if (parentTable)
                            tablesToFix.add(parentTable);
                    }
                }
            }
            if (change.type === 'insert' && change.position) {
                const tableElement = change.position.findAncestor?.('table') ||
                    change.position.parent?.findAncestor?.('table');
                if (tableElement)
                    tablesToFix.add(tableElement);
            }
        }
        return tablesToFix;
    }
    /**
     * Find parent table element
     */
    _findParentTable(element) {
        if (!element)
            return null;
        let parent = element;
        while (parent && !parent.is?.('element', 'table')) {
            parent = parent.parent;
        }
        return parent;
    }
    /**
     * Cleanup listeners when plugin is destroyed
     * Note: this.listenTo() listeners are automatically cleaned up by super.destroy()
     */
    destroy() {
        super.destroy();
    }
}

class ImageUploadPlugin extends Plugin {
    static get pluginName() {
        return 'ImageUploadPlugin';
    }
    init() {
        const editor = this.editor;
        editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
            return new Base64UploadAdapter(loader);
        };
    }
}
class Base64UploadAdapter {
    loader;
    constructor(loader) {
        this.loader = loader;
    }
    /**
     * Starts the upload process and converts the file to base64.
     * @returns Promise that resolves with the base64 URL
     */
    upload() {
        return this.loader.file.then((file) => {
            return new Promise((resolve, reject) => {
                const reader = new FileReader();
                reader.onload = () => {
                    const base64Url = reader.result;
                    resolve({ default: base64Url });
                };
                reader.onerror = () => {
                    reject('Failed to convert image to base64');
                };
                reader.readAsDataURL(file);
            });
        });
    }
    /**
     * Aborts the upload process.
     */
    abort() {
        // For base64 conversion, there's nothing to abort
        // This method is required by the UploadAdapter interface
    }
}

class ImageCustomPlugin extends Plugin {
    static get pluginName() {
        return 'ImageCustomPlugin';
    }
    init() {
        const editor = this.editor;
        // Thiết lập style mặc định là alignCenter khi chèn ảnh
        editor.commands.get('imageUpload')?.on('execute', (evt, args) => {
            // Đặt style mặc định sau khi ảnh được chèn
            setTimeout(() => {
                const selection = editor.model.document.selection;
                const imageElement = selection.getSelectedElement();
                if (imageElement && (imageElement.name === 'imageBlock' || imageElement.name === 'imageInline')) {
                    const currentStyle = imageElement.getAttribute('imageStyle');
                    // Chỉ đặt mặc định nếu chưa có style nào
                    if (!currentStyle) {
                        editor.model.change(writer => {
                            writer.setAttribute('imageStyle', 'alignCenter', imageElement);
                        });
                    }
                }
            }, 0);
        });
        // Downcast: Model -> View (HTML output)
        // CKEditor 5 có 2 loại ảnh: imageBlock và imageInline
        editor.conversion.for('downcast').add(dispatcher => {
            // Xử lý ảnh block (được wrap trong figure)
            dispatcher.on('insert:imageBlock', (evt, data, conversionApi) => {
                this.handleImageInsert(evt, data, conversionApi);
            }, { priority: 'low' });
            // Xử lý ảnh inline
            dispatcher.on('insert:imageInline', (evt, data, conversionApi) => {
                this.handleImageInsert(evt, data, conversionApi);
            }, { priority: 'low' });
            // Xử lý thay đổi attribute cho cả 2 loại ảnh
            ['imageBlock', 'imageInline'].forEach(imageType => {
                // Xử lý thay đổi src
                dispatcher.on(`attribute:src:${imageType}`, (evt, data, conversionApi) => {
                    this.handleImageAttributeChange(evt, data, conversionApi);
                }, { priority: 'low' });
                // Xử lý thay đổi width
                dispatcher.on(`attribute:width:${imageType}`, (evt, data, conversionApi) => {
                    this.handleImageAttributeChange(evt, data, conversionApi);
                }, { priority: 'low' });
                // Xử lý thay đổi height
                dispatcher.on(`attribute:height:${imageType}`, (evt, data, conversionApi) => {
                    this.handleImageAttributeChange(evt, data, conversionApi);
                }, { priority: 'low' });
                // Xử lý thay đổi imageStyle (căn chỉnh) - thêm float inline style
                dispatcher.on(`attribute:imageStyle:${imageType}`, (evt, data, conversionApi) => {
                    this.handleImageStyleChange(evt, data, conversionApi);
                }, { priority: 'low' });
            });
        });
        // Xử lý upcast (HTML paste) - xóa aspect-ratio từ HTML đầu vào
        editor.conversion.for('upcast').add(dispatcher => {
            dispatcher.on('element:img', (evt, data, conversionApi) => {
                const viewItem = data.viewItem;
                if (!viewItem)
                    return;
                // Kiểm tra viewItem có các method cần thiết
                if (typeof viewItem.getStyle !== 'function')
                    return;
                // Xóa aspect-ratio từ inline styles nếu có
                const hasAspectRatio = viewItem.getStyle('aspect-ratio');
                if (hasAspectRatio && typeof viewItem.removeStyle === 'function') {
                    viewItem.removeStyle('aspect-ratio');
                }
                // Đặt custom styles nếu _styles map tồn tại
                if (viewItem._styles && typeof viewItem._styles.set === 'function') {
                    viewItem._styles.set('margin', '0');
                    viewItem._styles.set('border', '0');
                    viewItem._styles.set('max-width', '100%');
                    viewItem._styles.set('height', 'auto');
                }
            }, { priority: 'high' });
        });
    }
    /**
     * Xử lý sự kiện chèn ảnh
     */
    handleImageInsert(evt, data, conversionApi) {
        const viewWriter = conversionApi.writer;
        const viewElement = conversionApi.mapper.toViewElement(data.item);
        if (!viewElement)
            return;
        // viewElement có thể là figure (cho block) hoặc img itself (cho inline)
        // Tìm element img thực tế
        const imgElement = this.findImgElement(viewElement);
        if (!imgElement)
            return;
        this.applyCustomStyles(viewWriter, imgElement);
    }
    /**
     * Xử lý sự kiện thay đổi attribute ảnh
     */
    handleImageAttributeChange(evt, data, conversionApi) {
        const viewWriter = conversionApi.writer;
        const viewElement = conversionApi.mapper.toViewElement(data.item);
        if (!viewElement)
            return;
        const imgElement = this.findImgElement(viewElement);
        if (!imgElement)
            return;
        this.applyCustomStyles(viewWriter, imgElement);
    }
    /**
     * Xử lý sự kiện thay đổi style ảnh - thêm float inline style cho căn chỉnh
     */
    handleImageStyleChange(evt, data, conversionApi) {
        const viewWriter = conversionApi.writer;
        const viewElement = conversionApi.mapper.toViewElement(data.item);
        if (!viewElement)
            return;
        // Tìm container ck-widget (element figure)
        const widgetElement = this.findWidgetElement(viewElement);
        if (!widgetElement)
            return;
        // Lấy giá trị imageStyle (căn chỉnh)
        const imageStyle = data.item.getAttribute('imageStyle');
        // Xóa các style hiện có trước
        viewWriter.removeStyle('float', widgetElement);
        viewWriter.removeStyle('margin', widgetElement);
        viewWriter.removeStyle('text-align', widgetElement);
        // Áp dụng style dựa trên căn chỉnh ảnh
        // Các options đã cấu hình: ['inline', 'alignLeft', 'alignRight', 'alignCenter']
        switch (imageStyle) {
            case 'inline':
                // Ảnh inline - không style đặc biệt, chỉ flow inline
                break;
            case 'alignLeft':
                // Float trái
                viewWriter.setStyle('float', 'left', widgetElement);
                viewWriter.setStyle('margin', '0 16px 16px 0', widgetElement);
                break;
            case 'alignRight':
                // Float phải
                viewWriter.setStyle('float', 'right', widgetElement);
                viewWriter.setStyle('margin', '0 0 16px 16px', widgetElement);
                break;
            case 'alignCenter':
            case 'block':
                // Căn giữa
                viewWriter.setStyle('text-align', 'center', widgetElement);
                viewWriter.setStyle('margin', '16px auto', widgetElement);
                break;
            default:
                // Mặc định - căn giữa
                viewWriter.setStyle('text-align', 'center', widgetElement);
                viewWriter.setStyle('margin', '16px auto', widgetElement);
                break;
        }
        // Áp dụng custom styles cơ bản cho element img
        const imgElement = this.findImgElement(viewElement);
        if (imgElement) {
            this.applyCustomStyles(viewWriter, imgElement);
        }
    }
    /**
     * Áp dụng custom styles cho element ảnh
     */
    applyCustomStyles(viewWriter, imgElement) {
        // Xóa aspect-ratio nếu tồn tại
        if (imgElement.getStyle('aspect-ratio')) {
            viewWriter.removeStyle('aspect-ratio', imgElement);
        }
        // Áp dụng custom styles
        viewWriter.setStyle('margin', '0', imgElement);
        viewWriter.setStyle('border', '0', imgElement);
        viewWriter.setStyle('max-width', '100%', imgElement);
        viewWriter.setStyle('height', 'auto', imgElement);
    }
    /**
     * Tìm container ck-widget (element figure)
     * CKEditor wrap ảnh block trong <figure class="ck-widget"><img></figure>
     */
    findWidgetElement(viewElement) {
        if (!viewElement)
            return null;
        // Nếu đây là element figure itself
        if (viewElement.name === 'figure') {
            return viewElement;
        }
        // Cho ảnh inline, trả về element itself (span wrapper)
        if (viewElement.name === 'span') {
            return viewElement;
        }
        // Tìm ngược lên tree để tìm figure/ck-widget
        let current = viewElement;
        while (current) {
            if (current.name === 'figure' || current.name === 'span') {
                return current;
            }
            // Di chuyển lên parent
            if (current.parent) {
                current = current.parent;
            }
            else {
                break;
            }
        }
        // Nếu không tìm thấy widget, trả về element gốc
        return viewElement;
    }
    /**
     * Tìm element img thực tế bên trong widget structure
     * CKEditor wrap ảnh block trong <figure class="ck-widget"><img></figure>
     */
    findImgElement(viewElement) {
        if (!viewElement)
            return null;
        // Nếu đây là element img itself
        if (viewElement.name === 'img') {
            return viewElement;
        }
        // Cho structure widget của CKEditor, tìm đệ quy
        // Ảnh block: figure > span > img
        // Ảnh thường được wrap trong một container
        const queue = [viewElement];
        while (queue.length > 0) {
            const current = queue.shift();
            if (!current)
                continue;
            if (current.name === 'img') {
                return current;
            }
            // Thêm children vào queue
            if (current.getChildren) {
                for (const child of current.getChildren()) {
                    queue.push(child);
                }
            }
        }
        return null;
    }
}

// Icon khổ dọc (Mặc định cũ)
const ICON_PORTRAIT = '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M14 2H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6V4h8v12z"/></svg>';
// Icon khổ ngang (Mới)
const ICON_LANDSCAPE = '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M18 4H2C.9 4 0 4.9 0 6v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 10H2V6h16v8z"/></svg>';
class PageOrientationPlugin extends Plugin {
    static pluginName = 'PageOrientationPlugin';
    _currentOrientation = 'PORTRAIT';
    orientationChangeEmitter;
    buttonView;
    init() {
        const editor = this.editor;
        const componentFactory = editor.ui.componentFactory;
        // Đăng ký nút tên là 'pageOrientation'
        componentFactory.add('pageOrientation', locale => {
            const view = new ButtonView(locale);
            this.buttonView = view;
            view.set({
                // label: 'Xoay giấy (A4)',
                icon: ICON_PORTRAIT,
                // tooltip: true,
                // withText: true,
                class: 'btn-orientation', // Class để style nếu cần
            });
            // Xử lý khi bấm nút
            view.on('execute', () => {
                this.toggleOrientation();
            });
            return view;
        });
    }
    /**
     * Toggle between portrait and landscape orientation
     */
    toggleOrientation() {
        const newOrientation = this._currentOrientation === 'PORTRAIT' ? 'LANDSCAPE' : 'PORTRAIT';
        this.setOrientation(newOrientation);
    }
    /**
     * Set orientation programmatically
     */
    setOrientation(orientation) {
        const editor = this.editor;
        const editingView = editor.editing.view;
        const rootElement = editingView.document.getRoot();
        editor.editing.view.change(writer => {
            if (orientation === 'LANDSCAPE') {
                writer.addClass('landscape', rootElement);
            }
            else {
                writer.removeClass('landscape', rootElement);
            }
        });
        // Update button icon
        if (this.buttonView) {
            this.buttonView.icon = orientation === 'LANDSCAPE' ? ICON_LANDSCAPE : ICON_PORTRAIT;
        }
        this._currentOrientation = orientation;
        this.orientationChangeEmitter?.(orientation);
    }
    /**
     * Get current orientation
     */
    getOrientation() {
        return this._currentOrientation;
    }
    /**
     * Register callback for orientation changes
     */
    onOrientationChange(callback) {
        this.orientationChangeEmitter = callback;
    }
}

/**
 * Cấu hình màu cho Document Builder
 * Bảng màu tập trung và cấu hình cho việc lựa chọn màu nhất quán
 */
/**
 * Trả về bảng màu chung được sử dụng trong tất cả tính năng của document builder
 * @returns Mảng các tùy chọn màu được định sẵn với giá trị hex và label
 */
function getPresetColors() {
    return [
        { color: '#000000', label: 'Black' },
        { color: '#4D4D4D', label: 'Dim grey' },
        { color: '#999999', label: 'Grey' },
        { color: '#E6E6E6', label: 'Light grey' },
        { color: '#FFFFFF', label: 'White' },
        { color: '#E64D4D', label: 'Red' },
        { color: '#E6994D', label: 'Orange' },
        { color: '#E6E64D', label: 'Yellow' },
        { color: '#99E64D', label: 'Light green' },
        { color: '#4DE64D', label: 'Green' },
        { color: '#4DE699', label: 'Aquamarine' },
        { color: '#4DE6E6', label: 'Turquoise' },
        { color: '#4D99E6', label: 'Light blue' },
        { color: '#4D4DE6', label: 'Blue' },
        { color: '#994DE6', label: 'Purple' },
    ];
}
/**
 * Trả về cấu hình bộ chọn màu với định dạng hex
 * @returns Đối tượng cấu hình bộ chọn màu
 */
function getColorPickerConfig() {
    return {
        format: 'hex',
    };
}
/**
 * Trả về cấu hình kích thước font cho document builder
 * @returns Mảng các tùy chọn kích thước font được định sẵn
 */
function getFontSizeOptions() {
    return [
        {
            title: '9',
            model: '9pt',
            view: {
                name: 'span',
                styles: { 'font-size': '9pt' },
                priority: 7,
            },
        },
        {
            title: '10',
            model: '10pt',
            view: {
                name: 'span',
                styles: { 'font-size': '10pt' },
                priority: 7,
            },
        },
        {
            title: '11',
            model: '11pt',
            view: {
                name: 'span',
                styles: { 'font-size': '11pt' },
                priority: 7,
            },
        },
        {
            title: '12',
            model: '12pt',
            view: {
                name: 'span',
                styles: { 'font-size': '12pt' },
                priority: 7,
            },
        },
        {
            title: '13',
            model: '13pt',
            view: {
                name: 'span',
                styles: { 'font-size': '13pt' },
                priority: 7,
            },
        },
        {
            title: '14',
            model: '14pt',
            view: {
                name: 'span',
                styles: { 'font-size': '14pt' },
                priority: 7,
            },
        },
        {
            title: '16',
            model: '16pt',
            view: {
                name: 'span',
                styles: { 'font-size': '16pt' },
                priority: 7,
            },
        },
        {
            title: '18',
            model: '18pt',
            view: {
                name: 'span',
                styles: { 'font-size': '18pt' },
                priority: 7,
            },
        },
        {
            title: '20',
            model: '20pt',
            view: {
                name: 'span',
                styles: { 'font-size': '20pt' },
                priority: 7,
            },
        },
        {
            title: '24',
            model: '24pt',
            view: {
                name: 'span',
                styles: { 'font-size': '24pt' },
                priority: 7,
            },
        },
    ];
}
function getHeadingOptions() {
    return [
        { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
        { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
        { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
        { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
    ];
}

/**
 * Document Builder Utilities
 * Các hàm tiện ích cho document builder
 */
/**
 * Chuẩn hóa nội dung bằng cách chuyển đổi tất cả màu HSL và RGB sang hex
 * @param content - Nội dung HTML cần chuẩn hóa
 * @returns Nội dung đã được chuẩn hóa với màu hex
 */
function normalize(content) {
    let normalized = content;
    // Chuyển đổi HSL sang hex
    const hslRegex = /hsl\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)/gi;
    normalized = normalized.replace(hslRegex, (match, h, s, l) => {
        try {
            const hue = parseInt(h, 10);
            const saturation = parseInt(s, 10);
            const lightness = parseInt(l, 10);
            // Kiểm tra giá trị hợp lệ
            if (hue >= 0 && hue <= 360 && saturation >= 0 && saturation <= 100 && lightness >= 0 && lightness <= 100) {
                return hslToHex(hue, saturation, lightness);
            }
        }
        catch (error) {
            console.warn('Failed to convert HSL to hex:', error, match);
        }
        return match; // Giữ nguyên nếu không thể chuyển đổi
    });
    // Chuyển đổi RGB sang hex
    const rgbRegex = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/gi;
    normalized = normalized.replace(rgbRegex, (match, r, g, b) => {
        try {
            const red = parseInt(r, 10);
            const green = parseInt(g, 10);
            const blue = parseInt(b, 10);
            if (red >= 0 && red <= 255 && green >= 0 && green <= 255 && blue >= 0 && blue <= 255) {
                return rgbToHex(red, green, blue);
            }
        }
        catch (error) {
            console.warn('Failed to convert RGB to hex:', error, match);
        }
        return match;
    });
    return normalized;
}

class HighlightRangePlugin extends Plugin {
    init() {
        const editor = this.editor;
        editor.conversion.for('editingDowncast').markerToHighlight({
            model: 'highlightRange',
            view: {
                classes: 'highlight-range',
            },
        });
    }
}

class SdDocumentBuilder {
    option;
    disabled = false;
    set _disabled(val) {
        this.disabled = val === '' || !!val;
        this.#updateState();
    }
    contentChange = new EventEmitter(); // Emit HTML content
    Editor = ClassicEditor;
    #editor;
    #id = '55b0afb0-288d-423c-98b3-5f9db286e16d';
    #subscription = new Subscription();
    #sharedColors = getPresetColors();
    #headingOptions = getHeadingOptions();
    #fontSizeOptions = getFontSizeOptions();
    #colorPickerConfig = getColorPickerConfig();
    #contentChangeSubject = new Subject();
    #idTimeOutScrollHeading = null;
    #headingElementsMap = new Map(); // Hash lưu trữ các heading
    // Config
    config = {
        getOption: () => this.option,
        licenseKey: 'GPL', // Hoặc key thương mại nếu có
        plugins: [
            GeneralHtmlSupport,
            FontSize,
            FontColor,
            FontFamily,
            FontBackgroundColor,
            Bold,
            Italic,
            Underline,
            Essentials,
            Paragraph,
            Heading,
            List,
            Table,
            TableToolbar,
            TableProperties,
            TableCellProperties,
            TableColumnResize,
            PasteFromOffice,
            PageBreak,
            Undo,
            Alignment, // Canh lề
            Subscript, // Mũ dưới (H₂O)
            Superscript, // Mũ trên (x²)
            // Image
            Image,
            ImageUpload,
            ImageToolbar,
            ImageCaption,
            ImageResize,
            ImageStyle,
            ImageBlock,
            // Custom Plugin
            HeadingPlugin,
            CommentPlugin,
            VariablePlugin,
            TableFitPlugin,
            Indent,
            IndentBlock,
            PageOrientationPlugin,
            ImageUploadPlugin,
            ImageCustomPlugin,
            HighlightRangePlugin,
        ],
        toolbar: {
            items: [
                'pageOrientation',
                '|',
                'heading',
                'fontFamily',
                '|',
                'fontSize',
                'fontColor',
                'fontBackgroundColor',
                '|',
                'bold',
                'italic',
                'underline',
                'subscript',
                'superscript',
                '|',
                'alignment',
                'bulletedList',
                'numberedList',
                '|',
                'insertTable',
                'imageUpload',
                'pageBreak',
                '|',
                'undo',
                'redo',
                '|',
                'addCommentBtn',
            ],
            shouldNotGroupWhenFull: true,
        },
        image: {
            styles: {
                options: ['inline', 'alignLeft', 'alignRight', 'alignCenter'],
            },
            toolbar: [
                'imageStyle:inline',
                'imageStyle:alignCenter',
                {
                    name: 'imageStyle:alignDropdown',
                    items: ['imageStyle:alignLeft', 'imageStyle:alignRight'],
                    defaultItem: 'imageStyle:alignLeft',
                },
            ],
        },
        fontSize: {
            options: this.#fontSizeOptions,
            supportAllValues: false, // Khuyên dùng false để ép user chọn đúng size chuẩn
        },
        heading: {
            options: this.#headingOptions,
        },
        // 4. Cấu hình bảng màu (Tùy chọn)
        fontColor: {
            // columns: 5,
            documentColors: 10,
            colorPicker: this.#colorPickerConfig,
            colors: this.#sharedColors,
        },
        fontBackgroundColor: {
            // columns: 5,
            documentColors: 10,
            colorPicker: this.#colorPickerConfig,
            colors: this.#sharedColors,
        },
        table: {
            contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', '|', 'tableProperties', 'tableCellProperties'],
            tableProperties: {
                borderColors: this.#sharedColors,
                backgroundColors: this.#sharedColors,
                colorPicker: this.#colorPickerConfig,
                defaultProperties: {
                    borderStyle: 'solid',
                    borderWidth: '1px',
                    borderColor: '#ccc',
                },
            },
            tableCellProperties: {
                borderColors: this.#sharedColors,
                backgroundColors: this.#sharedColors,
                colorPicker: this.#colorPickerConfig,
                defaultProperties: {
                    borderStyle: 'solid',
                    borderWidth: '1px',
                    borderColor: '#ccc',
                },
            },
        },
        indentBlock: {
            offset: 48, // Đơn vị px cho mỗi mức indent (tương đương 0.5 inch)
            unit: 'px',
        },
        // Quan trọng: Cho phép paste style từ Word nhưng bỏ qua margin/padding
        htmlSupport: {
            allow: [
                {
                    name: /.*/,
                    attributes: /.*/, // attributes chấp nhận boolean
                    classes: /.*/, // Cho phép mọi class
                    styles: {
                        // Cho phép tất cả styles trừ margin và padding
                        match: /^(?!margin|padding).*/,
                    },
                },
            ],
        },
    };
    ngOnInit() {
        // Debounce trong rxjs không hỗ trợ leading --> throttleTime
        this.#subscription.add(this.#contentChangeSubject.pipe(throttleTime(500, undefined, { leading: true, trailing: true })).subscribe(content => {
            this.contentChange.emit(normalize(content));
        }));
    }
    ngOnDestroy() {
        this.#subscription.unsubscribe();
    }
    onReady(editor) {
        this.#editor = editor;
        // Setup orientation plugin callback
        try {
            const orientationPlugin = editor.plugins.get('PageOrientationPlugin');
            if (orientationPlugin && typeof orientationPlugin.onOrientationChange === 'function') {
                orientationPlugin.onOrientationChange(orientation => {
                    this.option.onOrientation?.(orientation);
                });
                // Set initial orientation if provided
                if (this.option.orientation) {
                    orientationPlugin.setOrientation(this.option.orientation);
                }
            }
        }
        catch (error) {
            console.warn('PageOrientationPlugin not available:', error);
        }
        // Lắng nghe selection
        editor.model.document.selection.on('change', $event => {
            this.option.onSelection?.(this.#editor.model.document.selection, $event);
        });
        // Lắng nghe sự kiện thay đổi nội dung
        editor.model.document.on('change:data', () => {
            const content = editor.getData();
            this.#contentChangeSubject.next(content);
        });
        try {
            // Manual keybinding cho Tab nếu cần
            editor.keystrokes.set('Tab', (evt, cancel) => {
                const command = editor.commands.get('indentBlock');
                if (command && command.isEnabled) {
                    editor.execute('indentBlock');
                    cancel();
                }
            });
            // Manual keybinding cho Shift+Tab
            editor.keystrokes.set('Shift+Tab', (evt, cancel) => {
                const command = editor.commands.get('outdentBlock');
                if (command && command.isEnabled) {
                    editor.execute('outdentBlock');
                    cancel();
                }
            });
        }
        catch (error) {
            console.warn('Error setting up indent keybindings:', error);
        }
        this.#updateState();
    }
    setContent = (html) => {
        this.#editor?.setData?.(html);
    };
    getContent = () => {
        if (this.#editor) {
            return this.#editor.getData();
        }
        return '';
    };
    setOrientation = (orientation) => {
        if (!this.#editor)
            return;
        try {
            const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
            if (orientationPlugin && typeof orientationPlugin.setOrientation === 'function') {
                orientationPlugin.setOrientation(orientation);
            }
        }
        catch (error) {
            console.warn('Failed to set orientation:', error);
        }
    };
    getOrientation = () => {
        if (!this.#editor)
            return 'PORTRAIT';
        try {
            const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
            if (orientationPlugin && typeof orientationPlugin.getOrientation === 'function') {
                return orientationPlugin.getOrientation();
            }
        }
        catch (error) {
            console.warn('Failed to get orientation:', error);
        }
        return 'PORTRAIT';
    };
    scrollToTop() {
        setTimeout(() => {
            if (this.#editor) {
                const editableElement = this.#editor.ui.view.editable.element;
                if (editableElement) {
                    const scrollContainer = editableElement.closest('.builder-container');
                    if (scrollContainer) {
                        scrollContainer.scrollTo({
                            top: 0,
                            behavior: 'smooth',
                        });
                    }
                    else {
                        editableElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
                    }
                }
            }
        }, 100);
    }
    #updateState() {
        if (!this.#editor)
            return;
        if (this.disabled) {
            // Bật chế độ chỉ đọc với ID khóa
            this.#editor.enableReadOnlyMode(this.#id);
            // Disable page orientation button
            try {
                const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
                if (orientationPlugin && orientationPlugin.buttonView) {
                    orientationPlugin.buttonView.isEnabled = false;
                }
            }
            catch (error) {
                console.warn('Failed to disable orientation button:', error);
            }
        }
        else {
            // Tắt chế độ chỉ đọc với ID khóa tương ứng
            this.#editor.disableReadOnlyMode(this.#id);
            // Enable page orientation button
            try {
                const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
                if (orientationPlugin && orientationPlugin.buttonView) {
                    orientationPlugin.buttonView.isEnabled = true;
                }
            }
            catch (error) {
                console.warn('Failed to enable orientation button:', error);
            }
        }
    }
    #getTextFromElement = (element) => {
        let text = '';
        // Heading trong Model chứa các text node con
        for (const child of element.getChildren()) {
            if (child.is('$text') || child.is('$textProxy')) {
                text += child.data;
            }
        }
        return text;
    };
    #getTextFromRange = (range) => {
        let text = '';
        for (const item of range.getItems()) {
            // TextProxy là một phần của Text Node nằm trong Range
            if (item.is('$textProxy') || item.is('$text')) {
                text += item.data;
            }
        }
        return text;
    };
    // ========================================================================
    // 1. QUẢN LÝ HEADING
    // ========================================================================
    heading = {
        /**
         * Lấy tất cả headings trong document
         * @returns Danh sách tất cả headings
         */
        all: () => {
            if (!this.#editor)
                return [];
            const root = this.#editor.model.document.getRoot();
            if (!root)
                return [];
            // Reset lại map mỗi lần quét
            this.#headingElementsMap.clear();
            const headings = [];
            const range = this.#editor.model.createRangeIn(root);
            // Biến đếm để tạo ID unique
            let index = 0;
            for (const item of range.getItems()) {
                // Kiểm tra xem item có phải là Element và tên bắt đầu bằng 'heading' không
                if (item.is('element') && item.name.startsWith('heading')) {
                    // 1. Lấy text của heading
                    const text = this.#getTextFromElement(item);
                    // 2. Xác định Level (heading1 -> 1, heading2 -> 2)
                    // item.name có dạng 'heading1', 'heading2'
                    const level = parseInt(item.name.replace('heading', ''), 10) || 1;
                    // 3. Tạo ID runtime (Dùng để map khi click scroll)
                    // Bạn có thể generate slug từ text nếu muốn, ở đây mình dùng index cho đơn giản và unique
                    const id = `heading_${index}_${Date.now()}`;
                    // 4. Lưu tham chiếu Model Element vào Map
                    this.#headingElementsMap.set(id, item);
                    headings.push({
                        id: id,
                        text: text,
                        level: level,
                        type: item.name,
                    });
                    index++;
                }
            }
            return headings;
        },
        /**
         * Scroll tới vị trí của heading
         * @param id - ID của heading cần scroll tới
         */
        scroll: (id) => {
            if (!this.#editor)
                return;
            const modelElement = this.#headingElementsMap.get(id);
            if (modelElement) {
                this.#editor.model.change(writer => {
                    // Xóa marker cũ
                    if (this.#idTimeOutScrollHeading) {
                        clearTimeout(this.#idTimeOutScrollHeading);
                    }
                    const currentMarker = this.#editor.model.markers.get('highlightMarker');
                    if (currentMarker) {
                        writer.removeMarker(currentMarker);
                    }
                    // Tạo Range bao trùm highlight
                    const range = writer.createRangeOn(modelElement);
                    // Thêm Marker mới
                    writer.addMarker('highlightMarker', {
                        range: range,
                        usingOperation: false,
                    });
                });
                // Scroll tới vị trí tìm được
                const viewElement = this.#editor.editing.mapper.toViewElement(modelElement);
                if (viewElement) {
                    const domElement = this.#editor.editing.view.domConverter.viewToDom(viewElement);
                    if (domElement) {
                        domElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
                    }
                }
                // Tự động tắt marker sau 5 giây
                this.#idTimeOutScrollHeading = setTimeout(() => {
                    if (this.#editor) {
                        this.#editor.model.change(writer => {
                            const marker = this.#editor.model.markers.get('highlightMarker');
                            if (marker)
                                writer.removeMarker(marker);
                        });
                    }
                }, 5000);
            }
            else {
                console.warn(`Heading with id ${id} not found.`);
            }
        },
    };
    // ========================================================================
    // 2. QUẢN LÝ COMMENT
    // ========================================================================
    comment = {
        /**
         * Lấy tất cả comments trong document
         * @returns Danh sách tất cả comments
         */
        all: () => {
            if (!this.#editor)
                return [];
            const editableElement = this.#editor.ui.view.editable.element;
            if (!editableElement)
                return [];
            const commentMarkers = editableElement.querySelectorAll('.ck-comment-marker[data-comment-id^="comment:"]') || [];
            const commentsMap = new Map();
            commentMarkers.forEach(el => {
                const markerId = el.getAttribute('data-comment-id');
                if (markerId) {
                    const existing = commentsMap.get(markerId);
                    const text = el.textContent || '';
                    if (existing) {
                        existing.selectedText += text;
                    }
                    else {
                        commentsMap.set(markerId, {
                            markerId,
                            selectedText: text,
                        });
                    }
                }
            });
            return Array.from(commentsMap.values());
        },
        /**
         * Thêm comment vào vùng text đang được chọn
         * @param comment - Dữ liệu comment
         * @param data - Dữ liệu extra data
         * @returns SdDocumentBuilderComment hoặc null nếu không có text được chọn
         */
        add: (range, comment, args) => {
            // markerIdExternal khi truyền từ bên ngoài vào sẽ có thể là Date.now() hoặc uuidv4().
            // Miễn là đảm bảo markerIdExternal là unique.
            // Phục vụ cho case gọi API comment thành công thì mới sinh ra markerId.
            const { markerIdExternal, data } = args ?? {};
            if (!this.#editor)
                return null;
            const model = this.#editor.model;
            if (!range)
                return null;
            // 3. Lấy text từ range
            const selectedText = this.#getTextFromRange(range);
            if (!selectedText.trim()) {
                console.warn('Selected text is empty');
                return null;
            }
            // 4. Tạo ID unique cho marker
            const markerId = markerIdExternal ? `comment:${markerIdExternal}` : `comment:${Date.now()}`;
            // 5. Tạo marker trong model
            model.change(writer => {
                writer.addMarker(markerId, {
                    range,
                    usingOperation: true, // Quan trọng: Cho phép undo/redo
                    affectsData: true, // Lưu marker vào data khi getData()
                });
            });
            return {
                markerId,
                selectedText,
                comment,
                createdAt: new Date(),
                data,
            };
        },
        /**
         * Cập nhật nội dung comment
         * @param markerId - ID của marker
         * @param commentData - Dữ liệu mới
         * @returns Comment đã cập nhật hoặc null
         */
        update: (markerId, comment, data) => {
            if (!this.#editor)
                return null;
            const marker = this.#editor.model.markers.get(markerId);
            if (!marker) {
                console.warn(`Marker ${markerId} not found`);
                return null;
            }
            // Lấy text hiện tại từ marker (có thể đã thay đổi)
            const currentText = this.#getTextFromRange(marker.getRange());
            return {
                markerId: markerId,
                selectedText: currentText,
                comment,
                data,
            };
        },
        /**
         * Lấy chi tiết comment theo markerId
         * @param markerId - ID của marker
         * @returns Comment hoặc null
         */
        detail: (markerId) => {
            if (!this.#editor)
                return null;
            const marker = this.#editor.model.markers.get(markerId);
            if (!marker)
                return null;
            const currentText = this.#getTextFromRange(marker.getRange());
            return {
                markerId: markerId,
                selectedText: currentText,
            };
        },
        /**
         * Xóa comment theo markerId
         * @param markerId - ID của marker cần xóa
         * @returns true nếu xóa thành công, false nếu không tìm thấy
         */
        remove: (markerId) => {
            if (!this.#editor)
                return false;
            const model = this.#editor.model;
            const marker = model.markers.get(markerId);
            if (!marker) {
                console.warn(`Marker ${markerId} not found`);
                return false;
            }
            model.change(writer => {
                writer.removeMarker(markerId);
            });
            return true;
        },
        /**
         * Scroll đến vị trí comment
         * @param markerId - ID của marker cần scroll tới
         */
        scroll: (markerId) => {
            if (!this.#editor)
                return;
            const editor = this.#editor;
            const marker = editor.model.markers.get(markerId);
            if (marker) {
                // 1. Set Selection vào Marker đó trước
                editor.model.change(writer => {
                    writer.setSelection(marker.getRange());
                });
                // 2. Sau đó gọi scrollToTheSelection
                editor.editing.view.scrollToTheSelection({
                    alignToTop: true,
                });
                editor.editing.view.focus();
            }
            else {
                console.warn(`Marker with id ${markerId} not found.`);
            }
        },
    };
    // ========================================================================
    // 3. QUẢN LÝ VARIABLE
    // ========================================================================
    variable = {
        /**
         * Lấy tất cả variabes trong document
         * @returns Danh sách tất cả variables
         */
        all: () => {
            if (!this.#editor)
                return [];
            const model = this.#editor.model;
            const root = model.document.getRoot();
            if (!root)
                return [];
            const variables = [];
            try {
                const range = model.createRangeIn(root);
                for (const item of range.getItems()) {
                    // Sử dụng item.is('element', 'variable') là chính xác
                    if (item.is('element', 'variable')) {
                        variables.push({
                            id: item.getAttribute('id'),
                            uuid: item.getAttribute('uuid'),
                            value: item.getAttribute('value'),
                            display: item.getAttribute('display'),
                        });
                    }
                }
            }
            catch (e) {
                console.error(e);
                return [];
            }
            return variables;
        },
        /**
         * Scroll tới vị trí của variable
         * @param uuid - uuid của variable FE sẽ tự sinh sau mỗi lần drop vào editor
         */
        scroll: (uuid) => {
            if (!this.#editor)
                return;
            const model = this.#editor.model;
            const root = model.document.getRoot();
            if (!root)
                return;
            let targetElement = null;
            const range = model.createRangeIn(root);
            for (const item of range.getItems()) {
                if (item.is('element', 'variable') && item.getAttribute('uuid') === uuid) {
                    targetElement = item;
                    break;
                }
            }
            if (targetElement) {
                const viewElement = this.#editor.editing.mapper.toViewElement(targetElement);
                if (viewElement) {
                    const domElement = this.#editor.editing.view.domConverter.viewToDom(viewElement);
                    if (domElement) {
                        domElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
                        model.change(writer => {
                            writer.setSelection(targetElement, 'on');
                        });
                    }
                }
            }
            else {
                console.warn(`Variable với id ${uuid} không tìm thấy trong tài liệu.`);
            }
        },
    };
    // ========================================================================
    // 4. HÀM EXPORT DOCX (FULL HEADER/FOOTER + PAGE NUMBER)
    // ========================================================================
    /**
     * Xuất file Word có Header/Footer
     * @param fileName Tên file
     * @param headerText Nội dung Header (hoặc HTML)
     * '<p style="text-align: center; font-weight: bold;">CÔNG TY CỔ PHẦN CÔNG NGHỆ ABC</p>'
     * @param footerText Nội dung Footer (hoặc HTML)
     * '<p style="text-align: right; font-size: 10pt;">Trang <span style="mso-field-code: PAGE"></span> / <span style="mso-field-code: NUMPAGES"></span></p>'
     */
    exportDocx(args) {
        if (!this.#editor)
            return;
        const fileName = args?.fileName || `document_${Date.now()}.docx`;
        const header = args?.header || ``;
        const footer = args?.footer || ``;
        // 1. Kiểm tra xoay giấy
        const rootElement = this.#editor.editing.view.document.getRoot();
        const isLandscape = rootElement?.hasClass('landscape');
        const orientation = isLandscape ? 'landscape' : 'portrait';
        // Kích thước giấy cho Word (A4)
        // Portrait: 21cm x 29.7cm
        // Landscape: 29.7cm x 21cm
        // @page size trong CSS Word đôi khi cần set cứng width/height để header không bị lệch
        const pageCss = isLandscape
            ? 'size: 29.7cm 21cm; mso-page-orientation: landscape;'
            : 'size: 21cm 29.7cm; mso-page-orientation: portrait;';
        const contentHtml = this.#editor.getData();
        // 2. TẠO HTML CHUẨN MICROSOFT WORD XML
        // Phải có xmlns:o và xmlns:w thì Word mới hiểu được các lệnh mso-
        const fullHtml = `
      <html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>
        <head>
          <meta charset="UTF-8">
          <title>Export Document</title>
          
          <style>
            /* --- 1. SETUP TRANG GIẤY & HEADER/FOOTER --- */
            @page {
              ${pageCss}
              margin: 2.0cm; /* Lề chuẩn 2cm */
              
              /* Kỹ thuật map ID của Word */
              mso-header: header1;
              mso-footer: footer1;
            }

            /* Quan trọng: Ẩn các div header/footer khỏi luồng văn bản chính */
            /* Chúng sẽ chỉ hiện thị khi được @page gọi thông qua mso-header/footer */
            div#header1 { display: none; }
            div#footer1 { display: none; }

            /* --- 2. STYLE CƠ BẢN --- */
            body {
              font-family: 'Times New Roman', serif;
              font-size: 13pt;
              line-height: 1.5;
              tab-interval: 36pt;
            }
            
            /* Style Bảng */
            table { width: 100%; border-collapse: collapse; }
            td, th { border: 1px solid black; padding: 5px; }

            /* Style Biến (Giữ nguyên giao diện đẹp của bạn) */
            .variable-widget {
              color: #1565c0; font-weight: bold;
              background: #e3f2fd; padding: 0 4px; border-radius: 4px;
              border: 1px solid #90caf9;
            }
            
            /* Ẩn Comment khi in/xuất file */
            .ck-comment-marker { background-color: transparent; border: none; text-decoration: none; }
          </style>
        </head>
        <body>
          
          <div id="header1">
            <div style="mso-element:header">
               ${header}
               <div style='border-bottom: 1px solid #000; margin-top: 5px; margin-bottom: 20px;'></div>
            </div>
          </div>

          <div id="footer1">
            <div style="mso-element:footer">
               <div style='border-top: 1px solid #000; margin-top: 20px; margin-bottom: 5px;'></div>
               ${footer}
            </div>
          </div>

          <div class="content">
            ${contentHtml}
          </div>

        </body>
      </html>
    `;
        // 3. TẠO BLOB VÀ TẢI XUỐNG
        // Lưu ý: Dùng type 'application/msword' thay vì dùng thư viện html-docx-js
        // Thêm '\ufeff' (BOM) để fix lỗi font tiếng Việt
        const blob = new Blob(['\ufeff', fullHtml], { type: 'application/msword' });
        SdUtilities.downloadBlob(blob, fileName);
    }
    hightSelectRange = (range) => {
        if (!range)
            return;
        const editor = this.#editor;
        editor.model.change(writer => {
            // Xóa marker cũ (nếu có)
            if (editor.model.markers.has('highlightRange')) {
                writer.removeMarker('highlightRange');
            }
            // Tạo marker mới
            writer.addMarker('highlightRange', {
                usingOperation: false, // Không lưu vào lịch sử Undo/Redo
                affectsData: false, // Không ảnh hưởng đến data lấy ra (getData)
                range: range,
            });
        });
    };
    removeHighlightSeclectRange = () => {
        const editor = this.#editor;
        editor.model.change(writer => {
            if (editor.model.markers.has('highlightRange')) {
                writer.removeMarker('highlightRange');
            }
        });
    };
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdDocumentBuilder, isStandalone: true, selector: "sd-document-builder", inputs: { option: "option", _disabled: ["disabled", "_disabled"] }, outputs: { contentChange: "contentChange" }, ngImport: i0, template: "<div class=\"builder-container\">\n  <ckeditor\n    style=\"width: 100%\"\n    [editor]=\"Editor\" \n    [config]=\"config\" \n    (ready)=\"onReady($event)\"\n    [disabled]=\"disabled\">\n  </ckeditor>\n</div>", styles: ["@charset \"UTF-8\";.builder-container{background-color:#f3f4f6;height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.5}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:0 4px 6px -1px #0000001a!important;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:var(--ck-spacing-large);text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", ":host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}:host ::ng-deep .ck-comment-marker:hover{background-color:#ffeb3bcc}:host ::ng-deep .ck-comment-marker.active-highlight{background-color:#ffeb3b;outline:2px dashed #f57f17}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0;cursor:default}:host ::ng-deep .variable-widget:before{content:attr(data-display);font-size:10px}:host ::ng-deep .variable-widget:hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget.ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", "", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableWatchdog", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, decorators: [{
            type: Component,
            args: [{ selector: 'sd-document-builder', standalone: true, imports: [CommonModule, CKEditorModule], template: "<div class=\"builder-container\">\n  <ckeditor\n    style=\"width: 100%\"\n    [editor]=\"Editor\" \n    [config]=\"config\" \n    (ready)=\"onReady($event)\"\n    [disabled]=\"disabled\">\n  </ckeditor>\n</div>", styles: ["@charset \"UTF-8\";.builder-container{background-color:#f3f4f6;height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.5}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:0 4px 6px -1px #0000001a!important;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:var(--ck-spacing-large);text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", ":host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}:host ::ng-deep .ck-comment-marker:hover{background-color:#ffeb3bcc}:host ::ng-deep .ck-comment-marker.active-highlight{background-color:#ffeb3b;outline:2px dashed #f57f17}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0;cursor:default}:host ::ng-deep .variable-widget:before{content:attr(data-display);font-size:10px}:host ::ng-deep .variable-widget:hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget.ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n"] }]
        }], propDecorators: { option: [{
                type: Input,
                args: [{ required: true }]
            }], _disabled: [{
                type: Input,
                args: ['disabled']
            }], contentChange: [{
                type: Output
            }] } });

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

export { SdDocumentBuilder, SdHeaderFooterBuilder };
//# sourceMappingURL=sd-angular-core-components-document-builder.mjs.map
