/**
 * DevExtreme (cjs/__internal/grids/grid_core/search/m_search.js)
 * Version: 23.2.6 (build 24140-0112)
 * Build date: Sun May 19 2024
 *
 * Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.searchModule = void 0;
var _dom_adapter = _interopRequireDefault(require("../../../../core/dom_adapter"));
var _renderer = _interopRequireDefault(require("../../../../core/renderer"));
var _data = require("../../../../core/utils/data");
var _query = _interopRequireDefault(require("../../../../data/query"));
var _message = _interopRequireDefault(require("../../../../localization/message"));
var _m_utils = _interopRequireDefault(require("../m_utils"));

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    }
}

function _inheritsLoose(subClass, superClass) {
    subClass.prototype = Object.create(superClass.prototype);
    subClass.prototype.constructor = subClass;
    _setPrototypeOf(subClass, superClass)
}

function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function(o, p) {
        o.__proto__ = p;
        return o
    };
    return _setPrototypeOf(o, p)
}
const SEARCH_PANEL_CLASS = "search-panel";
const SEARCH_TEXT_CLASS = "search-text";
const HEADER_PANEL_CLASS = "header-panel";
const FILTERING_TIMEOUT = 700;

function allowSearch(column) {
    var _a;
    return !!(null !== (_a = column.allowSearch) && void 0 !== _a ? _a : column.allowFiltering)
}

function parseValue(column, text) {
    const {
        lookup: lookup
    } = column;
    if (!column.parseValue) {
        return text
    }
    if (lookup) {
        return column.parseValue.call(lookup, text)
    }
    return column.parseValue(text)
}
const dataController = base => function(_base) {
    _inheritsLoose(SearchDataControllerExtender, _base);

    function SearchDataControllerExtender() {
        return _base.apply(this, arguments) || this
    }
    var _proto = SearchDataControllerExtender.prototype;
    _proto.optionChanged = function(args) {
        switch (args.fullName) {
            case "searchPanel.text":
            case "searchPanel":
                this._applyFilter();
                args.handled = true;
                break;
            default:
                _base.prototype.optionChanged.call(this, args)
        }
    };
    _proto.publicMethods = function() {
        return _base.prototype.publicMethods.call(this).concat(["searchByText"])
    };
    _proto._calculateAdditionalFilter = function() {
        const filter = _base.prototype._calculateAdditionalFilter.call(this);
        const searchFilter = this.calculateSearchFilter(this.option("searchPanel.text"));
        return _m_utils.default.combineFilters([filter, searchFilter])
    };
    _proto.searchByText = function(text) {
        this.option("searchPanel.text", text)
    };
    _proto.calculateSearchFilter = function(text) {
        let i;
        let column;
        const columns = this._columnsController.getColumns();
        const searchVisibleColumnsOnly = this.option("searchPanel.searchVisibleColumnsOnly");
        let lookup;
        const filters = [];
        if (!text) {
            return null
        }

        function onQueryDone(items) {
            const valueGetter = (0, _data.compileGetter)(lookup.valueExpr);
            for (let i = 0; i < items.length; i++) {
                const value = valueGetter(items[i]);
                filters.push(column.createFilterExpression(value, null, "search"))
            }
        }
        for (i = 0; i < columns.length; i++) {
            column = columns[i];
            if (searchVisibleColumnsOnly && !column.visible) {
                continue
            }
            if (allowSearch(column) && column.calculateFilterExpression) {
                lookup = column.lookup;
                const filterValue = parseValue(column, text);
                if (lookup && lookup.items) {
                    (0, _query.default)(lookup.items).filter(column.createFilterExpression.call({
                        dataField: lookup.displayExpr,
                        dataType: lookup.dataType,
                        calculateFilterExpression: column.calculateFilterExpression
                    }, filterValue, null, "search")).enumerate().done(onQueryDone)
                } else if (void 0 !== filterValue) {
                    filters.push(column.createFilterExpression(filterValue, null, "search"))
                }
            }
        }
        if (0 === filters.length) {
            return ["!"]
        }
        return _m_utils.default.combineFilters(filters, "or")
    };
    return SearchDataControllerExtender
}(base);
const headerPanel = Base => function(_Base) {
    _inheritsLoose(SearchHeaderPanelExtender, _Base);

    function SearchHeaderPanelExtender() {
        return _Base.apply(this, arguments) || this
    }
    var _proto2 = SearchHeaderPanelExtender.prototype;
    _proto2.optionChanged = function(args) {
        if ("searchPanel" === args.name) {
            if ("searchPanel.text" === args.fullName) {
                const editor = this.getSearchTextEditor();
                if (editor) {
                    editor.option("value", args.value)
                }
            } else {
                this._invalidate()
            }
            args.handled = true
        } else {
            _Base.prototype.optionChanged.call(this, args)
        }
    };
    _proto2._getToolbarItems = function() {
        const items = _Base.prototype._getToolbarItems.call(this);
        return this._prepareSearchItem(items)
    };
    _proto2._prepareSearchItem = function(items) {
        const that = this;
        const dataController = this._dataController;
        const searchPanelOptions = this.option("searchPanel");
        if (searchPanelOptions && searchPanelOptions.visible) {
            const toolbarItem = {
                template(data, index, container) {
                    const $search = (0, _renderer.default)("<div>").addClass(that.addWidgetPrefix("search-panel")).appendTo(container);
                    that._editorFactoryController.createEditor($search, {
                        width: searchPanelOptions.width,
                        placeholder: searchPanelOptions.placeholder,
                        parentType: "searchPanel",
                        value: that.option("searchPanel.text"),
                        updateValueTimeout: 700,
                        setValue(value) {
                            dataController.searchByText(value)
                        },
                        editorOptions: {
                            inputAttr: {
                                "aria-label": _message.default.format("".concat(that.component.NAME, "-ariaSearchInGrid"))
                            }
                        }
                    });
                    that.resize()
                },
                name: "searchPanel",
                location: "after",
                locateInMenu: "never",
                sortIndex: 40
            };
            items.push(toolbarItem)
        }
        return items
    };
    _proto2.getSearchTextEditor = function() {
        const that = this;
        const $element = that.element();
        const $searchPanel = $element.find(".".concat(that.addWidgetPrefix("search-panel"))).filter((function() {
            return (0, _renderer.default)(this).closest(".".concat(that.addWidgetPrefix("header-panel"))).is($element)
        }));
        if ($searchPanel.length) {
            return $searchPanel.dxTextBox("instance")
        }
        return null
    };
    _proto2.isVisible = function() {
        const searchPanelOptions = this.option("searchPanel");
        return _Base.prototype.isVisible.call(this) || !!(null === searchPanelOptions || void 0 === searchPanelOptions ? void 0 : searchPanelOptions.visible)
    };
    return SearchHeaderPanelExtender
}(Base);
const rowsView = Base => function(_Base2) {
    _inheritsLoose(SearchRowsViewExtender, _Base2);

    function SearchRowsViewExtender() {
        return _Base2.apply(this, arguments) || this
    }
    var _proto3 = SearchRowsViewExtender.prototype;
    _proto3.init = function() {
        _Base2.prototype.init.apply(this, arguments);
        this._searchParams = [];
        this._dataController = this.getController("data")
    };
    _proto3.dispose = function() {
        clearTimeout(this._highlightTimer);
        _Base2.prototype.dispose.call(this)
    };
    _proto3._getFormattedSearchText = function(column, searchText) {
        const value = parseValue(column, searchText);
        const formatOptions = _m_utils.default.getFormatOptionsByColumn(column, "search");
        return _m_utils.default.formatValue(value, formatOptions)
    };
    _proto3._getStringNormalizer = function() {
        var _a, _b, _c, _d;
        const isCaseSensitive = this.option("searchPanel.highlightCaseSensitive");
        const dataSource = null === (_b = null === (_a = this._dataController) || void 0 === _a ? void 0 : _a.getDataSource) || void 0 === _b ? void 0 : _b.call(_a);
        const langParams = null === (_d = null === (_c = null === dataSource || void 0 === dataSource ? void 0 : dataSource.loadOptions) || void 0 === _c ? void 0 : _c.call(dataSource)) || void 0 === _d ? void 0 : _d.langParams;
        return str => (0, _data.toComparable)(str, isCaseSensitive, langParams)
    };
    _proto3._findHighlightingTextNodes = function(column, cellElement, searchText) {
        const that = this;
        let $parent = cellElement.parent();
        let $items;
        const stringNormalizer = this._getStringNormalizer();
        const normalizedSearchText = stringNormalizer(searchText);
        const resultTextNodes = [];
        if (!$parent.length) {
            $parent = (0, _renderer.default)("<div>").append(cellElement)
        } else if (column) {
            if (column.groupIndex >= 0 && !column.showWhenGrouped) {
                $items = cellElement
            } else {
                const columnIndex = that._columnsController.getVisibleIndex(column.index);
                $items = $parent.children("td").eq(columnIndex).find("*")
            }
        }
        $items = (null === $items || void 0 === $items ? void 0 : $items.length) ? $items : $parent.find("*");
        $items.each((_, element) => {
            var _a, _b;
            const $contents = (0, _renderer.default)(element).contents();
            for (let i = 0; i < $contents.length; i++) {
                const node = $contents.get(i);
                if (3 === node.nodeType) {
                    const normalizedText = stringNormalizer(null !== (_b = null !== (_a = node.textContent) && void 0 !== _a ? _a : node.nodeValue) && void 0 !== _b ? _b : "");
                    if (normalizedText.includes(normalizedSearchText)) {
                        resultTextNodes.push(node)
                    }
                }
            }
        });
        return resultTextNodes
    };
    _proto3._highlightSearchTextCore = function($textNode, searchText) {
        const that = this;
        const $searchTextSpan = (0, _renderer.default)("<span>").addClass(that.addWidgetPrefix("search-text"));
        const text = $textNode.text();
        const firstContentElement = $textNode[0];
        const stringNormalizer = this._getStringNormalizer();
        const index = stringNormalizer(text).indexOf(stringNormalizer(searchText));
        if (index >= 0) {
            if (firstContentElement.textContent) {
                firstContentElement.textContent = text.substr(0, index)
            } else {
                firstContentElement.nodeValue = text.substr(0, index)
            }
            $textNode.after($searchTextSpan.text(text.substr(index, searchText.length)));
            $textNode = (0, _renderer.default)(_dom_adapter.default.createTextNode(text.substr(index + searchText.length))).insertAfter($searchTextSpan);
            return that._highlightSearchTextCore($textNode, searchText)
        }
    };
    _proto3._highlightSearchText = function(cellElement, isEquals, column) {
        const that = this;
        const stringNormalizer = this._getStringNormalizer();
        let searchText = that.option("searchPanel.text");
        if (isEquals && column) {
            searchText = searchText && that._getFormattedSearchText(column, searchText)
        }
        if (searchText && that.option("searchPanel.highlightSearchText")) {
            const textNodes = that._findHighlightingTextNodes(column, cellElement, searchText);
            textNodes.forEach(textNode => {
                if (isEquals) {
                    if (stringNormalizer((0, _renderer.default)(textNode).text()) === stringNormalizer(null !== searchText && void 0 !== searchText ? searchText : "")) {
                        (0, _renderer.default)(textNode).replaceWith((0, _renderer.default)("<span>").addClass(that.addWidgetPrefix("search-text")).text((0, _renderer.default)(textNode).text()))
                    }
                } else {
                    that._highlightSearchTextCore((0, _renderer.default)(textNode), searchText)
                }
            })
        }
    };
    _proto3._renderCore = function() {
        const deferred = _Base2.prototype._renderCore.apply(this, arguments);
        if (this.option().rowTemplate || this.option("dataRowTemplate")) {
            if (this.option("templatesRenderAsynchronously")) {
                clearTimeout(this._highlightTimer);
                this._highlightTimer = setTimeout(() => {
                    this._highlightSearchText(this.getTableElement())
                })
            } else {
                this._highlightSearchText(this.getTableElement())
            }
        }
        return deferred
    };
    _proto3._updateCell = function($cell, parameters) {
        const {
            column: column
        } = parameters;
        const dataType = column.lookup && column.lookup.dataType || column.dataType;
        const isEquals = "string" !== dataType;
        if (allowSearch(column) && !parameters.isOnForm) {
            if (this.option("templatesRenderAsynchronously")) {
                if (!this._searchParams.length) {
                    clearTimeout(this._highlightTimer);
                    this._highlightTimer = setTimeout(() => {
                        this._searchParams.forEach(params => {
                            this._highlightSearchText.apply(this, params)
                        });
                        this._searchParams = []
                    })
                }
                this._searchParams.push([$cell, isEquals, column])
            } else {
                this._highlightSearchText($cell, isEquals, column)
            }
        }
        _Base2.prototype._updateCell.call(this, $cell, parameters)
    };
    return SearchRowsViewExtender
}(Base);
const searchModule = {
    defaultOptions: () => ({
        searchPanel: {
            visible: false,
            width: 160,
            placeholder: _message.default.format("dxDataGrid-searchPanelPlaceholder"),
            highlightSearchText: true,
            highlightCaseSensitive: false,
            text: "",
            searchVisibleColumnsOnly: false
        }
    }),
    extenders: {
        controllers: {
            data: dataController
        },
        views: {
            headerPanel: headerPanel,
            rowsView: rowsView
        }
    }
};
exports.searchModule = searchModule;
