$('[data-remove-image-button]').click(function () {
if (confirm("Удалить изображение?")) {
let removeBtn = $(this)[0];
let image = Dom.findParentChild(removeBtn, "[data-remove-image]");
let flag = Dom.findParentChild(removeBtn, "[data-remove-image-flag]");
let fileInputContainer = Dom.findParentChild(removeBtn, ".new-file-input");
let fileInput = $(fileInputContainer).find('input');
image.remove();
flag.value = "true";
let addNewFileBtn = Dom.findParentChild($(this)[0], ".upload-new-file-btn");
if (addNewFileBtn) {
$(addNewFileBtn).attr("hidden", true);
}
$(fileInputContainer).attr("hidden", false);
$(fileInputContainer).removeClass("mb-2");
removeBtn.parentElement.remove();
}
});
$(".upload-new-file-btn").click(function () {
let hide = !$(this).parent().find(".new-file-input").attr("hidden");
let fileInputContainer = $(this).parent().find(".new-file-input")[0];
let fileInput = $(this).parent().find(".new-file-input input")[0];
let isImage = $(fileInput).attr('accept').indexOf("image") > -1;
$(fileInputContainer).attr("hidden", hide);
$(this).parent().find(".file-path").attr("hidden", !hide);
$(this).text(hide ? (isImage ? "Загрузить новое изображение" : "Загрузить новый файл") : (isImage ? "Не загружать новое изображение" : "Не загружать новый файл"));
$(this).parent().find(".file-upload-text").html(hide ? "Файл загружен:" : "");
let removeBtn = Dom.findParentChild($(this)[0], ".remove-file-btn");
if (removeBtn) {
$(removeBtn).attr("hidden", !hide);
}
});
let elementLogicObjects = {};
$('.dropdown').each(function() {
if ($(this).hasClass('file-dropdown')) {
let selectFileElement = Dom.findParent($(this)[0], ".select-file");
if (selectFileElement) {
let fileDropdown = new FileDropdown(selectFileElement);
elementLogicObjects[selectFileElement] = fileDropdown;
}
} else {
let dropdownElement = $(this)[0];
let dropdown = new Dropdown(dropdownElement);
elementLogicObjects[dropdownElement] = dropdown;
}
});
function getElementLogicObject(element) {
if (element in elementLogicObjects) {
return elementLogicObjects[element];
}
return null;
}
const MODAL_WINDOW_BUTTON = {
BUTTON_1: "button_1"
}
async function showWindowModal(title, text, buttonText1 = "Yes", buttonText2 = "No",
modalDialogStyle = null) {
return new Promise((resolve) => {
// Кешируем селекторы модального окна, чтобы избежать повторных выборок
const $windowModal = $('#windowModal');
const $primaryBtn = $windowModal.find('.btn-primary');
const $secondaryBtn = $windowModal.find('.btn-secondary');
const $modalDialog = $windowModal.find('.modal-dialog');
$windowModal.find('.modal-title').text(title);
$windowModal.find('.modal-body').html(text);
$primaryBtn.text(buttonText1);
if (buttonText2 === null) {
$secondaryBtn.hide();
} else {
$secondaryBtn.text(buttonText2);
$secondaryBtn.show();
}
if (modalDialogStyle) {
$modalDialog.attr('style', modalDialogStyle);
} else {
$modalDialog.removeAttr('style');
}
$primaryBtn.off('click');
// Удаляем предыдущие handler'ы и добавляем новые. Используем локальный флаг
// чтобы гарантировать единственное резолвинг-промиса после завершения анимации скрытия (fade out).
$windowModal.off('hidden.bs.modal');
let buttonClicked = false;
// При клике на primary кнопку — только помечаем, что подтверждена кнопка и прячем модал.
// Не резолвим здесь; дождёмся события hidden.bs.modal (fade out complete).
$primaryBtn.on('click', function() {
buttonClicked = true;
$windowModal.modal('hide');
});
// Слушаем окончание скрытия модального окна (fade out complete).
$windowModal.on('hidden.bs.modal', function onHidden() {
// Снимаем обработчик сразу — он уже не нужен.
$windowModal.off('hidden.bs.modal', onHidden);
// Резолвим промис по флагу: BUTTON_1 если нажали кнопку, иначе null (отмена/закрытие).
if (buttonClicked) {
resolve(MODAL_WINDOW_BUTTON.BUTTON_1);
} else {
resolve(null);
}
});
$windowModal.modal('show');
});
}
const TOAST_TYPE = {
INFO: "INFO",
ERROR: "ERROR",
SUCCESS: "SUCCESS",
}
function showToast(text, type = TOAST_TYPE.INFO, closeButton = false, duration = 5000) {
let data = {
// node: text,
close: closeButton,
text: text,
duration: duration,
gravity: "top",
position: "right",
// onClick: function () { },
className: "",
};
switch (type) {
case TOAST_TYPE.ERROR:
data.style = {
background: "linear-gradient(to right, #ff5f6d, #e65c32ff)",
}
break;
case TOAST_TYPE.SUCCESS:
data.style = {
background: "linear-gradient(to right, #00b09b, #96c93d)",
}
break;
}
const toast = Toastify(data);
toast.showToast();
}
function showTableCompareModal() {
return new Promise(async (resolve, reject) => {
const modalContent = `
<div class="form-group">
<label for="compare-list">Список ФИО (каждое с новой строки):</label>
<textarea id="compare-list" class="form-control" rows="8"></textarea>
</div>
`.trim();
let answer = await showWindowModal('Сверка таблицы по ФИО', modalContent, 'Сверить', 'Отмена');
if (answer === MODAL_WINDOW_BUTTON.BUTTON_1) {
resolve({compareList: $('#compare-list').val()});
} else {
resolve(null);
}
});
}
function getCompareTableHtml(tableData, compareResult, copyTableColsElementId = null,
copyTableFiltersContainerElementSelector = null, dataTableOptions = null) {
if (!compareResult) {
return '<div>Нет данных для отображения</div>';
}
let compareTableId = 'compare-data-table-' + Date.now();
let filtersHtml = '';
let modifiedOptions = null;
let clonedFiltersContainer = null;
// Клонирование существующих фильтров (если передан селектор)
if (copyTableFiltersContainerElementSelector) {
const $origFilters = $(copyTableFiltersContainerElementSelector);
if ($origFilters && $origFilters.length) {
let $clone = $origFilters.clone(true, true);
const origContainerId = $clone.attr('id');
if (origContainerId) {
$clone.attr('id', 'compare_' + origContainerId);
}
$clone.find('[id]').each(function () {
const oldId = $(this).attr('id');
if (oldId && oldId.indexOf('compare_') !== 0) {
$(this).attr('id', 'compare_' + oldId);
}
});
clonedFiltersContainer = $clone; // сохраним для дальнейшего добавления нового фильтра
if (dataTableOptions) {
try {
modifiedOptions = JSON.parse(JSON.stringify(dataTableOptions));
if (modifiedOptions.dropdownFiltersConfig && Array.isArray(modifiedOptions.dropdownFiltersConfig.elementsData)) {
modifiedOptions.dropdownFiltersConfig.elementsData.forEach(ed => {
if (ed.dropdownId) ed.dropdownId = 'compare_' + ed.dropdownId;
if (ed.badgeId) ed.badgeId = 'compare_' + ed.badgeId;
});
}
} catch (e) {
console.error('Failed to clone dataTableOptions for compare modal', e);
}
}
}
}
// normalize структуры
const normalize = (res) => {
if (!res) return { findInOriginal: { foundStrings: [], notFoundStrings: [] }, notFoundOriginal: [], info: {} };
// already normalized
if (res.findInOriginal && (res.findInOriginal.foundStrings || res.findInOriginal.notFoundStrings)) {
return res;
}
// old backend format: findOriginalLinesResult + notFoundCompareLines
if (res.findOriginalLinesResult) {
const foundStrings = [];
const notFoundStrings = [];
const notFoundOriginal = [];
(res.findOriginalLinesResult || []).forEach(item => {
const originalLine = item.originalLine || '';
const foundCompareLine = item.foundCompareLine || null;
const similarityPercent = (item.similarityPercent !== undefined) ? item.similarityPercent : 0;
if (foundCompareLine) {
foundStrings.push({ compareLine: foundCompareLine, foundOriginalString: originalLine, similarityPercent: similarityPercent });
} else {
notFoundOriginal.push(originalLine);
}
});
(res.notFoundCompareLines || []).forEach(cl => {
notFoundStrings.push({ compareLine: cl, similarityPercent: 0 });
});
const info = res.info || {};
return {
findInOriginal: { foundStrings, notFoundStrings },
notFoundOriginal,
info: {
originalLinesCount: info.originalLinesCount || 0,
compareLinesCount: info.compareLinesCount || 0,
foundStringsCount: info.foundCompareLinesCount || foundStrings.length,
notFoundStringsCount: info.notFoundCompareLinesCount || notFoundStrings.length,
notFoundOriginalCount: (info.notFoundOriginalCount !== undefined) ? info.notFoundOriginalCount : notFoundOriginal.length,
foundCompareLinesCount: info.foundCompareLinesCount || 0,
foundCompareLinesPartiallyCount: info.foundCompareLinesPartiallyCount || 0,
// new counts introduced by updated StringUtils/compareStringArrays
foundCompareLinesGoodSimilarityCount: info.foundCompareLinesGoodSimilarityCount || 0,
foundCompareLinesBadSimilarityCount: info.foundCompareLinesBadSimilarityCount || 0,
minSimilarityPercentage: info.minSimilarityPercentage || 0
}
};
}
// fallback: best effort
const fio = res.findInOriginal || { foundStrings: [], notFoundStrings: [] };
return {
findInOriginal: { foundStrings: fio.foundStrings || [], notFoundStrings: fio.notFoundStrings || [] },
notFoundOriginal: res.notFoundOriginal || [],
info: Object.assign({
originalLinesCount: 0,
compareLinesCount: 0,
foundStringsCount: (fio.foundStrings || []).length,
notFoundStringsCount: (fio.notFoundStrings || []).length,
notFoundOriginalCount: (res.notFoundOriginal || []).length
}, res.info || {})
};
};
const normalized = normalize(compareResult);
const escapeHtml = (unsafe) => {
if (unsafe === null || unsafe === undefined) return '';
return String(unsafe)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
};
const findInOriginal = normalized.findInOriginal || { foundStrings: [], notFoundStrings: [] };
const found = findInOriginal.foundStrings || [];
const notFound = findInOriginal.notFoundStrings || [];
const notFoundOriginal = normalized.notFoundOriginal || [];
const info = normalized.info || {};
const minSimilarity = info.minSimilarityPercentage;
let html = '';
// Подготовка исходных колонок
let srcCols = [];
let srcRows = [];
let srcLookup = null;
const tableDataProvided = Array.isArray(tableData);
if (tableDataProvided) {
srcRows = tableData.map(row => {
if (!Array.isArray(row)) return [];
return row.map(cell => (cell === null || cell === undefined) ? '' : String(cell).trim());
});
// build lookup by column index 1 (as template uses tableData[i][1])
srcLookup = new Map();
for (let r = 0; r < srcRows.length; r++) {
const row = srcRows[r];
// prefer column index 1; if missing, index 0 or any non-empty
let key = '';
if (row.length > 1 && row[1]) {
key = row[1];
} else if (row.length > 0 && row[0]) {
key = row[0];
} else {
// find first non-empty
for (let c = 0; c < row.length; c++) {
if (row[c]) { key = row[c]; break; }
}
}
if (key) {
// if duplicate keys, keep first
if (!srcLookup.has(key)) srcLookup.set(key, row);
}
}
}
if (copyTableColsElementId) {
const $src = $('#' + copyTableColsElementId);
if ($src && $src.length) {
// always read headers into srcCols (we may still use tableData for rows)
$src.find('thead th').each(function () {
srcCols.push(String($(this).text().trim()));
});
// read tbody rows from DOM only if tableData was NOT provided
if (!tableDataProvided) {
$src.find('tbody tr').each(function () {
const rowArr = [];
$(this).find('td').each(function () {
rowArr.push(String($(this).text().trim()));
});
srcRows.push(rowArr);
});
}
}
}
// If no DOM source and no tableData provided, leave srcCols/srcRows empty (matching previous behaviour)
const compareCols = ['Номер п/п', 'Статус', 'ФИО сверки', 'ФИО таблицы', 'Процент совпадения, %'];
const finalCols = [];
srcCols.forEach(c => { if (!finalCols.includes(c)) finalCols.push(c); });
compareCols.forEach(c => { if (!finalCols.includes(c)) finalCols.push(c); });
// --- Новый фильтр "Статус сверки" ---
const statusColIndex = finalCols.indexOf('Статус');
const statusFilterDropdownId = 'compareStatusDropdown_' + compareTableId;
// HTML нового фильтра
const statusFilterHtml = `
<div class="dropdown-filter">
<label class="form-label">Статус сверки ФИО:</label>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" id="${statusFilterDropdownId}" data-toggle="dropdown" aria-expanded="false" data-multiple="false">Все статусы</button>
<div class="dropdown-menu" aria-labelledby="${statusFilterDropdownId}">
<a class="dropdown-item" href="#" data-filter="all">Все статусы</a>
<div class="dropdown-divider"></div>
<!-- Dynamic items will be added here -->
</div>
</div>
</div>`;
if (clonedFiltersContainer) {
// Добавляем новый фильтр в конец контейнера
clonedFiltersContainer.append($(statusFilterHtml));
filtersHtml = $('<div>').append(clonedFiltersContainer).html();
} else {
// Создаем новый контейнер только с фильтром статуса
filtersHtml = `<div id="compare-filters-${compareTableId}" class="mb-3 d-flex align-items-center" style="gap:20px; flex-wrap:wrap;">${statusFilterHtml}</div>`;
}
// Обновляем / создаём конфиг options для CustomDataTable
if (!modifiedOptions) {
modifiedOptions = {
dropdownFiltersConfig: { elementsData: [] },
drawRowNumbersForColIndex: 0,
lengthMenu: [[10, 20, 50, 100, -1], [10, 20, 50, 100, 'Все']]
};
}
if (!modifiedOptions.dropdownFiltersConfig) {
modifiedOptions.dropdownFiltersConfig = { elementsData: [] };
}
if (!Array.isArray(modifiedOptions.dropdownFiltersConfig.elementsData)) {
modifiedOptions.dropdownFiltersConfig.elementsData = [];
}
if (statusColIndex !== -1) {
modifiedOptions.dropdownFiltersConfig.elementsData.push({
columnIndex: statusColIndex,
dropdownId: statusFilterDropdownId,
badgeId: null,
textForAll: 'Все статусы'
});
}
// Вставляем фильтры перед сводкой
if (filtersHtml) {
html += '<div class="mb-3">' + filtersHtml + '</div>';
}
// Сводка
html += '<div class="mb-3">';
html += '<strong>Всего в таблице:</strong> ' + escapeHtml(info.originalLinesCount || 0) + '<br/>';
html += '<strong>Всего для сверки:</strong> ' + escapeHtml(info.compareLinesCount || 0) + '<br/>';
html += '<strong>Найдено совпадений:</strong> ' + escapeHtml(info.foundStringsCount || found.length) + '<br/>';
html += '<strong>Частично найдено (более ' + minSimilarity + '% совпадения):</strong> ' + (info.foundCompareLinesGoodSimilarityCount || 0) + '<br/>';
html += '<strong>Частично найдено (до ' + minSimilarity + '% совпадения):</strong> ' + (info.foundCompareLinesBadSimilarityCount || 0) + '<br/>';
html += '<strong>Частично найдено всего:</strong> ' + ((info.foundCompareLinesBadSimilarityCount + info.foundCompareLinesGoodSimilarityCount) || 0) + '<br/>';
html += '<strong>Не найдено в таблице:</strong> ' + notFound.length + '<br/>';
html += '<strong>Не найдено в списке сверки:</strong> ' + (info.notFoundOriginalCount || notFoundOriginal.length) + '<br/>';
html += '</div>';
html += '<h6>Результаты сверки</h6>';
// Кнопка скачивания CSV
const downloadBtnId = 'download-csv-btn-' + compareTableId;
html += '<div class="mb-2 d-flex justify-content-end">';
html += '<button id="' + downloadBtnId + '" type="button" class="btn btn-secondary btn-sm">Скачать таблицу</button>';
html += '</div>';
html += '<div class="table-responsive"><table id="' + compareTableId + '" class="table table-sm table-bordered">';
html += '<thead><tr>';
finalCols.forEach(col => { html += '<th>' + col + '</th>'; });
html += '</tr></thead><tbody>';
let rowIndex = 0;
const findSrcRowByValue = (value) => {
if (!value) return null;
const valNorm = String(value).trim();
// if we have a lookup built from tableData, use it and return only rows from tableData
if (srcLookup) {
if (srcLookup.has(valNorm)) return srcLookup.get(valNorm);
return null; // do NOT search DOM or other columns when tableData provided
}
for (let r = 0; r < srcRows.length; r++) {
for (let c = 0; c < srcRows[r].length; c++) {
if (srcRows[r][c] === valNorm) return srcRows[r];
}
}
return null;
};
const renderRow = (cells, extraClass) => {
html += '<tr' + (extraClass ? ' class="' + extraClass + '"' : '') + '>';
finalCols.forEach(fc => {
const val = (fc in cells) ? cells[fc] : '';
html += '<td>' + (val === null || val === undefined ? '' : val) + '</td>';
});
html += '</tr>';
};
const mapSrcRowToObj = (srcRowArr) => {
const obj = {};
srcCols.forEach((h, idx) => { obj[h] = srcRowArr[idx] || ''; });
return obj;
};
if (tableDataProvided && srcRows.length) {
// build map from original value -> found item
const foundByOriginal = new Map();
found.forEach(it => {
const key = (it.foundOriginalString || '').trim();
if (key) foundByOriginal.set(key, it);
});
// iterate original rows in order
for (let r = 0; r < srcRows.length; r++) {
const srcRow = srcRows[r];
// derive key from same logic as srcLookup
let key = '';
if (srcRow.length > 1 && srcRow[1]) key = srcRow[1];
else if (srcRow.length > 0 && srcRow[0]) key = srcRow[0];
else {
for (let c = 0; c < srcRow.length; c++) { if (srcRow[c]) { key = srcRow[c]; break; } }
}
rowIndex++;
const cells = mapSrcRowToObj(srcRow);
const foundItem = key ? foundByOriginal.get(key) : null;
if (foundItem) {
cells['Номер п/п'] = escapeHtml(rowIndex);
const pct = (foundItem.similarityPercent !== undefined && foundItem.similarityPercent !== null) ? Number(foundItem.similarityPercent) : 0;
let statusText = '';
if (pct === 100) {
statusText = 'Найдено';
} else if (pct > minSimilarity) {
statusText = 'Частично найден в таблице - более ' + minSimilarity + '% совпадение';
} else {
statusText = 'Частично найден в таблице - менее ' + minSimilarity + '% совпадение';
}
cells['Статус'] = statusText;
cells['ФИО сверки'] = escapeHtml(foundItem.compareLine);
cells['ФИО таблицы'] = escapeHtml(foundItem.foundOriginalString || key);
cells['Процент совпадения, %'] = (foundItem.similarityPercent !== undefined && foundItem.similarityPercent !== null) ? escapeHtml(foundItem.similarityPercent) : '';
const rowClass = (pct === 100) ? 'table-success' : 'table-warning';
renderRow(cells, rowClass);
foundByOriginal.delete(key);
} else {
// red row - not found in compare
cells['Номер п/п'] = escapeHtml(rowIndex);
cells['Статус'] = 'Не найдено в списке сверки';
cells['ФИО сверки'] = '';
cells['ФИО таблицы'] = escapeHtml(key);
cells['Процент совпадения, %'] = '';
renderRow(cells, 'table-danger');
}
}
// render remaining compare lines that weren't matched to any original
// these are in notFound OR leftover in foundByOriginal if any
// first render compare items from notFound (explicitly unmatched)
notFound.forEach(item => {
rowIndex++;
let cells = {};
cells['Номер п/п'] = escapeHtml(rowIndex);
cells['Статус'] = 'Не найдено в таблице';
cells['ФИО сверки'] = escapeHtml(item.compareLine);
cells['ФИО таблицы'] = '';
cells['Процент совпадения, %'] = (item.similarityPercent !== undefined && item.similarityPercent !== null) ? escapeHtml(item.similarityPercent) : '';
// use blue for notFound compare lines
renderRow(cells, 'table-info');
});
// any leftover found items that weren't matched by key (edge cases)
if (foundByOriginal.size) {
foundByOriginal.forEach(it => {
rowIndex++;
let cells = {};
cells['Номер п/п'] = escapeHtml(rowIndex);
const pct2 = (it.similarityPercent !== undefined && it.similarityPercent !== null) ? Number(it.similarityPercent) : 0;
let statusText2 = '';
if (pct2 === 100) {
statusText2 = 'Найдено';
} else if (pct2 > minSimilarity) {
statusText2 = 'Частично найден в таблице - более ' + minSimilarity + '% совпадение';
} else {
statusText2 = 'Частично найден в таблице - менее ' + minSimilarity + '% совпадение';
}
cells['Статус'] = statusText2;
cells['ФИО сверки'] = escapeHtml(it.compareLine);
cells['ФИО таблицы'] = escapeHtml(it.foundOriginalString || '');
cells['Процент совпадения, %'] = (it.similarityPercent !== undefined && it.similarityPercent !== null) ? escapeHtml(it.similarityPercent) : '';
const rowClass2 = (pct2 === 100) ? 'table-success' : 'table-warning';
renderRow(cells, rowClass2);
});
}
} else {
// fallback original behaviour: show found, then notFound, then notFoundOriginal
found.forEach(item => {
rowIndex++;
const compareString = escapeHtml(item.compareLine);
const foundOriginal = escapeHtml(item.foundOriginalString);
const percent = (item.similarityPercent !== undefined && item.similarityPercent !== null) ? escapeHtml(item.similarityPercent) : '';
let cells = {};
if (srcCols.length && foundOriginal) {
const srcRow = findSrcRowByValue(foundOriginal);
if (srcRow) Object.assign(cells, mapSrcRowToObj(srcRow));
}
const pctItem = (item.similarityPercent !== undefined && item.similarityPercent !== null) ? Number(item.similarityPercent) : 0;
let statusTextItem = '';
if (pctItem === 100) {
statusTextItem = 'Найдено';
} else if (pctItem > minSimilarity) {
statusTextItem = 'Частично найден в таблице - более ' + minSimilarity + '% совпадение';
} else {
statusTextItem = 'Частично найден в таблице - менее ' + minSimilarity + '% совпадение';
}
cells['Номер п/п'] = escapeHtml(rowIndex);
cells['Статус'] = statusTextItem;
cells['ФИО сверки'] = compareString;
cells['ФИО таблицы'] = foundOriginal;
cells['Процент совпадения, %'] = percent;
const rowClassItem = (pctItem === 100) ? 'table-success' : 'table-warning';
renderRow(cells, rowClassItem);
});
notFound.forEach(item => {
rowIndex++;
const compareString = escapeHtml(item.compareLine);
const percent = (item.similarityPercent !== undefined && item.similarityPercent !== null) ? escapeHtml(item.similarityPercent) : '';
let cells = {};
cells['Номер п/п'] = escapeHtml(rowIndex);
cells['Статус'] = 'Не найдено в таблице';
cells['ФИО сверки'] = compareString;
cells['ФИО таблицы'] = '';
cells['Процент совпадения, %'] = percent;
renderRow(cells, 'table-danger');
});
notFoundOriginal.forEach(item => {
rowIndex++;
const original = escapeHtml(item);
let cells = {};
if (srcCols.length) {
const srcRow = findSrcRowByValue(item);
if (srcRow) Object.assign(cells, mapSrcRowToObj(srcRow));
}
cells['Номер п/п'] = escapeHtml(rowIndex);
cells['Статус'] = 'Не найдено в списке сверки';
cells['ФИО сверки'] = '';
cells['ФИО таблицы'] = original;
cells['Процент совпадения, %'] = '';
renderRow(cells, 'table-secondary');
});
}
// --- New: ensure initialization script with modifiedOptions ---
if (modifiedOptions) {
if (modifiedOptions.drawRowNumbersForColIndex === undefined) {
modifiedOptions.drawRowNumbersForColIndex = 0;
}
html += '</tbody></table></div>';
html += '<script>(function(){try{var opts=' + JSON.stringify(modifiedOptions) + ';var table=new CustomDataTable("#' + compareTableId + '",opts);table.initDataTable();var btn=document.getElementById("' + downloadBtnId + '");if(btn){btn.addEventListener("click",function(){table.downloadCsv("compare_table_export.csv");});}}catch(e){console.error("Failed to initialize compare CustomDataTable",e);}})();</'+'script>';
} else {
html += '</tbody></table></div>';
}
return html;
}
function compareData(data1, data2) {
return new Promise((resolve, reject) => {
Utils.fetch(API_COMPARE_DATA_URL, 'POST', { data1: data1, data2: data2 })
.then(async (response) => {
console.log(response);
resolve(response.response);
})
.catch((response) => {
showToast("Ошибка. Не удалось провести сравнение. Попробуйте повторить позднее.",
TOAST_TYPE.ERROR);
console.log(response);
reject(response);
});
});
}
function updateDependentSelect(controllerSelector, $dependent) {
let selectedControllerVal = $(controllerSelector).val();
let optionAttrName = $dependent.data('controlling-select-option-attr-name');
$dependent.find('option').each(function() {
let $opt = $(this);
let optAttrVal = $opt.attr(optionAttrName);
if (optAttrVal == selectedControllerVal || $opt.val() == -1) {
$opt.show();
} else {
$opt.hide();
}
});
if ($dependent.find('option:selected').attr(optionAttrName) != selectedControllerVal) {
$dependent.val(-1);
}
}
$('[data-controlling-select-selector]').each(function() {
let $dependent = $(this);
let controllerSelector = $dependent.data('controlling-select-selector');
if (!controllerSelector) return;
$(controllerSelector).on('change', function() {
updateDependentSelect(controllerSelector, $dependent);
});
if ($(controllerSelector).length) {
updateDependentSelect(controllerSelector, $dependent);
}
});