﻿$engine JScript
$uname silence
$dname Тишина в отсеках
$addin stdlib
$addin stdcommands

// (с) Александр Орефков orefkov at gmail.com
// Это небольшой скрипт для подавления некоторых сообщений Конфигуратора, бессмысленных и беспощадных.
// Пока реализовано "в-лоб", в дальнейшем надо сделать список из "регэксп + результат",
// и гуи по настройке, какие подавлять, какие нет.

// Подключение библиотеки log4js, для удобного логгирования различных событий. 
stdlib.require('log4js.js', SelfScript);
stdlib.require('SettingsManagement.js', SelfScript);
stdlib.require("SelectValueDialog.js", SelfScript);
stdlib.require('ScriptForm.js', SelfScript);
stdlib.require('TextWindow.js', SelfScript);

stdlib.require('SyntaxAnalysis.js', SelfScript);

var logger = Log4js.getLogger(SelfScript.uniqueName);
var appender = new Log4js.BrowserConsoleAppender();
// Определяем формат вывода сообщений. 
appender.setLayout(new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN));
logger.addAppender(appender);
logger.setLevel(Log4js.Level.ERROR);

global.connectGlobals(SelfScript);

// # Подпишемся на событие при выводе предупреждения/вопроса
// ## подписки на события показа окон: 
// __onMessageBox__ - для модальных блокирующих окошек типа Предупреждение. 
// __onDoModal__ - для модальных окошек типа "Вопрос" , в частности для включенной "провекри синтасиса при записи"" подключаемся на событие модального окна. 
//
events.connect(windows, "onMessageBox", SelfScript.self)
if (profileRoot.getValue("ModuleTextEditor/CheckAutomatically")){
    events.connect(windows, "onDoModal", SelfScript.self);  
}

var notify = true;
RE_PROC              = new RegExp('^\\s*((?:procedure)|(?:function)|(?:процедура)|(?:функция))\\s+([\\wА-яёЁ\\d]+)\\s*\\(', 'i');
// # onMessageBox
//  Функция - обработчик
// ## Параметры
// __param__ - словарь содержащий все параметры Предупреждения
// ``` 1c
// Message(param.caption + " | " + param.text + " | " + param.type + " | " + param.timeout)
//```
function onMessageBox(param)
{

    // При отработке события перехват с MessageBox'а снимается, и в обработчике
    // можно смело его вызывать, не боясь зацикливания. Например мы сами хотим узнать ответ
    // пользователя и в зависимости от него выполнить какие-то действия
    // param.result = MessageBox(param.text, param.type, param.caption, param.timeout)
    // param.cancel = true

    if(param.text == "Внимание!!! Месторасположение информационной базы изменилось.\nПродолжить?")
    {
        param.result = mbaYes
        param.cancel = true
        return;
    }
    
    // сообщения типа "Объект Роль.Менеджер заблокирован." или "Объект Справочник.СохраненныеНастройки заблокирован."
    reRoleBlock = /Объект\s*[\d\wzа-яё]+\.[\d\wzа-яё\.]+\s*заблокирован\./ig
    if(reRoleBlock.test(param.text)){
        Message(param.text)
        param.result = mbaYes
        param.cancel = true
        return;
    }
    
    // сообщения типа "Объединение конфигураций завершено."
    reConfigUnionEnd = /объединение\s+конфигураций\s+завершено\./ig
    if(reConfigUnionEnd.test(param.text)){
        param.result = mbaYes
        param.cancel = true
        Message(param.text)
        return;
    }
}
// Перехватываем модальное окошко и если в первом контроле в тексте содержиться 
// фраза "При проверке модуля обнаружены ошибки!" тогда подавляем данно сообщение с выводом в трее неблокируюещего 
// сообщения о наличии ошибок. 
function onDoModal(dlgInfo){

    if(dlgInfo.stage == openModalWnd)
    {
        if (dlgInfo.Caption == "Конфигуратор"){

            for(var c = 0; c < dlgInfo.form.controlsCount; c++)
            {
                if (c > 2){
	               //Опытным путем подобранно, что больше чем 2 контрола нет на форме, соответственно если больше, то это не наша форма. 
                    return;
                }
                var ctr = dlgInfo.form.getControl(c);
                
                //Определим текстовое значение, если не заполненно, значит это не наш случай. 
                var text = "" + ctr.value;
                if (!text){
                    continue;
                }

                if (text.indexOf("При проверке модуля обнаружены ошибки!")!=-1){
                    try{
			         //Создадим объект sendkeys и отправим нажатие ENTER
                     //TODO: исправить на нативное нажатие кнопки. 
                        new ActiveXObject("WScript.Shell").SendKeys("{ENTER}");
                        if (notify)
                        {
			             //Создается объект notify для возможности отправить сообщение. 
			             //анализируем параметры системы и версии 1С, для версий выше 8.2.13 пользуемся стандартным сообщением пользователю. 
                          var notifysend = stdlib.require('NotifySend.js').GetNotifySend();
                          var СистемнаяИнформация = v8New("СистемнаяИнформация");
                          var версия = СистемнаяИнформация.ВерсияПриложения;
                          if (версия.indexOf("8.2.13")==-1){
                              notifysend.provider = notifysend.initprovider("Встроенный1С");
                          }
                          notifysend.Error("Сохраняем ", "При сохранении есть ошибки \n имей ввиду", 3);
                          notify = false;
                          stdlib.setTimeout(function () {
                              notify = true;
                          }, 3000);
                        }
                    } catch (e){
                };
                return
                }
            }
       }
    }
}

ProcedurCreateHelper = ScriptForm.extend({

    settingsRootPath : SelfScript.uniqueName,
    
    settings : {
        pflSnegopat : {
            'use'      : true, // Использовать... 
            'defaultContext': 0, //0 - на клиенте, 1 - на сервере без контекста, 2 - на сервере.' : false, // Учитывать регистр при поиске.
            'useAltenate'   : false, // Использовать алтернативный вариант. 
            'position'      : "afterProcedure", // Позиция создания "afterProcedure" - после созданной процедуры, "atLastContext" - после после последнего контекстного вызова.  "beforeProcedure"
            'time'          : 3 //Время в секундах для показа формы. 
        }
    },

    construct : function () {  
        this._super("scripts\\ProcedurCreateHelper.ssf");                
        this.form.КлючСохраненияПоложенияОкна = "ProcedurCreateHelper.dialog"
        this.loadSettings();
        this.timerId = null;
        this.textwindow = null;
        this.timerFormId = 0;
        this.count = 0;
        this.firstRefresh = true;
        ProcedurCreateHelper._instance = this;
    }, 

    loadSettings : function(){
        this._super();

        if (this.form.use || this.form.useAltenate){
            events.connect(windows, "onDoModal", this, "onDoModalAtClient");
        } else {
            try{
                events.disconnect(windows, "onDoModal", this, "onDoModalAtClient");    
            } catch(e){};
        }
    }, 

    Form_OnClose : function () {
        this.saveSettings();
    },

    Form_BeforeClose : function(Cancel){

    },

    Form_OnOpen : function() {
        this.firstRefresh = true;
        valueList = v8New("ValueList");
        valueList.add("afterProcedure", "После процедуры");
        valueList.add("beforeProcedure", "Перед процедурой");
        valueList.add("atLastContext", "После процедур контекста");
        this.form.Controls.PositionCreate.СписокВыбора = valueList;

        var valueItem = this.form.Controls.PositionCreate.СписокВыбора.FindByValue(this.form.position);
        if(!valueItem){
            this.form.position = "afterProcedure";
        }
        
        this.form.Controls.PositionCreate.Значение = this.form.position;
        
        this.form.Controls.defaultContext.Значение = this.form.defaultContext;
        
        if (this.count == 27){
            this.form.Controls.ServerNoContext.ButtonBackColor = v8New("Цвет", 0, 130, 209);
            this.form.Controls.Server.ButtonBackColor = v8New("Цвет", 255, 209, 0);
        }

        //this.form.CurrentControl = this.form.Controls.clear;
    },

    PositionCreate_OnChange:function(el){
        this.form.position = el.value;
    },

    Form_ObjectActivationProcessing:function(ActiveObject, Source){
        if (this.timerFormId > 0){
            killTimer(this.timerFormId);
            this.timerFormId = 0;
        }
    },

    Form_RefreshDisplay:function(){

        if (this.firstRefresh){
            this.firstRefresh = false;
            return;
        }

        if (this.timerFormId > 0){
            killTimer(this.timerFormId);
            this.timerFormId = 0;
        }  
    },

    onDoModalAtClient : function(dlgInfo){

         if(dlgInfo.stage == afterInitial) //beforeDoModal afterInitial
        {
            if (dlgInfo.Caption == "Конфигуратор"){
                if (dlgInfo.form.controlsCount != 7){
                    return;
                }
                var crt = dlgInfo.form.getControl(1);
                if (crt.name == "OnlyClient"){

                    if(!this.form.useAltenate){
                        crt.value = this.form.defaultContext;
                    } else {
                        //TODO: добавить проверку "Сервер" и "Сервер без контекста"
                        dlgInfo.form.sendEvent(dlgInfo.form.getControl(6).id, 0);
                        this.timerId = createTimer(500, this, 'onTimer');
                    }
                }

           }
        }
    }, 

    onTimer : function(timerId){

        var activeView = windows.getActiveView();
        if (!activeView){
            return;
        }
        
        var wnd = GetTextWindow();    
        if (wnd){
            this.lastActiveTextWindow = wnd;
            killTimer(this.timerId);
            this.timerId = 0;
        } else if (this.lastActiveTextWindow && !this.lastActiveTextWindow.IsActive()){
            this.lastActiveTextWindow = null;
        }

        this.analiseTextAndView(this.lastActiveTextWindow);


    }, 

    analiseTextAndView:function(textWindow){
        if (!textWindow){
            this.close();
            return;
        }

        var canCreate = false;

        curPos = (textWindow.GetCaretPos().beginRow) - 1;
        curPos = curPos == 0?1:curPos;
        this.beginRow = textWindow.GetCaretPos().beginRow;

        var str = textWindow.GetLine(curPos);
        Matches = RE_PROC.exec(str);
        if( Matches != null )
        {
            this.form.Controls.НадписьНазвание.Заголовок = Matches[2];
            this.selectedText = textWindow.GetSelectedText();
            canCreate = true;
            if (this.isOpen() && this.form.Panel.Pages.CurrentPage == this.form.Panel.Pages.Settings){ //Если открыто окно с настройками, тогда не показываем варианты создания. 
                canCreate = false;
            } 

       }

        if (canCreate){
            this.show();
            pch.form.Panel.CurrentPage = pch.form.Panel.Pages.Find("Job");
            this.timerFormId = createTimer(this.form.time*1000, this, 'onTimerToClose');
            var view = textWindow.GetView();
            if (view){
                view.activate();
            }
        } else {
            this.close();
        }
    },

    onTimerToClose:function(timerId){
        killTimer(this.timerFormId);
        this.timerFormId = 0;
        
        if (this.form.CurrentControl.Name != "ServerNoContext"){
            return;
        }
        this.close();
    }, 

    getPositionForAddLines : function(wnd, context, localCnt){
        //Message(""+context);
        maxPosition = 0;
        maxProcedure = "";
        if (localCnt == undefined){

            cnt = SyntaxAnalysis.AnalyseTextDocument(wnd);
            //currentMethod = cnt.getActiveLineMethod();
        } else {
            cnt = localCnt;
        }

        
        vtModules = cnt.getMethodsTable();
        for (var i = 0; i<vtModules.Count(); i++) {
            var thisRow = vtModules.Get(i);
            curContext = thisRow.Context;
            curContext = curContext.replace("НаСервереБезКонтекста", "atServerNoContext", "ig");
            curContext = curContext.replace("НаСервере", "atServer", "ig");
            if (curContext.length == 0){
                curContext = "atServer"
            }
            

            if (curContext.indexOf(context) ==-1){
                //Message("Пропустим "+curContext + " "+context + " позиция "+curContext.indexOf(context));
                continue;
            }

            //Message("name "+thisRow.Name + "context " + thisRow.Context + " " +thisRow.StartLine+" " + thisRow.EndLine + " " + thisRow._method.EndLine);

            maxPosition = (maxPosition > thisRow._method.EndLine+2) ? maxPosition : thisRow._method.EndLine+2;

        }

        return maxPosition;

    },

    createFunction : function(context, position){

        this.count++; 
        if (this.lastActiveTextWindow && !this.lastActiveTextWindow.IsActive()){
            this.lastActiveTextWindow = null;
            Messga("Не возможно создать процедуру, окно уже закрыто");
            return;
        } else {
            view = this.lastActiveTextWindow.GetView();
            if (view){
                view.activate();
            }
        }

        var activeView = windows.getActiveView();
        if (!activeView){
            Messga("Нет активных окон.");
            return;
        }
        
        var wnd = GetTextWindow();    
        if (!wnd){
            Message("Нет активного текстового окна.") ;
            return;
        }

        if (wnd.GetHwnd() != this.lastActiveTextWindow.GetHwnd()){
            Message("Нет активное текстовое окно не совпадает с сохраненным") ; //FIXME: добавить проверку метаданных. 
            return;   
        }

        //Определим новое название процедуры. 
        var newName = this.form.Controls.НадписьНазвание.Заголовок + ((context == 'atServer') ? 'НаСервере':'НаСервереБезКонтекста');
        logger.debug("new name :"+newName);

        var newProcedure = '\n'+((context == 'atServer') ? '&НаСервере':'&НаСервереБезКонтекста') + '\n' + 'Процедура'+ ' '+ newName + '()\n    \nКонецПроцедуры';

        //Первое определим положение курсора, если все в той же процедуре и выделенный текст, тогда будем заменять новым названием.
        // если курсор выделяет какой либо текст, значит надо заменить, его. 

        isSelection = false;
        lineToInsertName = 0;
        selection = null;
        lineToInsertProcedure = 0;


        selection = wnd.GetSelection();
        selectedText = wnd.GetSelectedText();
        if (selectedText == this.selectedText && wnd.GetCaretPos().beginRow == this.beginRow){
            isSelection = true;
            lineToInsertName = wnd.GetCaretPos().beginRow
            if (position == "afterProcedure"){
                lineToInsertProcedure = this.beginRow + 2;
            } else if(position == "beforeProcedure"){
                lineToInsertProcedure = this.beginRow - 2;
            } else {
                //Вот тут 
                lineToInsertProcedure = this.getPositionForAddLines(wnd, context)
            }
        } else {
            //Найдем по названию метода последнюю строку, добавим туда вызов. 
            cnt = SyntaxAnalysis.AnalyseTextDocument(wnd);
            currentMethod = cnt.getActiveLineMethod();

            method = cnt.getMethodByName(this.form.Controls.НадписьНазвание.Заголовок)

            if(!method){
                Message("Не обнаружили метод... ");
                return;
            }

            if (currentMethod.Name == method.Name){
                lineToInsertName = wnd.GetCaretPos().beginRow;
            } else {
                lineToInsertName = method.EndLine - 1; 
            }

            if (position == "afterProcedure"){
                lineToInsertProcedure = method.StartLine - 2;
            } else if(position == "beforeProcedure"){
                lineToInsertProcedure = this.EndLine + 1;
            } else {
                //Вот тут 
                lineToInsertProcedure = this.getPositionForAddLines(wnd, context, cnt)
            }
        }

        lineToInsertProcedure = lineToInsertProcedure==0?1:lineToInsertProcedure;
        lineToInsertName = lineToInsertName==0?1:lineToInsertName;

        //Определим позицию куда вставлять новый текст процедуры. 

        if (isSelection){
            wnd.SetSelectedText(newName + "()");
        } else {
            wnd.InsertLine(lineToInsertName, newName+"()");
        }

        wnd.InsertLine(lineToInsertProcedure, newProcedure);
        //Теперь расчитаем позицию для установки курсора. 
        wnd.SetCaretPos(lineToInsertProcedure + 3, 4);

        
    },

    ServerNoContext_Click:function(btn){
        this.createFunction("atServerNoContext", this.form.Controls.PositionCreate.Value);
        this.close();
    }, 

    Server_Click:function(btn){
        this.createFunction("atServer", this.form.Controls.PositionCreate.Value);
        this.close();
    }

})


// # DebugSilence
// 
// Во время активной разработки очень часто приходится перезапускать предприятие, открытое в режиме отладки 
// при этом каждый раз от пользователя ждут различных действий, таких как подтверждение перезапуска предприятия и подтверждения обновления базы данных. По факту получается для перезаска отладки неоходимо нажать F5 , потом ответить утвердительно на вопрос "Перезапустить предприятие", и снова ответь на вопрос "Обнвоить ли базу данных!". 
// Если посчитать сколько в день приходиться нажимать F5 потом enter, enter, то в итоге родился такой скрипт, который анализирует текущее состояние базы (отличаются конфигурации), при этом у нас включен режим отладки - значит мы в режиме отладки что-то подправили и теперь пытаемся перезапустить предприяте.
DebugModeHelper = stdlib.Class.extend({

    settingsRootPath : 'sillenceDebugModeHelper',
    defaultSettings : {
            use: false
    },

    construct : function () {    
        DebugModeHelper._instance = this;
        
        this.settings = SettingsManagement.CreateManager(this.settingsRootPath, this.defaultSettings);
        this.loadSettings();
        //events.connect(windows, "onDoModal", this);
        //stdcommands.CDebug.Start.addHandler(this, "onRestartDebug");
        this.first = false;
    },

    loadSettings:function(){
        this.settings.LoadSettings();
        if(!this.settings.current.use)
            this.settings.current.use = false;

        if (this.settings.current.use==true){
            stdcommands.CDebug.Start.addHandler(this, "onRestartDebug");
        } else {
            try{
                stdcommands.CDebug.Start.delHandler(this, "onRestartDebug");
            } catch (e) {}
             try{
                events.disconnect(windows, "onDoModal", this, "onDoModalRestart");
            } catch (e) {}

        }

    },

    changeSettings : function(){

        var values = v8New('СписокЗначений');
        values.add("on", "Включить перехват");
        values.add("off", "Выключить перехват");

        var name = "Перехват вопросов о перезапуске, сейчас "+ ((this.settings.use == true) ? " включен": " выключен");
        var dlg = new SelectValueDialog(name, values);
         if (dlg.selectValue()) {
             if (dlg.selectedValue=="on") {
                this.settings.current.use = true;
            } else if (dlg.selectedValue == "off") {
                this.settings.current.use = false;
            }
        }

        this.settings.SaveSettings();
        this.loadSettings();
    },

     //Перехватим событие о старте отладки . 
     onRestartDebug:function(cmd){
        if (!this.settings.current.use) {
            return;
        }
        if(cmd.isBefore)
        {   
            if (stdlib.isConfigsDifferent() && this.isDebugEnabled()){
                this.first = false;
                events.connect(windows, "onDoModal", this, "onDoModalRestart");    
            }
        }  else {
            try{
                events.disconnect(windows, "onDoModal", this, "onDoModalRestart");
            } catch (e) {}
        }
    },
    
    
    // Определим находимся ли в режиме отладки или нет. 
    isDebugEnabled:function()
    {
        // Команда "Перезапустить " неактивна - значит, мы не в режиме отладки.
        var state = stdcommands.CDebug.Restart.getState()
        return state && state.enabled
    },


    onDoModalRestart:function(dlgInfo){
        
        if(dlgInfo.caption == "Конфигуратор" && dlgInfo.stage == afterInitial)
        {
            try{
                var text = dlgInfo.form.getControl(0).value;
                if (text == "Приложение запущено. Перезапустить?") {
                    if (stdlib.isConfigsDifferent()){
                        this.first = true;
                        dlgInfo.form.sendEvent(dlgInfo.form.getControl(2).id, 0);
                    }
                } else if(text == "Редактируемая конфигурация отличается от конфигурации базы данных.\nОбновить конфигурацию базы данных?" && this.first) {
                    this.first = false;
                    dlgInfo.form.sendEvent(dlgInfo.form.getControl(2).id, 0);
                }
            } catch (e){
                logger.debug(e.description);

            };
        }
    }
})

function GetDebugModeHelper() {
    if (!DebugModeHelper._instance)
        new DebugModeHelper();
    return DebugModeHelper._instance;
}


function GetProcedurCreateHelper() {
    if (!ProcedurCreateHelper._instance)
        new ProcedurCreateHelper();
    return ProcedurCreateHelper._instance;
}


// ### Инициализия класса . 
//
//  Для отключения, достаточно только закомментировать данную строкоу.  
// TODO: добавить включение, выключение данного поведения. 
var dbg = GetDebugModeHelper();
var pch = GetProcedurCreateHelper();

SelfScript.self['macrosВкл/выкл вопросов при перезапуске во время отладки'] = function(){
    dbg.changeSettings();
    return true;

}


SelfScript.self['macrosНастройка создания обработчиков '] = function() {

    pch.show();
    pch.form.Panel.CurrentPage = pch.form.Panel.Pages.Find("Settings");
}
