﻿$engine JScript
$uname fileopen
$dname Открыть файл в Конфигураторе
$addin global
$addin stdcommands

global.connectGlobals(SelfScript)

/* Скрипт для перехвата сохранения файлов для Снегопата и немедленного открытия файлов
 * Автор		: Артур Аюханов aka artbear aartbear@gmail.com
 * Страница скрипта: http://snegopat.ru/scripts/wiki?name=fileopen.js
 * Дата создания: 04.10.2011
 * Описание		: http://snegopat.ru/forum/viewtopic.php?f=3&t=33
    Скрипт "Немедленное открытие файлов, сохраняемых в Конфигураторе"
    Я часто работаю с отчетами/обработками, встроенными в конфигурацию, следующим образом.
    Сохраняя встроенный отчет/обработку в файл, открываю этот файл (Файл - Открыть - поиск файла в папке, как правило, файлов в папке немало, секунда или две теряется  ), исправляю файл 
    и тут же, не перезапуская Конфигуратор и Предприятие, и не выгоняя пользователей в случае рабочей базы, открываю исправленный файл в режиме Предприятия.
    Уверен, что по такой схеме работаю не я один.
    И вот у меня родилась мысль немного автоматизировать этот процесс:
    разработчик сохраняет отчет/обработку в файл (тут как обычно), 
    а дальше вступает скрипт: перехватывает сохранение файла, узнает имя и путь файла, проверяет расширение этого файла,
    если это внешний отчет/обработка, то скрипт предлагает немедленно открыть этот файл в Конфигураторе.
    Планы: В настройках скрипта можно будет указать:
        всегда открывать такие файлы без вопроса или всегда задавать вопрос,
        расширения открываемых файлов - по умолчанию erf, epf 
 */
var _версияСкрипта = 0.4

var selectedFileName

// Подпишемся на перехват команды сохранения во внешний файл
// Также можно бы еще подписаться на "Файл-Сохранить как", но пока оставим это Артуру
events.addCommandHandler("{55C7732C-0C33-4394-ADCA-9D15082552B6}", 32, SelfScript.self, "hookSaveToExternalFileCommand")

var viewMetadata = null;
var refs = [];

// Перехват команды. Метод вызывается сначала перед выполнением команды, потом после выполнения (если не отменили)
function hookSaveToExternalFileCommand(cmd)
{
    if(cmd.isBefore)
    {
        selectedFileName = "";
        // Подпишемся на событие открытия диалога выбора файла
        events.connect(Designer, "onSelectFile", SelfScript.self)
        
        var view = windows.getFocusedView()//windows.getActiveView()
        if(!view){
            
        } else {
            viewMetadata = view;
        }
        
        
    }
    else
    {
        // Отпишемся от события открытия диалога выбора файла
        events.disconnect(Designer, "onSelectFile", SelfScript.self)
        // И откроем сохраненный файл
        if(selectedFileName.length){
            OpenFile(selectedFileName);
            if (viewMetadata){
                //проверим есть ли модуль менеджера, и откроем его в отдельном тексте. 
                doOpenCurStaticObject(stdcommands.Frntend.MDSearchRefs, true);
            }
        }
            
    }
}

// Функция ищет объект метаданных в контейнере по его имени
function findObject(root, name)
{
    //Message(name)
    var names = name.split(".")     // Разобъем имя на части
    for(var idx = 0; idx < names.length - 1; idx += 2)
    {
        var mdc = root.mdclass
        for(var i = 0, c = mdc.childsClassesCount; i < c; i++)
        {
            var cc = mdc.childClassAt(i)
            if(cc.name(1, false) == names[idx]){
                root = root.childObject(i, names[idx + 1])
                break
            }
        }
    }
    return root
}

// Перехват вывода в окно сообщений
function onMessage(params)
{
    refs.push(params.text)  // Запомним, что выводилось
    params.cancel = true    // Не будем реально выводить
}
// Перехват появления модального диалога
function onDoModal(dlgInfo)
{
    dlgInfo.cancel = true   // Просто сразу скажем, что в нем нажали OK
    dlgInfo.result = mbaOK
}

function doOpenCurStaticObject(command, forceShow)
{
    if(windows.modalMode != msNone)
        return false
    // Для начала проверим, что мы в окне метаданных
    var view = viewMetadata;
    if(!view)
        return false
    //debugger;
    var state = command.getState(view)
    if(!state || !state.enabled)
        return false
    refs = []
    // Ставим перехват на вывод в окно сообщений
    events.connect(Designer, "onMessage", SelfScript.self)
    // Подавляем показ диалога
    events.connect(windows, "onDoModal", SelfScript.self)
    // Посылаем команду поиска ссылок
    command.sendToView(view)
    // Убираем перехваты
    events.disconnect(Designer, "onMessage", SelfScript.self)
    events.disconnect(windows, "onDoModal", SelfScript.self)
    
    if(refs.length < 2)
        return false
    
    var rootObject = view.mdObj.container.rootObject
    
    var currentObject = findObject(rootObject, refs[0].match(/"(.+)"/)[1]);
    
     var mdc = currentObject.mdclass;
     
    for(var i = 0, c = mdc.propertiesCount; i < c; i++)
    {
        var mdProp = mdc.propertyAt(i)

        var mdPropName = mdc.propertyAt(i).name(1);
        if (mdPropName == "МодульМенеджера"){
            var text = currentObject.getModuleText(mdPropName);
            if (text.length > 0){
                textDoc = v8New("textdocument");
                textDoc.УстановитьТекст(text);
                textDoc.Показать("МодульМенеджера");
            }
        }
    }
    
    viewMetadata = null;
    refs = [];
    
    return true
}



// Обработчик, вызывается когда 1С использует диалог для запроса имени файла.
// Подключается и отключается в hookSaveToExternalFileCommand
function onSelectFile(selectFileData) //As ISelectFileData
{
    if(selectFileData.result != sfrNormal)
    {
        // Значит какой-то другой обработчик до нас уже что-то сделал
        if(selectFileData.result == sfrSelected && selectFileData.filesCount == 1)
        {
            // И при этом он не отменил операцию, и указал один файл
            // Запомним его, чтобы попытаться потом открыть
            selectedFileName = selectFileData.selectedFile(0)
        }
        return
    }
    // Сами получим имя файла для сохранения, чтобы знать, что открывать
    // ВАЖНО. Событие "onSelectFile" генерится как при системных запросах 1С,
    // так и при использовании в скриптах метода ДиалогВыбораФайла::Выбрать,
    // кроме тех случаев, когда ДиалогВыбораФайла::Выбрать используется в обработчиках
    // события onSelectFile, дабы избежать зацикливания.
	var selDlg = v8New("ДиалогВыбораФайла", РежимДиалогаВыбораФайла.Сохранение);
	selDlg.Заголовок = selectFileData.title.length ? selectFileData.title : "Сохранить во внешний отчет/обработку"
	selDlg.ПолноеИмяФайла = selectFileData.initialFileName
	selDlg.Каталог = selectFileData.folder
	
    for (var i = 0 ; i < selectFileData.filtersCount; i++) {
        filterVal = selectFileData.filterVal(i)
        selDlg.Фильтр += selectFileData.filterDescr(i)+" ("+filterVal+")|"+filterVal + "|"
    }
	if(!selDlg.Выбрать())
	    selectFileData.result = sfrCanceled
	else
	{
	    selectedFileName = selDlg.ПолноеИмяФайла
        selectFileData.addSelectedFile(selectedFileName)
	    selectFileData.result = sfrSelected
    }
}

var Text = '00000000-0000-0000-0000-000000000000' // простой текст
var TextOem = '74d75a51-58b7-46b0-931a-f3bac20e596e' // простой текст - кодировка Dos/Oem
var Epf = '0e0e54cf-253b-4fc9-a895-26897e1a51f7' // обработки
var Erf = '6d01520c-23c6-4301-86f7-e81268f07ee3' // отчеты
var Moxel = 'e555a6fe-768f-476a-bf4b-1d945aa56099' // табличный документ
var Configuration = 'c64ce8a4-a74d-40e9-996e-feadca885e11' // файл конфигурации
var Template = '03ad782c-900b-4594-bdb7-66ed05992b8b' // файл шаблонов

var kindArray = new Array(Text, TextOem, Epf, Erf, Moxel, Configuration, Template)

function Test()
{
    if (!Array.prototype.indexOf) {
      Array.prototype.indexOf = function (obj, fromIndex) {
        if (fromIndex == null) {
            fromIndex = 0;
        } else if (fromIndex < 0) {
            fromIndex = Math.max(0, this.length + fromIndex);
        }
        for (var i = fromIndex, j = this.length; i < j; i++) {
            if (this[i] === obj)
                return i;
        }
        return -1;
      };
    }
    assert(0, kindArray.indexOf(Text))
    assertNot(-1, kindArray.indexOf(Text))
}

    // try{
    //    _macrosTestOpenFile()
    // }
    // catch(e){
        // Message("ошибка macrosTestOpenFile() - "+e)
        // //Message("ошибка macrosTestOpenFile()") // - "+e)
    // }

// JScript source code
//24.08.2011 11:34:38 orefkov добавил:
//Тут возник вопрос по поводу открытия файлов в Конфигураторе.
//В объектной модели снегопата этого нет. Однако, вспомнив, как я открывал ert-шки в Конфигураторе 7ки в опенконфе, решил тряхнуть стариной и провернуть такой же финт ушами.

//А в опенконфе я их открывал просто - добавлял имя файла в начало MRU (это список недавно открываемых файлов, появляющийся в меню Файл) и посылал команду "Открыть нулевой файл из списка MRU".

//После десяти минут проб и ошибок в JS-Immediate, все получилось.

//Итак, по порядку

//Список MRU лежит (сюрприз!!!) в профайле 1С по пути (еще сюрприз!!!) - "App/MRUFileList"
//Этот список - обычный список значений, правда значения в нем не обычные.
//С помощью ЗначениеВСтрокуВнутр разобрался, что там лежит, а с помощью ЗначениеИзСтрокиВнутр - смог создать нужное мне значение.
//В этом значении главное - путь к файлу и какой-то гуид, очевидно обозначающий тип документа, который должен открывать этот файл. Для текстовых доков гуид прост - сплошные нули. Для других видов доков надо исследовать, перебирая значения из MRUFileList, и смотря на них через ЗначениеВСтрокуВнутр
//Далее просто впихнул свое значение в начало списка, сохранил весь список в профайл, и послал нужную команду.
//Вот код:

function _macrosTestOpenFile()
{
    //var filepath = "c:\\test\\snegopat\\core\\std\\scripts\\0_global_context.js"
    var filepath = "T:\\Snegopat\\Starter\\scripts\\configCaption.js"

    Message("1")
    try{
        OpenFile(filepath)        
    }
    catch(e){ Message("Неудача") }

    Message("2")
    try{
        OpenFile(filepath, "")
    }
    catch(e){ Message("Неудача") }

    Message("3")
    try{
      OpenFile(filepath, "1")
        Message("Неудача")
    }
    catch(e){}
}

function assert(p1, p2)
{
    if (p1 != p2) 
        throw "Значение <"+p1+"> не равно <"+p2+">, а ожидали равенство";
}
function assertNot(p1, p2)
{
    if (p1 == p2) 
        throw "Значение <"+p1+"> равно <"+p2+">, а ожидали неравенство";
}

//Еще один параметр в методе OpenFile - тип документа (их лучше задать символьными константами)
//Если тип не передан, попытаться найти его по расширению файла.
//Если расширение непонятное, использовать нулевой гуид.
//
//то есть чтобы можно было пользоваться так:
//$addin fileopen of
//fo.OpenFile(path)
//fo.OpenFile(path, fo.TextOEM)
//fo.OpenFile(path, fo.Template)
//и тд и тп.
//
function OpenFile(filepath) //, filekind)
{
    Message("Открываем файл "+filepath)
    // Подготовим наше значение для MRU - списка
    kind = Text
        // if (filekind)
            // kind = filekind
        // if (-1 == kindArray.indexOf(kind))
            // throw "Неверный параметр filekind = "+filekind
    
    var mruItem = ЗначениеИзСтрокиВнутр('{"#",36973550-6bbb-11d5-bf72-0050bae2bc79,\n' +
    '{1,\n' +
    '{"file://' + filepath + '",0},' + kind + '}\n' +
    '}')

    // Получим текущий список MRU из настроек
    var mru = profileRoot.getValue("App/MRUFileList")
    // Если там уже есть наше значение, удалим его
    var hasInMru = mru.НайтиПоЗначению(mruItem)
    if(hasInMru)
       mru.Удалить(hasInMru)
    // Если список полон, удалим последний элемент
    if(mru.Количество() == 8)
       mru.Удалить(7)
    // Вставим значение для нашего файла в начало списка
    mru.Вставить(0, mruItem)
    // Сохраним MRU-список обратно в настройки
    profileRoot.setValue("App/MRUFileList", mru)
    // И зашлем команду
    stdcommands.Frame.RecentFile.getState()
    stdcommands.Frame.RecentFile.send(0)
    
    return true
}

//Вот так пытливый ум преодолевает преграды.

//ЗЫ: Для просмотра MRU использовался следующий код:
function macrosИсследоватьMRU()
{
    var mru = profileRoot.getValue("App/MRUFileList")
    for(var i = new Enumerator(mru); !i.atEnd(); i.moveNext())
    {
        Message("v=" + ЗначениеВСтрокуВнутр(i.item().Значение), mExc3)
    }
}
//В просмотре профайла мне помогло scripts\devtools\pflview.js
