"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get ExportFile () {
        return ExportFile;
    },
    get PLUGIN_KEY () {
        return PLUGIN_KEY;
    },
    get PLUGIN_PRIORITY () {
        return PLUGIN_PRIORITY;
    }
});
const _base = require("../base");
const _errors = require("../../helpers/errors");
const _object = require("../../helpers/object");
const _constants = require("../../helpers/constants");
const _constants1 = require("../../i18n/constants");
const _dataProvider = /*#__PURE__*/ _interop_require_default(require("./dataProvider"));
const _typeFactory = /*#__PURE__*/ _interop_require_wildcard(require("./typeFactory"));
const _exportItem = /*#__PURE__*/ _interop_require_default(require("./contextMenuItem/exportItem"));
const _utils = require("./utils");
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_private_method_get(receiver, privateSet, fn) {
    if (!privateSet.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return fn;
}
function _class_private_method_init(obj, privateSet) {
    _check_private_redeclaration(obj, privateSet);
    privateSet.add(obj);
}
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
const PLUGIN_KEY = 'exportFile';
const PLUGIN_PRIORITY = 240;
var /**
   * Add export options to the Context Menu.
   *
   * @param {object} options Contains default added options of the Context Menu.
   */ _onAfterContextMenuDefaultOptions = /*#__PURE__*/ new WeakSet(), /**
   * Triggers a browser file download for the given blob.
   *
   * @param {Blob} blob The blob to download.
   * @param {string} name The filename (including extension).
   */ _triggerDownload = /*#__PURE__*/ new WeakSet();
class ExportFile extends _base.BasePlugin {
    static get PLUGIN_KEY() {
        return PLUGIN_KEY;
    }
    static get PLUGIN_PRIORITY() {
        return PLUGIN_PRIORITY;
    }
    static get SETTING_KEYS() {
        return [
            PLUGIN_KEY
        ];
    }
    /**
   * Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit}
   * hook and if it returns `true` then the {@link ExportFile#enablePlugin} method is called.
   *
   * @returns {boolean}
   */ isEnabled() {
        return true;
    }
    /**
   * Enables the plugin functionality for this Handsontable instance.
   */ enablePlugin() {
        if (this.enabled) {
            return;
        }
        this.addHook('afterContextMenuDefaultOptions', (...args)=>_class_private_method_get(this, _onAfterContextMenuDefaultOptions, onAfterContextMenuDefaultOptions).call(this, ...args));
        super.enablePlugin();
    }
    /**
   * Disables the plugin functionality for this Handsontable instance.
   */ disablePlugin() {
        super.disablePlugin();
    }
    /**
   * Exports table data as a string.
   *
   * This method is only supported for text-based formats such as CSV.
   * Calling it with a binary format (e.g. `'xlsx'`) throws an error.
   *
   * @param {string} format Export format type eq. `'csv'`.
   * @param {object} options Export options.
   * @param {string} [options.mimeType] MIME type (e.g. `'text/csv'` for CSV). Default depends on format.
   * @param {string} [options.fileExtension] File extension (e.g. `'csv'`). Default depends on format.
   * @param {string} [options.filename='Handsontable [YYYY]-[MM]-[DD]'] File name. Placeholders `[YYYY]`, `[MM]`, `[DD]` are replaced with the current date.
   * @param {string} [options.encoding='utf-8'] Character encoding.
   * @param {boolean} [options.bom] Include BOM signature. Default depends on format (e.g. `true` for CSV).
   * @param {string} [options.columnDelimiter=','] Column delimiter (CSV only).
   * @param {string} [options.rowDelimiter='\r\n'] Row delimiter (CSV only).
   * @param {boolean} [options.colHeaders=false] Include column headers in the exported file.
   * @param {boolean} [options.rowHeaders=false] Include row headers in the exported file.
   * @param {boolean|string} [options.exportHiddenColumns=false] Controls how hidden columns are handled. `true` exports them as normal visible columns. `false` omits them entirely. `'hide'` exports them and marks them as hidden in Excel (XLSX only).
   * @param {boolean|string} [options.exportHiddenRows=false] Controls how hidden rows are handled. `true` exports them as normal visible rows. `false` omits them entirely. `'hide'` exports them and marks them as hidden in Excel (XLSX only).
   * @param {number[]} [options.range=[]] Cell range to export: `[startRow, startColumn, endRow, endColumn]` (visual indexes).
   * @param {boolean|RegExp|Function} [options.sanitizeValues=false] Controls the sanitization of cell values (CSV only).
   * @returns {string}
   */ exportAsString(format, options = {}) {
        const formatter = this._createTypeFormatter(format, options);
        if (formatter.binary) {
            (0, _errors.throwWithCause)(`Export format type "${format}" cannot be exported as a string (binary format).`);
        }
        return formatter.export();
    }
    /**
   * Exports table data as a blob object.
   *
   * This method is only supported for text-based formats such as CSV.
   * Calling it with a binary format (e.g. `'xlsx'`) throws an error — use
   * [[ExportFile#exportAsBlobAsync]] instead.
   *
   * @param {string} format Export format type e.g. `'csv'`.
   * @param {object} options Export options.
   * @param {string} [options.mimeType] MIME type. Default depends on format.
   * @param {string} [options.fileExtension] File extension. Default depends on format.
   * @param {string} [options.filename='Handsontable [YYYY]-[MM]-[DD]'] File name.
   * @param {string} [options.encoding='utf-8'] Character encoding.
   * @param {boolean} [options.bom] Include BOM signature. Default depends on format (e.g. `true` for CSV).
   * @param {string} [options.columnDelimiter=','] Column delimiter (CSV only).
   * @param {string} [options.rowDelimiter='\r\n'] Row delimiter (CSV only).
   * @param {boolean} [options.colHeaders=false] Include column headers.
   * @param {boolean} [options.rowHeaders=false] Include row headers.
   * @param {boolean} [options.exportHiddenColumns=false] Include hidden columns.
   * @param {boolean} [options.exportHiddenRows=false] Include hidden rows.
   * @param {number[]} [options.range=[]] Cell range: `[startRow, startColumn, endRow, endColumn]`.
   * @param {boolean|RegExp|Function} [options.sanitizeValues=false] Sanitization (CSV only).
   * @returns {Blob}
   */ exportAsBlob(format, options = {}) {
        const formatter = this._createTypeFormatter(format, options);
        if (formatter.binary) {
            (0, _errors.throwWithCause)(`exportAsBlob() does not support binary formats such as "${format}". ` + 'Use exportAsBlobAsync() instead.');
        }
        return this._createBlob(formatter);
    }
    /**
   * Exports table data as a blob object, asynchronously.
   *
   * Supports all export formats. For text-based formats (e.g. `'csv'`),
   * the `Promise` resolves immediately. For binary formats (e.g. `'xlsx'`),
   * the export runs asynchronously.
   *
   * @param {string} format Export format type e.g. `'csv'` or `'xlsx'`.
   * @param {object} options Export options.
   * @param {string} [options.mimeType] MIME type. Default depends on format.
   * @param {string} [options.fileExtension] File extension. Default depends on format.
   * @param {string} [options.filename='Handsontable [YYYY]-[MM]-[DD]'] File name.
   * @param {string} [options.encoding='utf-8'] Character encoding (text formats only).
   * @param {boolean} [options.bom] Include BOM signature (text formats only).
   * @param {string} [options.columnDelimiter=','] Column delimiter (CSV only).
   * @param {string} [options.rowDelimiter='\r\n'] Row delimiter (CSV only).
   * @param {boolean} [options.colHeaders=false] Include column headers.
   * @param {boolean} [options.rowHeaders=false] Include row headers.
   * @param {boolean} [options.exportHiddenColumns=false] Include hidden columns.
   * @param {boolean} [options.exportHiddenRows=false] Include hidden rows.
   * @param {number[]} [options.range=[]] Cell range: `[startRow, startColumn, endRow, endColumn]`.
   * @param {boolean|RegExp|Function} [options.sanitizeValues=false] Sanitization (CSV only).
   * @param {boolean} [options.exportFormulas=false] Export cell formulas instead of their computed values (XLSX only).
   * @param {boolean|number} [options.compression] Enable DEFLATE compression: `true` uses level 6; a number 1–9 sets a specific level. Omit or pass a falsy value to use no compression (XLSX only).
   * @param {ConditionalFormattingDescriptor[]} [options.conditionalFormatting=[]] Conditional formatting rules to apply to the exported file (XLSX only).
   * @param {SheetOptions[]} [options.sheets=[]] Configuration for multi-sheet export. Each entry defines one worksheet (XLSX only).
   * @returns {Promise<Blob>}
   * @since 17.1.0
   */ async exportAsBlobAsync(format, options = {}) {
        return this._createBlob(this._createTypeFormatter(format, options));
    }
    /**
   * Triggers a synchronous file download for text-based export formats (e.g. `'csv'`).
   *
   * Calling this method with a binary format (e.g. `'xlsx'`) throws an error — use
   * [[ExportFile#downloadFileAsync]] instead.
   *
   * @param {string} format Export format type e.g. `'csv'`.
   * @param {object} options Export options.
   * @param {string} [options.mimeType] MIME type. Default depends on format.
   * @param {string} [options.fileExtension] File extension. Default depends on format.
   * @param {string} [options.filename='Handsontable [YYYY]-[MM]-[DD]'] File name.
   * @param {string} [options.encoding='utf-8'] Character encoding.
   * @param {boolean} [options.bom] Include BOM signature. Default depends on format.
   * @param {string} [options.columnDelimiter=','] Column delimiter (CSV only).
   * @param {string} [options.rowDelimiter='\r\n'] Row delimiter (CSV only).
   * @param {boolean} [options.colHeaders=false] Include column headers.
   * @param {boolean} [options.rowHeaders=false] Include row headers.
   * @param {boolean} [options.exportHiddenColumns=false] Include hidden columns.
   * @param {boolean} [options.exportHiddenRows=false] Include hidden rows.
   * @param {number[]} [options.range=[]] Cell range: `[startRow, startColumn, endRow, endColumn]`.
   * @param {boolean|RegExp|Function} [options.sanitizeValues=false] Sanitization (CSV only).
   * @returns {void}
   */ downloadFile(format, options = {}) {
        const formatter = this._createTypeFormatter(format, options);
        if (formatter.binary) {
            (0, _errors.throwWithCause)(`downloadFile() does not support binary formats such as "${format}". ` + 'Use downloadFileAsync() instead.');
        }
        const name = `${formatter.options.filename}.${formatter.options.fileExtension}`;
        _class_private_method_get(this, _triggerDownload, triggerDownload).call(this, this._createBlob(formatter), name);
    }
    /**
   * Triggers an asynchronous file download. Supports all export formats.
   *
   * For text-based formats (e.g. `'csv'`), the download is triggered immediately
   * and the returned `Promise` resolves right away.
   * For binary formats (e.g. `'xlsx'`), the export runs asynchronously. When the
   * `dialog` plugin is enabled, a progress overlay is shown for the duration of the export.
   *
   * @param {string} format Export format type e.g. `'csv'` or `'xlsx'`.
   * @param {object} options Export options.
   * @param {string} [options.mimeType] MIME type. Default depends on format.
   * @param {string} [options.fileExtension] File extension. Default depends on format.
   * @param {string} [options.filename='Handsontable [YYYY]-[MM]-[DD]'] File name.
   * @param {string} [options.encoding='utf-8'] Character encoding (text formats only).
   * @param {boolean} [options.bom] Include BOM signature (text formats only).
   * @param {string} [options.columnDelimiter=','] Column delimiter (CSV only).
   * @param {string} [options.rowDelimiter='\r\n'] Row delimiter (CSV only).
   * @param {boolean} [options.colHeaders=false] Include column headers.
   * @param {boolean} [options.rowHeaders=false] Include row headers.
   * @param {boolean} [options.exportHiddenColumns=false] Include hidden columns.
   * @param {boolean} [options.exportHiddenRows=false] Include hidden rows.
   * @param {number[]} [options.range=[]] Cell range: `[startRow, startColumn, endRow, endColumn]`.
   * @param {boolean|RegExp|Function} [options.sanitizeValues=false] Sanitization (CSV only).
   * @param {boolean} [options.exportFormulas=false] Export cell formulas instead of their computed values (XLSX only).
   * @param {boolean|number} [options.compression] Enable DEFLATE compression: `true` uses level 6; a number 1–9 sets a specific level. Omit or pass a falsy value to use no compression (XLSX only).
   * @param {ConditionalFormattingDescriptor[]} [options.conditionalFormatting=[]] Conditional formatting rules to apply to the exported file (XLSX only).
   * @param {SheetOptions[]} [options.sheets=[]] Configuration for multi-sheet export. Each entry defines one worksheet (XLSX only).
   * @returns {Promise<void>}
   * @since 17.1.0
   */ async downloadFileAsync(format, options = {}) {
        const formatter = this._createTypeFormatter(format, options);
        const dialogPlugin = this.hot.getPlugin('dialog');
        const hasDialog = dialogPlugin?.isEnabled();
        const { rootWindow } = this.hot;
        const name = `${formatter.options.filename}.${formatter.options.fileExtension}`;
        const runExport = async ()=>{
            const blob = await Promise.resolve(this._createBlob(formatter));
            _class_private_method_get(this, _triggerDownload, triggerDownload).call(this, blob, name);
        };
        if (hasDialog && formatter.binary) {
            dialogPlugin.show({
                content: (0, _utils.buildExportDialogContent)(this.hot.getTranslatedPhrase(_constants1.EXPORT_FILE_DIALOG_TITLE)),
                customClassName: _constants.LOADING_CLASS_NAME,
                background: 'semi-transparent',
                closable: false,
                animation: false
            });
            // rAF fires at the START of a frame, before paint. A double-rAF lets the
            // browser complete one full paint cycle (dialog becomes visible) before
            // the export blocks the main thread.
            await new Promise((resolve)=>{
                rootWindow.requestAnimationFrame(()=>{
                    rootWindow.requestAnimationFrame(resolve);
                });
            });
            try {
                await runExport();
            } finally{
                dialogPlugin.hide();
            }
            return;
        }
        await runExport();
    }
    /**
   * Returns `true` when the plugin can produce an export in the given format.
   *
   * For text-based formats such as `'csv'`, no extra setup is required and the
   * method always returns `true`.
   * For binary formats such as `'xlsx'`, the method returns `true` only when the
   * corresponding engine has been provided in the plugin's `engines` map.
   *
   * @param {string} format Export format — `'csv'` or `'xlsx'`.
   * @returns {boolean}
   */ supportsExportFormat(format) {
        if (!_typeFactory.EXPORT_TYPES[format]) {
            return false;
        }
        if (format === 'xlsx') {
            const settings = this.hot.getSettings()[PLUGIN_KEY];
            return (0, _object.isObject)(settings) && (0, _object.isObject)(settings.engines) && Boolean(settings.engines.xlsx);
        }
        return true;
    }
    /**
   * Creates and returns a class formatter for the specified export type.
   *
   * The engine for the requested format is looked up from the plugin's `engines`
   * map and merged as a default so that per-call options can override it if needed.
   *
   * @private
   * @param {string} format Export format type eq. `'csv'` or `'xlsx'`.
   * @param {object} options Export options.
   * @returns {BaseType}
   */ _createTypeFormatter(format, options = {}) {
        if (!_typeFactory.EXPORT_TYPES[format]) {
            (0, _errors.throwWithCause)(`Export format type "${format}" is not supported.`);
        }
        const pluginSettings = this.hot.getSettings()[PLUGIN_KEY];
        const engines = (0, _object.isObject)(pluginSettings) && (0, _object.isObject)(pluginSettings.engines) ? pluginSettings.engines : undefined;
        const engineFromSettings = engines?.[format];
        const mergedOptions = engineFromSettings !== undefined ? {
            engine: engineFromSettings,
            ...options
        } : options;
        return (0, _typeFactory.default)(format, new _dataProvider.default(this.hot), mergedOptions);
    }
    /**
   * Creates a blob object from the provided type formatter.
   *
   * For synchronous formatters (e.g. CSV), returns a `Blob` directly.
   * For asynchronous formatters (e.g. XLSX), returns a `Promise<Blob>`.
   *
   * @private
   * @param {BaseType} typeFormatter The instance of the specific formatter/exporter.
   * @returns {Blob|Promise<Blob>}
   */ _createBlob(typeFormatter) {
        if (typeof Blob === 'undefined') {
            (0, _errors.throwWithCause)('Blob is not available in this environment.');
        }
        const exported = typeFormatter.export();
        if (typeof exported?.then === 'function') {
            return exported.then((buffer)=>new Blob([
                    buffer
                ], {
                    type: typeFormatter.options.mimeType
                }));
        }
        return new Blob([
            exported
        ], {
            type: `${typeFormatter.options.mimeType};charset=${typeFormatter.options.encoding}`
        });
    }
    constructor(...args){
        super(...args), _class_private_method_init(this, _onAfterContextMenuDefaultOptions), _class_private_method_init(this, _triggerDownload);
    }
}
function onAfterContextMenuDefaultOptions(options) {
    options.items.push({
        name: '---------'
    }, (0, _exportItem.default)(this));
}
function triggerDownload(blob, name) {
    const { rootDocument, rootWindow } = this.hot;
    const URL = rootWindow.URL || rootWindow.webkitURL;
    const url = URL.createObjectURL(blob);
    const a = rootDocument.createElement('a');
    a.style.display = 'none';
    a.setAttribute('href', url);
    a.setAttribute('download', name);
    rootDocument.body.appendChild(a);
    a.dispatchEvent(new MouseEvent('click'));
    rootDocument.body.removeChild(a);
    this.hot._registerTimeout(()=>{
        URL.revokeObjectURL(url);
    }, 100);
}
