HEX
Server: nginx/1.18.0
System: Linux test-ipsremont 5.4.0-214-generic #234-Ubuntu SMP Fri Mar 14 23:50:27 UTC 2025 x86_64
User: ips (1000)
PHP: 8.0.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/html/laravel/public/js/base.js
let initForm = function () {
    return {
        inputEditor: null,
        outputEditor: null,
        localOutputEditor: null,
        correctAnswerEditor: null,
        editPrompt: false,
        editExamples: false,
        message: '',
        output: '',
        localOutput: '',
        notamId: 0,
        totalTokens: 0,
        inputErrors: 0,
        correctionErrors: 0,
        chatGptErrors: 0,
        init: function () {
            var container = document.getElementById("inputEditor");
            let self = this;
            const options = {
                allowSchemaSuggestions: true,
                language: 'ru',
                mode: 'code',
                onValidationError: function (errors) {
                    self.inputErrors = errors.length;
                },
                onValidate: function (json) {
                    return validatePeriods(json, [], '');
                }
            }
            var json = '';
            if (container) {
                this.inputEditor = new JSONEditor(container, options, json);
            }
        },
        sendData: function () {
            if (this.inputErrors) {
                alert('Ошибки в поле ввода');
                return;
            }

            let data = jQuery('form#mainForm').serialize();


            this.message = 'Загружаю результат...';
            this.output = 'Обработка';
            this.notamId = 0;
            this.totalTokens = 0;
            let self = this;

            showLoader('#gptPanelBlock');
            $.ajax({
                type: 'POST',
                url: '/sendNotamRequest',
                data: data,
                success: function (result) {
                    document.getElementById("outputEditor").innerHTML = '';
                    document.getElementById("correctAnswerEditor").innerHTML = '';
                    if (result.status === 'error') {
                        self.message = result.message;
                    } else {
                        self.output = result.message.replaceAll('\"', '"');
                        self.message = '';
                        let container = document.getElementById("outputEditor"),
                            options = {
                                language: 'ru',
                                mode: 'view',
                                onValidationError: function (errors) {
                                    self.chatGptErrors = errors.length;
                                },
                                onValidate: function (json) {
                                    return validatePeriods(json, [], '');
                                }
                            },
                            chatGPTresponse = JSON.parse(self.output);

                        self.outputEditor = new JSONEditor(container, options, chatGPTresponse);

                        let correctAnswer = document.getElementById('correctAnswerEditor'),
                            correctAnswerEditorOptions = {
                                allowSchemaSuggestions: true, schema: window.jsonSchema,
                                language: 'ru',
                                mode: 'code',

                                onValidationError: function (errors) {
                                    self.correctionErrors = errors.filter((error) => error.type === 'error').length;
                                },
                                onValidate: function (json) {
                                    return validatePeriods(json, [], '');
                                }
                            };
                        self.correctAnswerEditor = new JSONEditor(correctAnswer, correctAnswerEditorOptions, chatGPTresponse);

                        if (result.notamId) {
                            self.notamId = result.notamId;
                            if (SECOND_PROMT_ID && document.getElementById('prompt_id').value !== SECOND_PROMT_ID) {
                                self.localOutput = 'Обработка';
                                data += "&prompt_id="+SECOND_PROMT_ID+"&notam_id=" + encodeURIComponent(result.notamId);
                                self.sendNotamToLocalModel(data);
                            }
                        }
                        if (result.totalTokens) {
                            self.totalTokens = result.totalTokens;
                        }
                    }
                    jQuery('#gptPanelBlock').unblock();

                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        sendNotamToLocalModel: function (data) {
            showLoader('#localPanelBlock');
            $.ajax({
                type: 'POST',
                url: '/sendNotamRequest',
                data: data,
                success: function (result) {
                    document.getElementById("localOutputEditor").innerHTML = '';
                    if (result.status === 'error') {
                        self.message = result.message;
                    } else {
                        self.localOutput = result.message.replaceAll('\"', '"');
                        self.message = '';
                        let chatGPTresponse = JSON.parse(self.localOutput);
                        let correctAnswer = document.getElementById('localOutputEditor'),
                            localOutputEditorOptions = {
                                allowSchemaSuggestions: true,
                                schema: window.jsonSchema,
                                language: 'ru',
                                mode: 'code',
                                onValidationError: function (errors) {
                                    self.correctionErrors = errors.filter((error) => error.type === 'error').length;
                                },
                                onValidate: function (json) {
                                    return validatePeriods(json, [], '');
                                }
                            };
                        self.localOutputEditor = new JSONEditor(correctAnswer, localOutputEditorOptions, chatGPTresponse);

                        if (result.notamId) {
                            self.notamId = result.notamId;
                        }
                        if (result.totalTokens) {
                            self.totalTokens = result.totalTokens;
                        }
                    }
                    jQuery('#localPanelBlock').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });

        },
        sendMel: function () {
            jQuery('form#mainForm #inputData').html(this.inputEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));
            let data = jQuery('form#mainForm').serialize();

            showLoader();

            this.message = 'Загружаю результат...';
            this.output = '';
            this.melId = 0;
            this.totalTokens = 0;
            let self = this;

            $.ajax({
                type: 'POST',
                url: '/sendMel',
                data: data,
                success: function (result) {
                    document.getElementById("outputEditor").innerHTML = '';
                    document.getElementById("correctAnswerEditor").innerHTML = '';
                    if (result.status === 'error') {
                        self.message = result.message;
                    } else {
                        self.output = result.message.replaceAll('\"', '"');
                        self.message = '';
                        let container = document.getElementById("outputEditor"),
                            options = {
                                language: 'ru',
                                mode: 'view',
                                onValidationError: function (errors) {
                                    self.chatGptErrors = errors.length;
                                },
                                onValidate: function (json) {
                                    return validatePeriods(json, [], '');
                                }
                            },
                            chatGPTresponse = JSON.parse(self.output);

                        self.outputEditor = new JSONEditor(container, options, chatGPTresponse);

                        let correctAnswer = document.getElementById('correctAnswerEditor'),
                            correctAnswerEditorOptions = {
                                schema: window.jsonSchema,
                                language: 'ru',
                                mode: 'code',
                                modes: ['code', 'tree'],
                                onValidationError: function (errors) {
                                    self.correctionErrors = errors.filter((error) => error.type === 'error').length;
                                },
                                onValidate: function (json) {
                                    return validatePeriods(json, [], '');
                                }
                            };
                        self.correctAnswerEditor = new JSONEditor(correctAnswer, correctAnswerEditorOptions, chatGPTresponse);

                        if (result.melId) {
                            self.melId = result.melId;
                        }
                        if (result.totalTokens) {
                            self.totalTokens = result.totalTokens;
                        }
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        validateNotam: function (isNew) {
            if (this.correctionErrors) {
                alert('Ошибки в поле коррекции ответа');
                return;
            }
            if (this.chatGptErrors) {
                alert('Ошибки в поле ответа chatGPT');
                return;
            }

            jQuery('form#mainForm #inputData').html(this.inputEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));
            jQuery('form#mainForm #correctData').html(this.correctAnswerEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));
            jQuery('form#mainForm #chatGptData').html(this.outputEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));

            if (isNew) {
                document.getElementById('notamId').value = 0;
            }

            let self = this,
                data = jQuery('form#mainForm').serialize();


            $.ajax({
                type: 'POST',
                url: '/validateNotam',
                data: data,
                success: function (result) {
                    alert(result.message);

                    if (result.notamId) {
                        self.notamId = result.notamId;
                    }
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        validateMel: function (isNew) {
//            if (this.correctionErrors) {
//                alert('Ошибки в поле коррекции ответа');
//                return;
//            }
//            if (this.chatGptErrors) {
//                alert('Ошибки в поле ответа chatGPT');
//                return;
//            }

            jQuery('form#mainForm #inputData').html(this.inputEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));
            jQuery('form#mainForm #correctData').html(this.correctAnswerEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));
            jQuery('form#mainForm #chatGptData').html(this.outputEditor.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""));

            if (isNew) {
                document.getElementById('melId').value = 0;
            }

            let self = this,
                data = jQuery('form#mainForm').serialize();


            $.ajax({
                type: 'POST',
                url: '/validateMel',
                data: data,
                success: function (result) {
                    alert(result.message);

                    if (result.melId) {
                        self.melId = result.melId;
                    }
                },
                error: function (response) {
                    console.log(response);
                },
            });
        }
    }
}

let initLogs = function () {
    return {
        logs: {},
        searchTimeout: null,
        filters: {
            page: 1,
            searchText: '',
            fine_tuning: '',
            check_needed: '',
            validation_errors: '',
            actual: '',
            geo_validation_error: '',
            sort: 'start_date'
        },
        pagination: {},
        pages: [],
        offset: 4,
        stats: [],
        init: function () {
            let self = this;
            this.logs = window.logs;
            this.filters = window.filters;
            this.pagination = this.logs;
            this.stats = window.stats;

            this.refreshStats('notam');
            setInterval(function () {
                self.refreshStats('notam');
            }, 10000);
        },
        refreshStats: function (type) {
            let self = this;
            $.ajax({
                type: 'GET',
                url: '/notam/refreshStats',
                success: function (result) {
                    self.stats = result.stats;
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        copyToLocal: function (log) {
            let gptEditor = window.editorsCollection[log.id]['correctResponse'];
            let localEditor = window.editorsCollection[log.id]['localCorrectResponse'];
            let gpt41Editor =  window.editorsCollection[log.id]['gpt41_correctResponse'];
            text = gptEditor.getText();
            gpt41Editor.updateText(text);
            jQuery('#' + log.id + '-gpt41_correctResponse').val(text);

        },
        copyToGpt: function (log) {
            let gptEditor = window.editorsCollection[log.id]['correctResponse'];
            let localEditor = window.editorsCollection[log.id]['localCorrectResponse'];
            let gpt41Editor =  window.editorsCollection[log.id]['gpt41_correctResponse'];
            text = gpt41Editor.getText();
            gptEditor.updateText(text);
            jQuery('#' + log.id + '-correctResponse').val(text);
        },
        pagesNumber() {
            if (!this.pagination.to) {
                return [];
            }
            let from = this.pagination.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            let to = from + (this.offset * 2);
            if (to >= this.pagination.last_page) {
                to = this.pagination.last_page;
            }
            let pagesArray = [];
            for (let page = from; page <= to; page++) {
                pagesArray.push(page);
            }

            return pagesArray;
        },
        changePage(number) {
            let self = this;
            number = number ? number : localStorage.getItem('current_page', number);
            document.location.href = '?' + this.buildUrl(number);
        },
        buildUrl(page = 1) {
            this.filters.page = page;
            const params = new URLSearchParams(this.filters);
            return params.toString();
        },
        searchLogs: function (page = 1) {
            // TODO: rebuild to ajax
            // this.sendAjax('/mel/searchMels', {'_token': $('meta[name="csrf-token"]').attr('content'), filters: this.filters}, 'POST');
            showLoader();
            if (!page) {
                page = this.filters.page;
            }
            document.location.href = '?' + this.buildUrl(page);
        },
        deleteLog: function (logId, page) {
            showLoader();
            let self = this;
            let data = {logId: logId, page: page, filters: filters, '_token': CSRF_TOKEN};

            $.ajax({
                type: 'POST',
                url: '/deleteLog',
                data: data,
                success: function (result) {
                    self.logs = result.logs;
                    self.pagination = self.logs;
                    setTimeout(function () {
                        window.buildEditors();
                    }, 2000);
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        initMaps: function () {
            for (var key in Object.entries(this.logs.data)) {
                if (this.logs.data[key].content) {
                    if (this.logs.data[key].content && typeof JSON.parse(this.logs.data[key].content) === 'object') {
                        let json = JSON.parse(this.logs.data[key].content);
                        let id = 'map-' + this.logs.data[key].id;
                        setTimeout(function () {
                            document.dispatchEvent(new CustomEvent('init-map', {detail: {'id': id, 'json': json}}));
                        }, 500);
                    }
                }
            }
        },
        updateCorrectResponse: function (notamId) {

            let correctResponse = jQuery('#' + notamId + '-correctResponse').val(),
                localCorrectResponse = jQuery('#' + notamId + '-localCorrectResponse').val(),
                gpt41CorrectResponse = jQuery('#' + notamId + '-gpt41_correctResponse').val(),
                data = {
                    '_token': $('meta[name="csrf-token"]').attr('content'), id: notamId, filters: this.filters,
                    correctResponse: correctResponse, localCorrectResponse: localCorrectResponse, gpt41CorrectResponse: gpt41CorrectResponse
                };

            if (!this.validateJson(correctResponse)) {
                alert('Невалидный JSON в поле "GPT-4o"');
                return;
            }
            if (localCorrectResponse && !this.validateJson(localCorrectResponse)) {
                alert('Невалидный JSON в поле "Ответ локальной модели"');
                return;
            }

            if (gpt41CorrectResponse && !this.validateJson(gpt41CorrectResponse)) {
                alert('Невалидный JSON в поле "GPT-4.1"');
                return;
            }

            this.sendAjax('/notam/updateCorrectResponse', data, 'POST');
        },
        retry: function (notamId) {
            this.sendAjax('/notam/retry/' + notamId, {}, 'GET');
        },
        validateJson: function (json) {
            try {
                JSON.parse(json);
                return true;
            } catch (e) {
                return false;
            }
        },
        sendAjax: function (url, body, method = 'POST') {
            let self = this;

            return $.ajax({
                type: method,
                url: url,
                data: body,
                success: function (result) {
                    if (result.logs) {
                        self.logs = result.logs;
                        self.pagination = self.logs;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    }

                    if (result.message) {
                        let messageType = 'danger';
                        if (result.status) {
                            messageType = result.status;
                        }
                        dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: messageType, text: result.message}]}}));
                    } else {
                        dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: 'success', text: 'Ответ сохранен'}]}}));
                    }

                    return result;
                },
            });
        },
    }
}

let initMels = function () {
    return {
        logs: {},
        searchTimeout: null,
        filters: {
            page: 1,
            searchText: '',
            fine_tuning: '',
            check_needed: '',
            applied_for_tuning: '',
            actual: '',
            sort: 'start_date'
        },
        pagination: {},
        pages: [],
        offset: 4,
        importInProgress: false,
        importMessage: '',
        pdfFiles: {},
        showFiles: false,
        init: function () {
            this.logs = window.logs;
            this.filters = window.filters;
            this.pagination = this.logs;
            this.pdfFiles = window.pdfFiles;
        },
        pagesNumber() {
            if (!this.pagination.to) {
                return [];
            }
            let from = this.pagination.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            let to = from + (this.offset * 2);
            if (to >= this.pagination.last_page) {
                to = this.pagination.last_page;
            }
            let pagesArray = [];
            for (let page = from; page <= to; page++) {
                pagesArray.push(page);
            }

            return pagesArray;
        },
        changePage(number) {
            let self = this;
            number = number ? number : localStorage.getItem('current_page', number);
            this.pagination.current_page = number;
            this.searchMels(number);
        },
        buildUrl(page = 1) {
            const params = new URLSearchParams(this.filters);
            return params.toString();
        },
        searchMels: function (page = 1) {
            this.filters.page = page;
            this.sendAjax('/mel/searchMels', {'_token': $('meta[name="csrf-token"]').attr('content'), filters: this.filters}, 'POST');
        },
        importFile: function () {
            let fileInput = document.getElementById('pdf-file');
            this.importInProgress = true;
            this.importMessage = '';

            if (!fileInput.value) {
                this.importMessage = 'Выберите файл для импорта';
                this.importInProgress = false;
            }

            let form = document.getElementById('importForm'),
                data = new FormData(form);

            let self = this;

            $.ajax({
                type: 'POST',
                url: '/mel/processFile',
                data: data,
                processData: false, // prevent jQuery from processing data
                contentType: false, // prevent jQuery from setting content type
                success: function (result) {
                    if (result.logs) {
                        self.logs = result.logs;
                        self.pagination = self.logs;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    }

                    if (result.pdfFiles) {
                        self.pdfFiles = result.pdfFiles;
                    }
                    return result;
                },
            }).then((result) => {
                result = JSON.parse(result);
                if (result.message) {
                    this.importMessage = result.message;
                }
                this.importInProgress = false;
            });
        },
        updateContent: function (melId) {
            this.filters.page = 1;
            let content = jQuery('#' + melId + '-content').val(),
                data = {'_token': $('meta[name="csrf-token"]').attr('content'), id: melId, content: content};

            this.sendAjax('/mel/updateContent', data, 'POST');
        },
        updateCorrectResponse: function (melId) {
            let correctResponse = jQuery('#' + melId + '-correctResponse').val(),
                data = {'_token': $('meta[name="csrf-token"]').attr('content'), id: melId, correctResponse: correctResponse};

            this.sendAjax('/mel/updateCorrectResponse', data, 'POST');
        },
        confirmMel: function (melId) {
            let data = {melId: melId, filters: this.filters, '_token': $('meta[name="csrf-token"]').attr('content')};
            this.sendAjax('/mel/confirmMel', data, 'POST');
        },
        deleteMel: function (melId) {
            let data = {melId: melId, filters: this.filters, '_token': $('meta[name="csrf-token"]').attr('content')};
            this.sendAjax('/mel/deleteMel', data, 'POST');
        },
        sendAjax: function (url, body, method = 'POST') {
            let self = this;

            return $.ajax({
                type: method,
                url: url,
                data: body,
                success: function (result) {
                    if (result.logs) {
                        self.logs = result.logs;
                        self.pagination = self.logs;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    }

                    if (result.pdfFiles) {
                        self.pdfFiles = result.pdfFiles;
                    }
                    return result;
                },
            });
        },
        initMaps: function () {
            for (var key in Object.entries(this.logs.data)) {
                if (this.logs.data[key].content) {
                    if (this.logs.data[key].content && typeof JSON.parse(this.logs.data[key].content) === 'object') {
                        let json = JSON.parse(this.logs.data[key].content);
                        let id = 'map-' + this.logs.data[key].id;
                        setTimeout(function () {
                            document.dispatchEvent(new CustomEvent('init-map', {detail: {'id': id, 'json': json}}));
                        }, 500);
                    }
                }
            }
        }
    }
}


let initAips = function () {
    return {
        aips: {},
        searchTimeout: null,
        filters: {
            page: 1,
            searchText: '',
            fine_tuning: '',
            check_needed: '',
            applied_for_tuning: '',
            actual: '',
            sort: 'start_date'
        },
        pagination: {},
        pages: [],
        offset: 4,
        importInProgress: false,
        importMessage: '',
        showFiles: false,
        editAip: null,
        summary: null,
        textSearch: '',
        init: function () {
            this.aips = window.aips;
            this.filters = window.filters;
            this.pagination = this.aips;
            window.buildEditors();
            let self = this;
            setTimeout(function () {
                if (self.editAip) {
                    self.editAip.correct_response =
                    window.editorsCollection[self.editAip.id]['correctResponse'].getText();
                    self.compareInputAndOutput();
                }
            }, 1000);
        },
        pagesNumber() {
            if (!this.pagination.to) {
                return [];
            }
            let from = this.pagination.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            let to = from + (this.offset * 2);
            if (to >= this.pagination.last_page) {
                to = this.pagination.last_page;
            }
            let pagesArray = [];
            for (let page = from; page <= to; page++) {
                pagesArray.push(page);
            }

            return pagesArray;
        },
        changePage(number) {
            let self = this;
            number = number ? number : localStorage.getItem('current_page', number);
            this.pagination.current_page = number;
            this.searchAips(number);
        },
        compareInputAndOutput() {
            let container = document.getElementById("aip-input");
            let blocks = container.querySelectorAll('*');
            let correctResponse = (this.editAip.correct_response || this.editAip.response).toLowerCase()
                .replaceAll(':', '')
                .replace(/^\-\s*/, '')
                .replace(/^\d+\.\s*/, '')
            ;

            for (let block of blocks) {
                let text = block.innerText.toLowerCase()
                    .replaceAll(':', '')
                    .replace(/^\-\s*/, '')
                    .replace(/^\d+\.\s*/, '')
                ;

                if (!text || text.length < 3) {
                    jQuery(block).css('backgroundColor', 'inherit');
                    continue;
                }

                if (correctResponse.includes(text)) {
                    // Exact match found in response — no highlight
                    jQuery(block).css('backgroundColor', 'inherit');
                    jQuery(block).data('ini-color', 'inherit');
                } else {
                    jQuery(block).css('backgroundColor', '#ffcccc');
                    jQuery(block).data('ini-color', '#ffcccc');
                }
            }
        },
        getBigrams(str) {
            let bigrams = [];
            for (let i = 0; i < str.length - 1; i++) {
                bigrams.push(str.substring(i, i + 2));
            }
            return bigrams;
        },
        searchInModal: function () {
            jQuery('.modal .jsoneditor-search').first().find('input').val(this.textSearch);
            jQuery('.modal .jsoneditor-search').first().find('input').trigger('input');

            // find block by text in div id=aip-input and scroll "aip-input" to that block
            let container = document.getElementById("aip-input");
            let blocks = container.querySelectorAll('*');

                let alreadyFocused = false;
                for (let block of blocks) {
                    if (this.textSearch && block.innerText.toLowerCase().includes(this.textSearch.toLowerCase())) {
                       if (!alreadyFocused) {
                           container.scrollTo(0, block.offsetTop - container.offsetTop);
                       }
                       jQuery(block).css('backgroundColor', 'yellow');
                    } else {
                        jQuery(block).css('backgroundColor', jQuery(block).data('ini-color') || 'inherit');
                    }
                }

                if (!this.textSearch) {
                    this.compareInputAndOutput();
                }
        },
        buildUrl(page = 1) {
            const params = new URLSearchParams(this.filters);
            return params.toString();
        },
        searchAips: function (page = 1) {
            this.filters.page = page;
            this.sendAjax('/aip/searchAips', {'_token': $('meta[name="csrf-token"]').attr('content'), filters: this.filters}, 'POST');
        },
        importFile: function () {
            let fileInput = document.getElementById('fileField');
            this.importInProgress = true;
            this.importMessage = '';

            if (!fileInput.value) {
                this.importMessage = 'Выберите файлы для импорта';
                this.importInProgress = false;
            }

            let form = document.getElementById('importForm'),
                data = new FormData(form);

            let self = this;

            $.ajax({
                type: 'POST',
                url: '/aip/processFile',
                data: data,
                processData: false, // prevent jQuery from processing data
                contentType: false, // prevent jQuery from setting content type
                success: function (result) {
                    if (result.aips) {
                        self.aips = result.aips;
                        self.pagination = self.aips;

                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000)
                        fileInput.value = '';
                    }

                    return result;
                },
            }).then((result) => {
                result = JSON.parse(result);
                if (result.message) {
                    this.importMessage = result.message;
                }
                this.importInProgress = false;
            });
        },
        save: function (aipId) {
            let correctResponse = jQuery('#' + aipId + '-correctResponse').val(),
                comment = jQuery('#' + aipId + '-comment').val(),
                data = {'_token': $('meta[name="csrf-token"]').attr('content'), id: aipId, correctResponse: correctResponse, comment: comment};

            if (!this.validateJson(correctResponse)) {
                addMessage('danger', 'Невалидный JSON в поле "Исправленный ответ"');
                return;
            }
            this.sendAjax('/aip/save', data, 'POST').then((result) => {
                if (result.success) {
                    addMessage('success', 'Данные сохранены');
                }
            });
        },
        validateJson: function (json) {
            try {
                JSON.parse(json);
                return true;
            } catch (e) {
                return false;
            }
        },
        download: function (aipId) {
            this.sendAjax('/aip/download', {aipId: aipId, '_token': CSRF_TOKEN}, 'POST').then((result) => {
                if (result.url) {
                    let link = document.createElement('a');
                    link.href = result.url;
                    link.download = result.name;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                }
            });
        },
        sendAjax: function (url, body, method = 'POST') {
            let self = this;

            return $.ajax({
                type: method,
                url: url,
                data: body,
                success: function (result) {
                    if (result.aips) {
                        self.aips = result.aips;
                        self.editAip.correct_response = body.correctResponse;
                        self.editAip.comment = body.comment;
                        self.pagination = self.aips;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    }

                    if (result.summary) {
                        self.summary = result.summary;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 1000);
                    }
                    return result;
                },
            });
        },
        openModal: function (aip) {
            let self = this;
            this.editAip = aip;
            this.summary = null;
            setTimeout(function () {
                window.buildEditors();
                self.compareInputAndOutput();
                let container = document.getElementById("aip-input");
                let blocks = container.querySelectorAll('*');
                for (let block of blocks) {
                    if (block.innerText && !block.onclick) {
                        block.addEventListener('click', function () {
                            self.textSearch = block.innerText;
                            self.searchInModal();
                        });
                    }
                }
            }, 1000);
        },
        changeFont: function (increase) {
            let container = document.getElementById("aip-input");
            let style = window.getComputedStyle(container, null).getPropertyValue('font-size');
            let currentSize = parseFloat(style);

            if (increase) {
                container.style.fontSize = (currentSize + 1) + 'px';
            } else {
                container.style.fontSize = (currentSize - 1) + 'px';
            }

            container = document.querySelector('.modal .jsoneditor-tree-inner');
            style = window.getComputedStyle(container, null).getPropertyValue('font-size');
            currentSize = parseFloat(style);
            if (increase) {
                container.style.fontSize = (currentSize + 1) + 'px';
                container.querySelectorAll('div').forEach(function(el){
                    el.style.fontSize = (currentSize + 1) + 'px';
                    el.style.lineHeight = (currentSize + 1) + 'px';
                });
            } else {
                container.style.fontSize = (currentSize - 1) + 'px';
                container.querySelectorAll('div').forEach(function(el){
                    el.style.fontSize = (currentSize - 1) + 'px';
                    el.style.lineHeight = (currentSize - 1) + 'px';
                });
            }
        }
    }
}
let initGlobalMap = function () {
    return {
        initMap: function () {
            setTimeout(function () {
                console.log('Init global map');
                document.dispatchEvent(new CustomEvent('init-global-map', {detail: {'id': 'global-map'}}));
            }, 500);
        }
    }
}

let initLogEdit = function () {
    return {
        log: window.log,
        prompt: window.prompt,
        init: function () {

        },
        deleteLog: function (logId, page) {
            showLoader();
            self = this;
            let data = {logId: logId, page: page, '_token': CSRF_TOKEN};

            $.ajax({
                type: 'POST',
                url: '/deleteLog',
                data: data,
                success: function (result) {
                    if (result.logs) {
                        self.logs = result.logs;
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        updateCorrectResponse: function (notamId) {

            let correctResponse = jQuery('#' + notamId + '-correctResponse').val(),
                localCorrectResponse = jQuery('#' + notamId + '-localCorrectResponse').val(),
                gpt41CorrectResponse = jQuery('#' + notamId + '-gpt41_correctResponse').val(),
                data = {
                    '_token': $('meta[name="csrf-token"]').attr('content'), id: notamId, filters: this.filters,
                    correctResponse: correctResponse, localCorrectResponse: localCorrectResponse, gpt41CorrectResponse: gpt41CorrectResponse
                };

            if (!this.validateJson(correctResponse)) {
                alert('Невалидный JSON в поле "GPT-4o"');
                return;
            }


            if (localCorrectResponse && !this.validateJson(localCorrectResponse)) {
                alert('Невалидный JSON в поле "Ответ локальной модели"');
                return;
            }

            if (gpt41CorrectResponse && !this.validateJson(gpt41CorrectResponse)) {
                alert('Невалидный JSON в поле "GPT-4.1"');
                return;
            }

            this.sendAjax('/notam/updateCorrectResponse', data, 'POST');
        },
        sendAjax: function (url, body, method = 'POST') {
            let self = this;

            return $.ajax({
                type: method,
                url: url,
                data: body,
                success: function (result) {
                    if (result.logs) {
                        self.logs = result.logs;
                        self.pagination = self.logs;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    }

                    if (result.message) {
                        let messageType = 'danger';
                        if (result.status) {
                            messageType = result.status;
                        }
                        dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: messageType, text: result.message}]}}));
                    } else {
                        dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: 'success', text: 'Ответ сохранен'}]}}));
                    }

                    return result;
                },
            });
        },
        copyToLocal: function (log) {
            let gptEditor = window.editorsCollection[log.id]['correctResponse'];
            let localEditor = window.editorsCollection[log.id]['localCorrectResponse'];
            let gpt41Editor =  window.editorsCollection[log.id]['gpt41_correctResponse'];
            text = gptEditor.getText();
            gpt41Editor.updateText(text);
            jQuery('#' + log.id + '-gpt41_correctResponse').val(text);

        },
        copyToGpt: function (log) {
            let gptEditor = window.editorsCollection[log.id]['correctResponse'];
            let localEditor = window.editorsCollection[log.id]['localCorrectResponse'];
            let gpt41Editor =  window.editorsCollection[log.id]['gpt41_correctResponse'];
            text = gpt41Editor.getText();
            gptEditor.updateText(text);
            jQuery('#' + log.id + '-correctResponse').val(text);
        },
        validateJson: function (json) {
            try {
                JSON.parse(json);
                return true;
            } catch (e) {
                return false;
            }
        },

    }
}

let initSettings = function () {
    return {
        prompts: {},
        inputEditor: null,
        outputEditor: null,
        correctAnswerEditor: null,
        editPrompt: true,
        editExamples: true,
        message: '',
        output: '',
        notamId: 0,
        totalTokens: 0,
        inputErrors: 0,
        correctionErrors: 0,
        chatGptErrors: 0,
        pagination: {},
        pages: [],
        offset: 4,
        init: function () {
            this.prompts = window.prompts;
            this.pagination = this.prompts;
        },
        pagesNumber() {
            if (!this.pagination.to) {
                return [];
            }
            let from = this.pagination.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            let to = from + (this.offset * 2);
            if (to >= this.pagination.last_page) {
                to = this.pagination.last_page;
            }
            let pagesArray = [];
            for (let page = from; page <= to; page++) {
                pagesArray.push(page);
            }

            return pagesArray;
        },
        changePage(number) {
            let self = this;
            number = number ? number : localStorage.getItem('current_page', number);
            document.location.href = '?' + this.buildUrl(number);
        },
        buildUrl(page = 1) {
            const params = new URLSearchParams({
                page: page
            });
            return params.toString();
        },
        duplicatePrompt: function (promptId) {
            showLoader();
            self = this;
            let data = {promptId: promptId, '_token': CSRF_TOKEN};
            $.ajax({
                type: 'POST',
                url: '/duplicatePrompt',
                data: data,
                success: function (result) {
                    if (result.prompts) {
                        self.prompts = result.prompts;
                        self.pagination = self.prompts;
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        deletePrompt: function (promptId) {
            showLoader();
            self = this;
            let data = {promptId: promptId, '_token': CSRF_TOKEN};

            $.ajax({
                type: 'POST',
                url: '/deletePrompt',
                data: data,
                success: function (result) {
                    if (result.prompts) {
                        self.prompts = result.prompts;
                        self.pagination = self.prompts;
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        activatePrompt: function (promptId, activatePrompt) {
            showLoader();
            self = this;
            let data = {activate: activatePrompt, promptId: promptId, '_token': CSRF_TOKEN};

            $.ajax({
                type: 'POST',
                url: '/activatePrompt',
                data: data,
                success: function (result) {
                    if (result.prompts) {
                        self.prompts = result.prompts;
                        self.pagination = self.prompts;
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        }
    }
}

let initApp = function () {
    return {
        init: function () {
            let menuLinks = document.querySelectorAll('.js-menu-item a');

            [...menuLinks].forEach(function (link) {
                if (link.href === document.location.href.replace(APP_URL, '')) {
                    link.parentNode.className += ' active';
                }
            });
        }
    }
}

let initPromptEdit = function () {
    return {
        fineTuningJobs: window.fineTuningJobs,
        updatePrompt: function (promptId) {
            showLoader();
            if (window.editorsCollection.length) {
                let textarea = '';
                window.editorsCollection.forEach((editor, index) => {
                    if (editor.json) {
                        textarea = document.getElementById(index + '-json');
                        if (textarea !== undefined) {
                            textarea.value = editor.json.getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, "");
                        }
                    }
                });
            }

            let examples = document.getElementsByClassName('example-checkbox'),
                exampleIds = [];

            for (var i = 0; i < examples.length; i++) {
                if (jQuery(examples[i]).is(':checked')) {
                    exampleIds.push(examples[i].value);
                }
            }

            self = this;

            let data = {
                prompt: jQuery('#' + promptId + '-prompt').val(),
                json: jQuery('#' + promptId + '-json').val(),
                temperature: jQuery('#temperature').val(),
                model: jQuery('#model').val(),
                type: jQuery('#type').val(),
                is_active: jQuery('#is_active').val(),
                example_ids: exampleIds,
                promptId: promptId,
                '_token': CSRF_TOKEN
            };

            $.ajax({
                type: 'POST',
                url: '/updatePrompt',
                data: data,
                success: function (result) {
                    if (result.message && result.type) {
                        addMessage(result.type, result.message);
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        },
        newFineTuningJob(promptId, additionalTuning, maxExamples = 0) {
            let data = {
                    promptId: promptId,
                    additionalTuning: additionalTuning,
                    maxExamples: maxExamples,
                    skipExamples: document.getElementById('skip').value,
                    '_token': CSRF_TOKEN
                },
                self = this;

            showLoader();
            $.ajax({
                type: 'GET',
                url: '/newFineTuningJob',
                data: data,
                success: function (result) {
                    if (result.jobs.length) {
                        self.fineTuningJobs = result.jobs;
                    }
                    jQuery('#inputPanel').unblock();
                },
                error: function (response) {
                    console.log(response);
                },
            });
        }
    }
}

let addMessage = function (type, message) {
    dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: type, text: message}]}}));
}

window.editorsCollection = [];
window.jsonErrors = false;

function validatePeriods(json, errors, parentKey) {
    for (var k in json) {
        // console.log(json);
        if (typeof json[k] === "object") {
            let newParentKey = '';
            if (parentKey !== '') {
                newParentKey = parentKey;
            } else {
                newParentKey = k;
            }
            errors = validatePeriods(json[k], errors, newParentKey);
        } else {
            if (k === 'startDate') {
                let startDate = json['startDate'];
                if (typeof json['startTime'] !== "undefined") {
                    startDate += ' ' + json['startTime'][0] + json['startTime'][1] + ':' + json['startTime'][2] + json['startTime'][3];
                }
                let endDate = null;

                if (typeof json['endDate'] !== "undefined" && json['endDate'] !== 'PERM') {
                    endDate = json['endDate'];
                    if (typeof json['endTime'] !== "undefined") {
                        endDate += ' ' + json['endTime'][0] + json['endTime'][1] + ':' + json['endTime'][2] + json['endTime'][3];
                    }
                } else if (json['endDate'] !== 'PERM') {
                    errors.push({
                        path: [parentKey + '[startDate]'],
                        message: 'Отсутствует дата окончания'
                    });
                    continue;
                }

                if (startDate && endDate && !startDate.indexOf('::')) {
                    console.log('Comparing dates: ' + startDate + ' and ' + endDate);
                    if (moment(startDate) > moment(endDate)) {
                        errors.push({
                            path: [parentKey],
                            message: 'Время начала действия больше времени окончания: ' + startDate + ' > ' + endDate
                        });
                    }
                }
            } else if (k === 'B_Line') {
                let startDate = textToDate(json['B_Line']);
                let endDate = null;
                if (typeof json['C_Line'] !== 'undefined' && json['C_Line'] !== 'PERM') {
                    endDate = textToDate(json['C_Line']);
                } else if (json['C_Line'] !== 'PERM') {
                    errors.push({
                        path: ['B_Line'],
                        message: 'Отсутствует дата окончания (C_Line)'
                    });
                    continue;
                }

                if (startDate && endDate) {
                    if (moment(startDate) > moment(endDate)) {
                        errors.push({
                            path: ['B_Line'],
                            message: 'Время начала действия NOTAM больше времени окончания: ' + startDate + ' > ' + endDate
                        });
                    }
                }
            }
        }
    }

    return errors;
}

function textToDate(t) {
    let date = t;
    if (t.length === 10) {
        date = '20' + t[0] + t[1] + '-' + t[2] + t[3] + '-' + t[4] + t[5] + ' ' + t[6] + t[7] + ":" + t[8] + t[9];
    }
    return date;
}



    function resolveRef(ref) {
        if (!ref) return null;
        // common ref forms: #/definitions/Name or #/components/schemas/Name or /definitions/Name
        const parts = ref.split('/');
        return parts[parts.length - 1] || null;
    }

    function collectFromNode(node, outSet, propName = '') {
        if (outSet.propName) {
            outSet.propName.add(name);
        }
        if (!node || typeof node !== 'object') return;
        if (node.$ref) {
            const name = resolveRef(node.$ref);
            if (name) {
                outSet.add(name);


                return;
            }
        }

        if (Array.isArray(node)) {
            node.forEach(([name,n]) => {
                collectFromNode(n, outSet, name);
            });
            return;
        }
        // array items
        if (node.items) {
            collectFromNode(node.items, outSet, propName);
        }

        // unions / combinators
        ['anyOf', 'oneOf', 'allOf'].forEach(k => {
            if (node[k]) node[k].forEach((sub, propName) => collectFromNode(sub, outSet, propName));
        });
        // if the node has a 'type' that is 'object' and a $ref-like title, try title
        if (node.title && typeof node.title === 'string') {
            outSet.add(node.title);
        }
        // also traverse nested properties to catch deeper $refs (but mapping keys are only direct properties)
        if (node.properties) {
            Object.values(node.properties).forEach((p, name) => collectFromNode(p, outSet, name));
        }
    }

    function processProperties(props, mapping) {
        if (!props) return;
        Object.entries(props).forEach(([propName, propSchema]) => {
            const names = new Set();
            collectFromNode(propSchema, names, propName);
            // if (names.size > 0) {
                mapping[propName] = Array.from(names);
            // }
        });
    }

    function gatherAllDefinitions(sch) {
        // support v4 style `definitions` and OpenAPI `components.schemas`
        return Object.assign({}, sch.definitions || {}, (sch.components && sch.components.schemas) || {});
    }

    function buildMapping(sch) {
        const mapping = {};
        // root properties
        processProperties(sch.properties, mapping);
        // definitions / components
        const defs = gatherAllDefinitions(sch);
        Object.values(defs).forEach((def, key) => {
            if (def && def.properties) {
                let name = Object.keys(defs)[key];
                    processProperties(def.properties, mapping);

                if (!mapping[name]) {
                    mapping[name] = Array.from(new Set(Object.keys(def.properties)));
                }
            }
        });
        return mapping;
    }

function getMappedTypesArray(schema) {
    const result = buildMapping(schema);
    return result;
}

function buildEditors() {
    window.allAllowedTemplates = getMappedTypesArray(window.jsonSchema);
    let options = {
            language: 'ru',
            mode: 'tree',
            limitDragging: false,
            modes: ['tree', 'code'], // allowed modes
            allowSchemaSuggestions: true,
            autocomplete: {
                getOptions: function () {
                    return [
                        'ARR', 'DEP', 'FLT_NUMB', 'SCHEDULED_FLTS', 'CHARTER_FLTS', 'DOMESTIC_FLTS', 'VIP_FLTS', 'INTERNATIONAL_FLTS', 'TWO-WAY COMM',
                        'ALL', 'VIA_ATS_ROUTES', 'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC',
                        'full', 'clock', 'unticlock', 'both', 'direct', 'backward', 'odd', 'even',
                        'Restricted', 'Prohibited', 'Unknown', 'Military', 'Alert', 'Caution', 'Danger', 'Nat_secur', 'Training', 'TSA', 'TRA', 'closed', 'uav',
                        'state_border', 'FIR_border', 'polar_point', 'point', 'line', 'arc',
                        "MagVar","ACFT", "cat", "fuselage_width", "limited_to", "types", "wing_span", "AWY_Segment", "ATC_only", "Backward_course", "Cond_RTE", "Direct_course",
                        "Direction", "Dist",
                        "Coordinate_System", "Coordinates", "WptName", "Max_ALT", "Min_ALT_backward", "Min_ALT_direct", "RNAV",
                        "direction","Longitude", "Latitude","ID","Name","FIR","Type","type","AVLB","From","To","Width", "Coordinate_System", "Coordinates", "WptName", "cdr_value", "new_route", "AcftCategory", "AcftTypeObject",
                        "Code","Fuel","Times","ASDA","ACFT_proh","AC_type", "Code_more_less", "AcftTypes", "Aerodrome", "ACFT_allow",  "AVLB_AS_ALT",  "Day_only", "Elevation",
                        "RFFS", "Transition_ALT", "Transition_FL", "UTC", "Airway", "AWY_Segments", "Airway_ID",
                        "AppCat", "Approach", "OCH(A)", "OCH(B)", "OCH(C)", "OCH(D)", "OCH(DL)", "OCH(H)", "OCA(A)", "OCA(B)", "OCA(C)", "OCA(D)", "OCA(DL)", "OCA(H)",
                        "RWY", "Apron", "Elevation",  "PCN", "PCR",  "Area", "Activated", "Area_name", "Coordinate_System", "Coordinates",
                        "Coordinates_excl", "Lower_limit", "No_Fly", "Upper_limit", "AreaType", "Arrival",  "Avalability",
                        "Radius", "CdrValue", "ConditionalRoutingDirection", "Conditional_Routing", "Condition_from", "AP", "Direction", "WPT", "Condition_to", "AP",
                        "Direction", "WPT", "Routes", "Coordinate", "CoordinateValue", "CoordinateWithType", "Longitude",
                         "Latitude_cntr", "Longitude_cntr", "radius",
                        "state",   "Coordinate_System", "DIST",  "RAD",
                        "CustomCoordinate", "DateType", "Day", "day", "month", "periods", "DaysRange", "Departure",  "Direction", "Direction_awy", "E_line",
                        "Aerodromes", "Airways", "Aprons", "Areas", "Conditional_Routings", "FIRs", "GLSs", "Helipads", "ILSs", "Navaids", "Obstacles", "Radios",
                        "References", "Runways", "Stands", "Taxiways", "Waypoints", "exemptions", "Exemption", "airports", "condition", "numbers",  "ExemptionCondition",
                        "ExemptionType", "ADSC", "CPDLC", "CPDLC_Logon", "FIR_code", "Flight_rules", "Max_alt", "Min_alt", "FLevel_struct", "FL", "Range",
                        "FlLvls", "FlightNumber", "FullDegrees", "GLS", "CALL_SIGN", "CANAL", "G_slope", "Thld", "Helipad", "ACFT_allow",
                         "Coordinates", "Day_only", "Elevation",   "RFFS", "Transition_ALT",
                        "Transition_FL", "UTC", "ILS", "Back_course", "CALL_SIGN", "CAT", "Course", "FREQ_CS", "FREQ_GS", "G_slope", "Thld", "IcaoCode",
                        "LettersAndNumbers", "MonthName", "MoreOrLessVal", "more_less", "value", "Navaid", "Channel", "Coordinate_System", "Coordinates",
                        "Frequency", "A_Line", "AltitudeMax", "AltitudeMin",
                         "B_Line", "C_Line", "CheckNeeded", "CoordinatesLat", "CoordinatesLon", "Cycle", "D_Line", "E_Line", "Entity", "FIRs", "F_Line", "G_Line",
                        "Id", "N_STATUS", "N_status", "QCode", "REF_NOTAM",  "SiteIndexA", "Status", "TYPE", "Text", "Title", "Traffic", "altitudeMax",
                        "altitudeMin", "area", "coordinatesLat", "coordinatesLon", "cycle", "entity", "id", "radius", "siteIndexA", "status", "text", "title",
                        "traffic",  "NumberRange", "NumberType", "Obstacles", "Active", "Coordinate_System", "Coordinates", "Elevation", "Height",
                        "Light_mark", "Marking",  "Pcn", "Period", "endDate", "endTime", "startDate", "startTime", "Procedures",
                        "Approach", "Arrivals", "Departures", "RFFS", "RFFS_CAT", "RTE_Segment", "AWY", "Direction", "FLevel",
                        "TO", "Radio", "Call_Sign", "Freq", "Route", "RTE_Segments", "Runways", "ACFT_allow",  "Dimensions",
                        "Length",  "LDG",    "STRP_Dimensions", "Length",  "TKOF", "Threshold",  "Slope",
                        "SoftEqualString", "Stand", "ACFT_allow",  "Coordinates", "Coordinate_system",   "Stand_Number",
                        "SunTime", "Taxiways", "ACFT_allow",  "Declared_Distances", "LDA", "SWY", "TODA", "TORA", "Thld",
                        "TWY",  "Threshold", "Day_Marking", "Declared_Distances", "CW_Length", "CW_Width", "LDA", "SWY", "TODA", "TORA",
                        "Displaced", "Displaced_by", "LDG", "Mag_Azimuth", "Obstacle_Lights", "PAPI_light",   "RWY_App_light", "RWY_Edge_light", "RWY_End_light",
                        "RWY_Thld_light", "RWY_centerline_mark", "RWY_end_coordinates", "Coordinate_System", "RWY_end_elevation", "Rwy_Slope", "SWY_Light",
                        "TAX", "TKOF", "Taxiing", "Thld", "Thld_Coord", "Coordinate_System", "Thld_Elevation", "Time", "ValuePlusUnits", "VerticalLimits",
                        "Waypoint", "Coordinate_System", "Coordinates", "WeekDayName", "WptName"
                    ];
                }
            },
            onCreateMenu: function (items, node) {
                let topPath = node.path.pop();
                const iniPath = topPath;
                topPath = node.path.pop(); // get parent of parent
                let arrayChildFound = false;
                while (/^[0-9]+$/.test(topPath)) {
                    topPath = node.path.pop();
                    arrayChildFound = true;
                }

                let allAllowedTemplates = window.allAllowedTemplates;
                let allowedTemplates = [];

                if (typeof allAllowedTemplates[topPath] !== "undefined") {
                    allowedTemplates = allAllowedTemplates[topPath];
                }
                if (arrayChildFound && allowedTemplates.length == 1) {
                    topPath = allowedTemplates[0];
                    if (typeof allAllowedTemplates[topPath] !== "undefined") {
                        allowedTemplates = allAllowedTemplates[topPath];
                    }
                }

                for (var i = 0; i < items.length; i++) {
                    if (items[i].className
                        && ["jsoneditor-extract","jsoneditor-sort-asc","jsoneditor-sort-desc","jsoneditor-transform"].indexOf(items[i].className)!==-1) {
                        items.splice(1, i);
                        i--;
                    } else {
                        console.log('NOT removing ' + items[i].className);
                    }


                    if (items[i].className == "jsoneditor-insert" || items[i].className == "jsoneditor-append") {
                        let subMenu = items[i].submenu;

                        if (items[i].className == "jsoneditor-append") {
                            items[i].text = 'Вставить перед ' + iniPath;
                        }
                        if (items[i].className == "jsoneditor-insert") {
                            items[i].text = 'Вставить после ' + iniPath;
                        }

                        if (node.path) {
                            let separatorPassed = false;
                            for (var j = 0; j < subMenu.length; j++) {
                                if (subMenu[j].type == 'separator') {
                                    separatorPassed = true;
                                    continue;
                                }
                                if (!separatorPassed) {
                                    continue;
                                }

                                if (allowedTemplates.indexOf(subMenu[j].text) === -1) {
                                    items[i].submenu[j].className += ' hidden';
                                } else {
                                    console.log('NOT Removing ' + subMenu[j].text);
                                    items[i].submenu[j].className = items[i].submenu[j].className.replace('hidden', '');
                                }

                            }
                        }
                    }
                }

                return items;
            },
            onValidationError: function (errors) {
                window.jsonErrors = errors.filter((error) => error.type === 'error').length;
            },
            onValidate: function (json) {
                return validatePeriods(json, [], '');
            }
        },
        data = '',
        self = this;
    let editors = document.querySelectorAll('.editor');

    let editorObject = '';
    let textarea = '';
    [...editors].forEach(function (editor) {
        let id = editor.dataset.id,
            name = editor.dataset.name;
        let editorJsonData = {};
        try {
            editorJsonData = editor.dataset.value ? JSON.parse(editor.dataset.value) : {};
        } catch (e) {
            editorJsonData = editor.dataset.value ?? {};
        }

        editor.className = editor.className.replace(' processed', '');
        editor.innerHTML = '';

        if (editor.className.indexOf('processed') !== -1 && window.editorsCollection[id]) {
            window.editorsCollection[id][name].update(editorJsonData);
        } else {
            editor.className += ' processed';

            try {
                if (window.editorsCollection[id] === undefined) {
                    window.editorsCollection[id] = [];
                }

                if (editor.dataset.textname) {
                    jQuery('#' + editor.dataset.textname).val(editor.dataset.value);
                    options.onChangeText = (jsonString) => {
                        jQuery('#' + editor.dataset.textname).val(jsonString);
                    }
                }
                if (name === 'input') {
                    options.mode = 'view';
                    options.readOnly = true;
                    editor.value = editor.value.replace(/(?:\r\n|\r|\n|\n\t|\t)/g, "\n");
                }

                let editorOptions = {...options};
                if (name === 'output' || name === 'correctResponse' || name === 'gpt41_correctResponse'
                    || name === 'localCorrectResponse' || name === 'correct' || name === 'localOutput') {
                    editorOptions.schema = window.jsonSchema;
                    editorOptions.templates = window.notamTemplates;
                }
                if (editor.dataset.schema === 'mel') {
                    editorOptions.schema = window.melJsonSchema;
                }
                if (editor.dataset.schema === 'aip') {
                    editorOptions.schema = '';
                }

                if (name !== 'input') {
                    if (typeof editorJsonData === 'string') {
                        editorJsonData = window.jsonrepair(editorJsonData);
                        editorJsonData = JSON.parse(editorJsonData);
                        console.log('Repaired json', editorJsonData);
                    }

                    editorObject = new JSONEditor(editor, editorOptions, editorJsonData);
                    editorObject.expandAll();
                    window.editorsCollection[id][name] = editorObject;
                }
            } catch (e) {
                textarea = document.getElementById(editor.dataset.id + '-' + editor.dataset.name);
                if (textarea !== undefined && textarea !== '') {
                    jQuery(editor).css('display', 'none');
                    jQuery(textarea).show().css('height', '300px').css('width', '100%');
                }
            }
        }
    });
}

function initImport() {
    return {
        promptId: 0,
        notams: [],
        message: '',
        limit: 1,
        offset: 0,
        processed: 0,
        inProgress: false,
        sendData: function () {
            if (!this.promptId) {
                alert('Выберите PROMPT');
                return;
            }

            showLoader();

            this.message = 'Загружаю результат...';
            this.output = '';
            this.notamId = 0;
            this.totalTokens = 0;
            let self = this;

            let form = document.getElementById('mainForm');
            $.ajax({
                type: 'POST',
                url: '/processFile',
                data: new FormData(form),
                async: true,
                cache: false,
                processData: false,
                contentType: false,
                enctype: 'multipart/form-data',
                success: function (result) {
                    if (result.status === 'error') {
                        self.message = result.message;
                    } else {
                        self.notams = result.notams;
                        self.message = '';
                    }
                    jQuery('#inputPanel').unblock();

                    setTimeout(function () {
                        window.buildEditors();
                    }, 1000);
                },
                error: function (response) {
                    console.log(response);
                }
            });
        },
        checkPromptForUniqueNotams: function () {
            if (!this.promptId) {
                alert('Выберите PROMPT');
                return;
            }
            this.processed = 0;
            this.message = 'Загружаю результат...';
            this.output = '';
            this.notamId = 0;
            this.totalTokens = 0;
            let self = this;

            let data = {
                'limit': this.limit,
                'offset': this.offset,
                '_token': CSRF_TOKEN,
                'prompt_id': jQuery('#prompt_id').val()
            };

            this.processMiniBatch(data);
        },
        processMiniBatch: function (data) {
            let self = this;
            $.ajax({
                type: 'POST',
                url: '/checkPromptForUniqueNotams',
                data: data,
                success: function (result) {
                    if (result.status === 'error') {
                        self.message = result.message;
                    } else {
                        self.notams = result.notams;
                        self.message = '';
                    }


                    self.processed = result.processed;
                    if (window.uniqueNotams > result.processed) {
                        data.offset += data.limit;
                        self.processMiniBatch(data);
                    }
                    setTimeout(function () {
                        window.buildEditors();
                    }, 1000);
                },
                error: function (response) {
                    console.log(response);
                }
            });
        },
        validateNotam: function (notamId, batchId) {
            if (notamId && window.editorsCollection[notamId] !== undefined) {
                let self = this,
                    data = {
                        'chatGptData': window.editorsCollection[notamId]['output'].getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""),
                        'correctData': window.editorsCollection[notamId]['correct'].getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""),
                        'notamId': notamId,
                        'comments': jQuery('#comments-' + notamId).val(),
                        '_token': CSRF_TOKEN
                    };

                $.ajax({
                    type: 'POST',
                    url: '/validateNotam',
                    data: data,
                    success: function (result) {
                        if (result.message) {
                            alert(result.message);
                        }

                        self.loadBatch(batchId);
                    },
                    error: function (response) {
                        console.log(response);
                    },
                });
            }
        },
        validateMel: function (melId, batchId) {
            if (melId && window.editorsCollection[melId] !== undefined) {
                let self = this,
                    data = {
                        'chatGptData': window.editorsCollection[melId]['output'].getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""),
                        'correctData': window.editorsCollection[melId]['correct'].getText().replace(/(?:\r\n|\r|\n|\n\t|\t)/g, ""),
                        'melId': melId,
                        'comments': jQuery('#comments-' + melId).val(),
                        '_token': CSRF_TOKEN
                    };

                $.ajax({
                    type: 'POST',
                    url: '/validateMel',
                    data: data,
                    success: function (result) {
                        if (result.message) {
                            alert(result.message);
                        }

                        self.loadBatch(batchId);
                    },
                    error: function (response) {
                        console.log(response);
                    },
                });
            }
        },
        loadBatch: function (batchId) {
            let self = this,
                data = {
                    'batch_id': batchId,
                    '_token': CSRF_TOKEN
                };

            self.notams = {};
            showLoader();
            $.ajax({
                type: 'POST',
                url: '/loadBatch',
                data: data,
                success: function (result) {
                    if (result.message) {
                        alert(result.message);
                    }

                    if (result.notams) {
                        self.notams = result.notams;
                    }
                    jQuery('#inputPanel').unblock();
                    setTimeout(function () {
                        window.buildEditors();
                    }, 1000);
                },
                error: function (response) {
                    console.log(response);
                },
            });
        }
    }
}

$(document).ready(function () {
    buildEditors();
    jQuery('.panel [data-action=collapse]').click(function (e) {
        e.preventDefault();
        var $panelCollapse = jQuery(this).parent().parent().parent().parent().nextAll();
        jQuery(this).parents('.panel').toggleClass('panel-collapsed');
        jQuery(this).toggleClass('rotate-180');
        $panelCollapse.slideToggle(150);
    });
});

let showLoader = function (elId = '#inputPanel') {
    jQuery(elId).block({
        message: '<i class="icon-spinner2 spinner"></i>',
        overlayCSS: {
            backgroundColor: '#fff',
            opacity: 0.8,
            cursor: 'wait'
        },
        css: {
            border: 0,
            padding: 0,
            backgroundColor: 'none'
        }
    });
};

let initMessages = function () {
    "use strict";
    return {
        messages: [],
        isEmpty: function () {
            return this.messages.reduce(
                function (isEmpty, message) {
                    return isEmpty && message === undefined
                }, true
            )
        },
        removeMessage: function (messageIndex) {
            this.messages[messageIndex] = undefined;
        },
        addMessages: function (newMessages, hideAfter = 3000) {
            var $this = this;
            newMessages.map(function (message) {
                $this.messages = $this.messages.concat(message);
                if (hideAfter) {
                    $this.setHideTimeOut($this.messages.length - 1, hideAfter);
                }
            });
        },
        getData: function (data) {
            if (data.messages && data.messages.messages && data.messages.messages.length) {
                this.addMessages(data.messages.messages, 3000);
            }
        },
        setHideTimeOut: function (messageIndex, hideAfter) {
            var $this = this;
            setTimeout(
                function (messageIndex) {
                    $this.removeMessage(messageIndex);
                },
                hideAfter,
                messageIndex
            );
        }
    }
}

let initGptCompare = function () {
    return {
        notams: window.notams,
        stats: window.stats,
        pagination: window.notams,
        pages: [],
        offset: 2,
        filters: {page: 1, sort_by: 'created_at', sort_direction: 'desc'},
        init: function () {
            this.notams = this.splitResults(this.notams);
        },
        pagesNumber() {
             if (!this.pagination.to) {
                return [];
            }
            let from = this.pagination.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            let to = from + (this.offset * 2);
            if (to >= this.pagination.last_page) {
                to = this.pagination.last_page;
            }
            let pagesArray = [];
            for (let page = from; page <= to; page++) {
                pagesArray.push(page);
            }

            return pagesArray;
        },
        changePage(number) {
            let self = this;
            number = number ? number : localStorage.getItem('current_page', number);
            this.pagination.current_page = number;
            this.searchData(number);
        },
        splitResults: function (notams) {
            for (let key in notams.data) {
                notams.data[key].notams = JSON.parse(notams.data[key].notams);
                let iniNotams = notams.data[key].notams;
                notams.data[key].notams.sort((a, b) => a.model_name.localeCompare(b.model_name));

                if (iniNotams !== notams.data[key].notams) {
                    console.log('REORDERED ' + notams.data[key].notams[0].id);
                }
            }
            return notams;
        },
        searchData: function (page) {
            this.filters.page = page;
            this.sendAjax('/notam/searchGptCompareRequests', {'_token': $('meta[name="csrf-token"]').attr('content'), filters: this.filters});
        },
        sendData: function (notamId) {
            let data = jQuery('form#mainForm').serialize();
            this.sendAjax('/notam/sendNotamCompareRequest', data);
        },
        sendAjax: function (url, body, method = 'POST') {
            let self = this;

            return $.ajax({
                type: method,
                url: url,
                data: body,
                success: function (result) {
                    if (result.notams) {
                        self.notams = self.splitResults(result.notams);
                        self.pagination = result.notams;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    } else if (result.data) {
                        self.notams = self.splitResults(result);
                        self.pagination = result;
                        setTimeout(function () {
                            window.buildEditors();
                        }, 2000);
                    }

                    if (result.stats) {
                        self.stats = result.stats;
                    }

                    if (result.stats) {
                        self.stats = result.stats;
                    }

                    if (result.message) {
                        dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: (result.type?result.type:'danger'), text: result.message}]}}));
                    }
                    // else {
                    //     document.getElementById('input').value = '';
                    //     dispatchEvent(new CustomEvent('messages-loaded', {detail: {messages: [{type: 'success', text: 'Запрос поставлен в очередь на обработку'}]}}));
                    // }

                    return result;
                },
            });
        }
    }
}