Подготовка собственного дерева товара без изменения кода A-01: пять настоящих атрибутов нормализуются, единственная служебная ячейка удаляется. Товарная строка и остальная структура полностью проверяются.
Где и как запускать
Серверный неглобальный модуль своей новой File CF 8.5.1.1343; толстая управляемая форма получает только проверенные числа и строку. Запуск 1С не проверялся.
Подготовка
Вручную создайте НОВУЮ пустую файловую учебную ИБ только C:\1c-labs\OwnHtmlTemplate6_42b9dd93-7d20-4c85-b94f-28e06b2b3d58\IB. Имя конфигурации УчебныйHTMLШаблон85; ОсновнойРежимЗапуска=УправляемоеПриложение; РежимСовместимости=НеИспользовать. Windows 8.5.1.1343, толстый управляемый клиент. Точная строка соединения File="этот путь"; без дополнительных параметров. UUID в пути — маркер лаборатории, не доказательство идентичности базы.
Установите отдельное свойство конфигурации Версия точно OwnHtmlTemplate6-7ac232f0-63a9-4fc8-a59e-b8c2dbfc6a41. Не добавляйте документы, справочники, регистры и расширения. Роль HTMLШаблон85Администратор с учебными правами Администрирование и АктивныеПользователи; один собственный пользователь HTMLШаблон85Ученик с этой ролью. Код читает настоящие UUID пользователя и соединения, не создаёт пользователей и не меняет права. После обновления учебной CF закройте Конфигуратор и остальные сеансы.
Общие → Общие модули → HTMLШаблон85. Сервер=Да; Глобальный, ВызовСервера, Привилегированный, КлиентУправляемоеПриложение, КлиентОбычноеПриложение, ВнешнееСоединение=Нет. ПовторноеИспользованиеВозвращаемыхЗначений=НеИспользовать. Вставьте весь основной код; ОчиститьСвойШаблон85 экспортируется без аргументов. Все реальные HTML объекты создаются только здесь.
Общие → Общие формы → ПодготовкаHTMLШаблона85. Добавьте Число15.2 для пятнадцати реквизитов: ДоВвода85, ПослеВвода85, ДоСвязи85, ПослеСвязи85, ДоМета85, ПослеМета85, ДоВставки85, ПослеВставки85, ДоЛинии85, ПослеЛинии85, ЯчеекДо85, ЯчеекПосле85, Количество85, Цена85, Сумма85. Код85 — Строка20, Протокол85 — неограниченная Строка, Остановка85/Завершено85 — Булево, исходно Ложь. Поля результата и многострочный протокол только для просмотра; флаги скрыты.
Собственная команда ОчиститьШаблон85, Действие=ОчиститьШаблон85(Команда), кнопка командной панели связана с этой командой. Вставьте полный FORM. Сервер формы проверяет неизменяемый пакет, ровно16 полей, точные имена, наличие, все типы и значения ДО чтения прямых полей и переноса результата. Клиент повторно проверяет пятнадцать чисел и строку. Деревья, атрибуты и reader через RPC не передаются.
Конфигурация → Открыть модуль управляемого приложения: вставьте APPLICATION. Настоящий ПриНачалеРаботыСистемы() откроет только эту учебную форму после проверки File/version/config. Startup и callback команды вручную не вызывайте; это реальные штатные события.
Читается только фиксированная короткая строка основного кода. Два span A-/01, безопасные input/link/meta/embed/hr без адресов ресурсов, собственные товарная и служебная строки. Это серверное дерево в памяти: не поле HTML, не DOM браузера, не рендеринг, не отправка формы. ЧтениеHTML закрывается при успехе и ошибке; ПостроительDOM.Прочитать исполняется на сервере, не в тонком клиенте.
Получение элемента по id описано общим типом ЭлементHTML, поэтому каждый специальный тип и документ проверяются фактически. Прочитанные ТекстHTML клонируются из своих span. СоздатьТекстовыйУзел, ТекстDOM и фиктивные конструкторы HTML подтипов не используются. Детей пяти пустым элементам не добавляем: два текста становятся детьми только пяти собственных data-study-code атрибутов.
Начальные пустота и отсутствие владельца у нового атрибута — осторожное проверяемое условие, не гарантия. ЭлементВладелец в справке имеет тип ЭлементHTML. Неожиданный ответ или исключение означают отказ, не успех. Значение атрибута не присваивается: два текста дают A-01, пять конкретных Normalize должны фактически объединить их в один с тем же значением.
Каждая граница перечитывает весь свой граф и удерживаемую идентичность атрибутов. Отдельный глубокий клон без ровно пяти добавленных атрибутов сравнивается с исходным целым логическим деревом. Удаление проверочных атрибутов затрагивает только копию; реальные атрибуты не удаляются и не восстанавливаются.
Для DeleteCell допускается только noteRow с ровно одной своей noteCell: Ячейки — настоящая readonly КоллекцияЭлементовHTML, не произвольный Массив. Вызов ровно без аргументов и без присвоения результата, как в установленной справке. Выбор ячейки в многоклеточной строке справкой не объяснён и здесь не обещается. Перед вызовом на отдельной ожидаемой копии через RemoveChild убирается её точная служебная ячейка; рабочее дерево после DeleteCell должно полностью совпасть с этой копией.
РодительскийУзел у ячейки в справке объявлен как ЭлементЯчейкаТаблицыHTML/Неопределено, хотя описание говорит о родителе. Код не обещает тип Row: проверяет фактическую идентичность своему noteRow, а после удаления отсутствие родителя. Служебная ячейка 1→0; товарные code/quantity/price и сумма750 сохраняются.
Один запуск своей команды после обновления только новой учебной CF. При любой ошибке или неподходящем состоянии постоянный STOP и исключение: повтор, восстановление и продолжение запрещены. Ошибка может оставить частичные изменения только собственного дерева в памяти. Не сбрасывайте флаги в отладчике; новый ручной протокол начинайте после закрытия клиента.
1С, CF, ИБ, GUI, сеть и сохранение файлов здесь не выполнялись. Все числа далее — ожидаемый будущий ручной протокол, не уже выполненный запуск.
Процедура ПроверитьСтенд85()
Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnHtmlTemplate6_42b9dd93-7d20-4c85-b94f-28e06b2b3d58\IB"";" Тогда
ВызватьИсключение "Нужен только собственный точно подготовленный File стенд 8.5.1.1343";
КонецЕсли;
Если Метаданные.Документы.Количество() <> 0 Или Метаданные.Справочники.Количество() <> 0
Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0
Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0
Или Метаданные.ОбщиеМодули.Количество() <> 1 Или Метаданные.ОбщиеМодули.Найти("HTMLШаблон85") = Неопределено
Или Метаданные.ОбщиеФормы.Количество() <> 1 Или Метаданные.ОбщиеФормы.Найти("ПодготовкаHTMLШаблона85") = Неопределено Тогда
ВызватьИсключение "Оставьте только свой общий модуль и общую форму, без прикладных данных";
КонецЕсли;
МодульМД = Метаданные.ОбщиеМодули.Найти("HTMLШаблон85");
Если Не МодульМД.Сервер Или МодульМД.Глобальный Или МодульМД.ВызовСервера Или МодульМД.Привилегированный
Или МодульМД.КлиентУправляемоеПриложение Или МодульМД.КлиентОбычноеПриложение Или МодульМД.ВнешнееСоединение Тогда
ВызватьИсключение "Общий модуль должен быть только серверным и неглобальным";
КонецЕсли;
Если МодульМД.ПовторноеИспользованиеВозвращаемыхЗначений <> Метаданные.СвойстваОбъектов.ПовторноеИспользованиеВозвращаемыхЗначений.НеИспользовать Тогда
ВызватьИсключение "Запретите кеширование возвращаемых значений";
КонецЕсли;
Если РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.БазаДанных).Количество() <> 0
Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансАктивные).Количество() <> 0
Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансОтключенные).Количество() <> 0 Тогда
ВызватьИсключение "В своей пустой лаборатории расширения не допускаются";
КонецЕсли;
Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Если Текущий = Неопределено Или Пользователи.Количество() <> 1 Тогда
ВызватьИсключение "Нужен один настоящий собственный пользователь";
КонецЕсли;
Если Текущий.Имя <> "HTMLШаблон85Ученик" Или Пользователи[0].УникальныйИдентификатор <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Не совпала настоящая идентичность учебного пользователя";
КонецЕсли;
Соединения = ПолучитьСоединенияИнформационнойБазы();
Если Соединения.Количество() <> 1 Тогда
ВызватьИсключение "Закройте Конфигуратор и другие сеансы своей лаборатории";
КонецЕсли;
Соединение = Соединения[0];
Если Соединение.НомерСоединения <> НомерСоединенияИнформационнойБазы()
Или Соединение.Пользователь.УникальныйИдентификатор <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Единственное соединение должно принадлежать текущему своему пользователю";
КонецЕсли;
КонецПроцедуры
Функция ПрочитатьСвойHTML85()
HTML = "<html><head><title>Свой шаблон карточки</title><meta id='meta' name='study-template' content='A-01'><link id='link' rel='study-template'></head><body><span id='prefix'>A-</span><span id='suffix'>01</span><form id='form'><input id='input' type='text' name='code' value='A-01'></form><hr id='line'><embed id='embed'><table id='table'><tbody><tr id='product'><td id='code'>A-01</td><td id='quantity'>3</td><td id='price'>250</td></tr><tr id='noteRow'><td id='noteCell'>Учебная служебная отметка A-01</td></tr></tbody></table></body></html>";
Чтение = Новый ЧтениеHTML;
Чтение.УстановитьСтроку(HTML);
Попытка
Построитель = Новый ПостроительDOM;
Документ = Построитель.Прочитать(Чтение);
Исключение
Чтение.Закрыть();
ВызватьИсключение;
КонецПопытки;
Чтение.Закрыть();
Если ТипЗнч(Документ) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Свой HTML reader должен фактически дать ДокументHTML";
КонецЕсли;
Возврат Документ;
КонецФункции
Функция ПолучитьСвойУзел85(Документ, Идентификатор, ИмяТипа)
Если ТипЗнч(Документ) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Нужен настоящий собственный HTML документ";
КонецЕсли;
Узел = Документ.ПолучитьЭлементПоИдентификатору(Идентификатор);
Если Узел = Неопределено Тогда
ВызватьИсключение "В собственной подготовке не найден точный узел " + Идентификатор;
КонецЕсли;
Если ТипЗнч(Узел) <> Тип(ИмяТипа) Или Узел.ДокументВладелец <> Документ Тогда
ВызватьИсключение "Не совпали фактический специальный тип и собственный документ узла";
КонецЕсли;
Возврат Узел;
КонецФункции
Функция ПрочитатьСвойСчет85(Документ)
Код = ПолучитьСвойУзел85(Документ, "code", "ЭлементЯчейкаТаблицыHTML");
Количество = ПолучитьСвойУзел85(Документ, "quantity", "ЭлементЯчейкаТаблицыHTML");
Цена = ПолучитьСвойУзел85(Документ, "price", "ЭлементЯчейкаТаблицыHTML");
Если ТипЗнч(Код.ТекстовоеСодержимое) <> Тип("Строка") Или Код.ТекстовоеСодержимое <> "A-01"
Или ТипЗнч(Количество.ТекстовоеСодержимое) <> Тип("Строка") Или Количество.ТекстовоеСодержимое <> "3"
Или ТипЗнч(Цена.ТекстовоеСодержимое) <> Тип("Строка") Или Цена.ТекстовоеСодержимое <> "250" Тогда
ВызватьИсключение "Проверьте весь собственный код товара, количество и цену";
КонецЕсли;
ЧислоКоличества = Число(Количество.ТекстовоеСодержимое);
ЧислоЦены = Число(Цена.ТекстовоеСодержимое);
Сумма = ЧислоКоличества * ЧислоЦены;
Если ЧислоКоличества <> 3 Или ЧислоЦены <> 250 Или Сумма <> 750 Тогда
ВызватьИсключение "Не подтвердился настоящий расчет своего счета 3 x 250 = 750";
КонецЕсли;
Возврат Новый ФиксированнаяСтруктура("Количество,Цена,Сумма", ЧислоКоличества, ЧислоЦены, Сумма);
КонецФункции
Функция ПрочитатьСвойТекст85(Документ, Идентификатор, ОжидаемыйТекст)
Контейнер = ПолучитьСвойУзел85(Документ, Идентификатор, "ЭлементHTML");
Если Контейнер.ДочерниеУзлы.Количество() <> 1 Тогда
ВызватьИсключение "В своем span нужен ровно один реальный HTML текст";
КонецЕсли;
Текст = Контейнер.ДочерниеУзлы.Элемент(0);
Если ТипЗнч(Текст) <> Тип("ТекстHTML") Или Текст.ДокументВладелец <> Документ
Или ТипЗнч(Текст.ЗначениеУзла) <> Тип("Строка") Или Текст.ЗначениеУзла <> ОжидаемыйТекст Тогда
ВызватьИсключение "Не совпали настоящий HTML текст, его документ и вся исходная часть кода товара";
КонецЕсли;
Возврат Текст;
КонецФункции
Функция СоздатьСвоюМетку85(Документ)
Атрибут = Документ.СоздатьАтрибут("data-study-code");
Если ТипЗнч(Атрибут) <> Тип("АтрибутHTML") Или Атрибут.ДокументВладелец <> Документ
Или ТипЗнч(Атрибут.Имя) <> Тип("Строка") Или Атрибут.Имя <> "data-study-code"
Или ТипЗнч(Атрибут.Значение) <> Тип("Строка") Или Атрибут.Значение <> "" Или Атрибут.ДочерниеУзлы.Количество() <> 0 Тогда
ВызватьИсключение "Новый собственный атрибут допускается только после фактически подтвержденного пустого состояния";
КонецЕсли;
Если Атрибут.ЭлементВладелец <> Неопределено Тогда
ВызватьИсключение "Осторожная проверка своего нового атрибута не подтвердила отсутствие владельца";
КонецЕсли;
Префикс = ПрочитатьСвойТекст85(Документ, "prefix", "A-").КлонироватьУзел(Истина);
Суффикс = ПрочитатьСвойТекст85(Документ, "suffix", "01").КлонироватьУзел(Истина);
Если ТипЗнч(Префикс) <> Тип("ТекстHTML") Или ТипЗнч(Суффикс) <> Тип("ТекстHTML")
Или Префикс.ДокументВладелец <> Документ Или Суффикс.ДокументВладелец <> Документ
Или Префикс.РодительскийУзел <> Неопределено Или Суффикс.РодительскийУзел <> Неопределено
Или Префикс.ЗначениеУзла <> "A-" Или Суффикс.ЗначениеУзла <> "01" Тогда
ВызватьИсключение "Нужны два настоящих отсоединенных TextHTML клона этого же собственного документа";
КонецЕсли;
ДобавленПрефикс = Атрибут.ДобавитьДочерний(Префикс);
Если ТипЗнч(ДобавленПрефикс) <> Тип("ТекстHTML") Или Не ДобавленПрефикс.УзелИдентичен(Префикс)
Или Атрибут.ДочерниеУзлы.Количество() <> 1 Тогда
ВызватьИсключение "Не подтвержден реальный первый текст своего атрибута";
КонецЕсли;
ПервыйРебенок = Атрибут.ДочерниеУзлы.Элемент(0);
Если ТипЗнч(ПервыйРебенок) <> Тип("ТекстHTML") Или ПервыйРебенок.ДокументВладелец <> Документ
Или ПервыйРебенок.ЗначениеУзла <> "A-" Или Не ПервыйРебенок.УзелИдентичен(Префикс)
Или Атрибут.Значение <> "A-" Или Атрибут.ЭлементВладелец <> Неопределено Тогда
ВызватьИсключение "Перед вторым native Add перечитываем весь actual первый текст и свой еще отсоединенный атрибут";
КонецЕсли;
ДобавленСуффикс = Атрибут.ДобавитьДочерний(Суффикс);
Если ТипЗнч(ДобавленСуффикс) <> Тип("ТекстHTML") Или Не ДобавленСуффикс.УзелИдентичен(Суффикс) Или Атрибут.ДочерниеУзлы.Количество() <> 2
Или Атрибут.Значение <> "A-01" Тогда
ВызватьИсключение "Не подтверждены второй текст и native значение всего собственного атрибута";
КонецЕсли;
ПервыйРебенок = Атрибут.ДочерниеУзлы.Элемент(0);
ВторойРебенок = Атрибут.ДочерниеУзлы.Элемент(1);
Если ТипЗнч(ПервыйРебенок) <> Тип("ТекстHTML") Или ТипЗнч(ВторойРебенок) <> Тип("ТекстHTML")
Или ПервыйРебенок.ДокументВладелец <> Документ Или ВторойРебенок.ДокументВладелец <> Документ
Или ПервыйРебенок.ЗначениеУзла <> "A-" Или ВторойРебенок.ЗначениеУзла <> "01"
Или Не ПервыйРебенок.УзелИдентичен(Префикс) Или Не ВторойРебенок.УзелИдентичен(Суффикс)
Или Атрибут.ЭлементВладелец <> Неопределено Тогда
ВызватьИсключение "Весь staged атрибут должен остаться своим и содержать два точных HTML клона в нужном порядке";
КонецЕсли;
Возврат Атрибут;
КонецФункции
Функция ПроверитьМетку85(Элемент, Документ, НужныеДети)
Если ТипЗнч(Элемент) <> Тип("ЭлементВводаHTML") И ТипЗнч(Элемент) <> Тип("ЭлементСвязьHTML")
И ТипЗнч(Элемент) <> Тип("ЭлементМетаHTML") И ТипЗнч(Элемент) <> Тип("ЭлементВставкаHTML") И ТипЗнч(Элемент) <> Тип("ЭлементЛинияHTML") Тогда
ВызватьИсключение "Допускаются только пять своих проверенных специальных типов";
КонецЕсли;
Если Элемент.ДокументВладелец <> Документ Или Элемент.ДочерниеУзлы.Количество() <> 0 Или Элемент.ЕстьДочерниеУзлы() Тогда
ВызватьИсключение "Свой элемент должен остаться в своём документе без дочернего содержимого";
КонецЕсли;
Атрибут = Элемент.ПолучитьУзелАтрибута("data-study-code");
Если НужныеДети = 0 Тогда
Если Атрибут <> Неопределено Или Элемент.ЕстьАтрибут("data-study-code") Тогда
ВызватьИсключение "Нельзя заменить уже присутствующий служебный атрибут";
КонецЕсли;
Возврат Неопределено;
КонецЕсли;
Если НужныеДети <> 1 И НужныеДети <> 2 Тогда
ВызватьИсключение "Допускаются только два проверяемых состояния собственного атрибута";
КонецЕсли;
Если ТипЗнч(Атрибут) <> Тип("АтрибутHTML") Или Атрибут.ДокументВладелец <> Документ
Или Атрибут.ЭлементВладелец <> Элемент Или Атрибут.Имя <> "data-study-code"
Или ТипЗнч(Атрибут.Значение) <> Тип("Строка") Или Атрибут.Значение <> "A-01"
Или Атрибут.ДочерниеУзлы.Количество() <> НужныеДети Тогда
ВызватьИсключение "Не подтверждён весь собственный атрибут";
КонецЕсли;
Для Индекс = 0 По НужныеДети - 1 Цикл
Текст = Атрибут.ДочерниеУзлы.Элемент(Индекс);
ОжидаемыйТекст = ?(НужныеДети = 1, "A-01", ?(Индекс = 0, "A-", "01"));
Если ТипЗнч(Текст) <> Тип("ТекстHTML") Или Текст.ДокументВладелец <> Документ
Или ТипЗнч(Текст.ЗначениеУзла) <> Тип("Строка") Или Текст.ЗначениеУзла <> ОжидаемыйТекст Тогда
ВызватьИсключение "Неверны фактические типы, документ, значения или порядок текстов атрибута";
КонецЕсли;
КонецЦикла;
Возврат Атрибут;
КонецФункции
Функция ПроверитьСлужебнуюСтроку85(Документ, ЕстьЯчейка)
Если ТипЗнч(ЕстьЯчейка) <> Тип("Булево") Тогда
ВызватьИсключение "Нужно точное булево состояние собственной служебной строки";
КонецЕсли;
Строка = ПолучитьСвойУзел85(Документ, "noteRow", "ЭлементСтрокаТаблицыHTML");
Если ТипЗнч(Строка.Ячейки) <> Тип("КоллекцияЭлементовHTML") Или ТипЗнч(Строка.ДочерниеУзлы) <> Тип("СписокУзловHTML")
Или Строка.Ячейки.Количество() <> ?(ЕстьЯчейка, 1, 0) Или Строка.ДочерниеУзлы.Количество() <> ?(ЕстьЯчейка, 1, 0) Тогда
ВызватьИсключение "Строка допускается только с одной своей ячейкой или после её подтверждённого удаления";
КонецЕсли;
Если Не ЕстьЯчейка Тогда
Если Документ.ПолучитьЭлементПоИдентификатору("noteCell") <> Неопределено Тогда
ВызватьИсключение "Удалённая ячейка не должна оставаться в собственном дереве";
КонецЕсли;
Возврат Неопределено;
КонецЕсли;
Ячейка = Строка.Ячейки.Элемент(0);
Если ТипЗнч(Ячейка) <> Тип("ЭлементЯчейкаТаблицыHTML") Или Ячейка.ДокументВладелец <> Документ
Или Не Ячейка.УзелИдентичен(ПолучитьСвойУзел85(Документ, "noteCell", "ЭлементЯчейкаТаблицыHTML"))
Или Не Ячейка.УзелИдентичен(Строка.ДочерниеУзлы.Элемент(0))
Или ТипЗнч(Ячейка.ТекстовоеСодержимое) <> Тип("Строка") Или Ячейка.ТекстовоеСодержимое <> "Учебная служебная отметка A-01" Тогда
ВызватьИсключение "Нужна именно единственная собственная служебная ячейка, не товарная";
КонецЕсли;
Родитель = Ячейка.РодительскийУзел;
Если Родитель = Неопределено Тогда
ВызватьИсключение "До удаления служебная ячейка должна быть присоединена";
КонецЕсли;
Если Не Родитель.УзелИдентичен(Строка) Тогда
ВызватьИсключение "Фактический родитель должен быть именно своей служебной строкой";
КонецЕсли;
Возврат Ячейка;
КонецФункции
Функция ПодготовитьОжидаемоеДерево85(Исходный)
Ожидаемый = Исходный.КлонироватьУзел(Истина);
Если ТипЗнч(Ожидаемый) <> Тип("ДокументHTML") Или Ожидаемый.УзелИдентичен(Исходный) Или Не Ожидаемый.УзелРавен(Исходный) Тогда
ВызватьИсключение "Нужна отдельная полностью равная исходному дереву ожидаемая копия";
КонецЕсли;
ЯчейкаОжидаемойКопии = ПроверитьСлужебнуюСтроку85(Ожидаемый, Истина);
СтрокаОжидаемойКопии = ПолучитьСвойУзел85(Ожидаемый, "noteRow", "ЭлементСтрокаТаблицыHTML");
УдалённаяЯчейка = СтрокаОжидаемойКопии.УдалитьДочерний(ЯчейкаОжидаемойКопии);
Если ТипЗнч(УдалённаяЯчейка) <> Тип("ЭлементЯчейкаТаблицыHTML") Или Не УдалённаяЯчейка.УзелИдентичен(ЯчейкаОжидаемойКопии)
Или УдалённаяЯчейка.ДокументВладелец <> Ожидаемый Или УдалённаяЯчейка.РодительскийУзел <> Неопределено Тогда
ВызватьИсключение "В ожидаемой копии должна быть отделена только её точная служебная ячейка";
КонецЕсли;
ПроверитьСлужебнуюСтроку85(Ожидаемый, Ложь);
ПроверитьСлужебнуюСтроку85(Исходный, Истина);
ПрочитатьСвойСчет85(Ожидаемый);
Возврат Ожидаемый;
КонецФункции
Процедура ПроверитьГраф85(Документ, Исходный, Установлено, Нормализовано, Метки, ЕстьЯчейка)
ПроверитьСтенд85();
Если ТипЗнч(Установлено) <> Тип("Число") Или ТипЗнч(Нормализовано) <> Тип("Число")
Или (Установлено <> 0 И Установлено <> 1 И Установлено <> 2 И Установлено <> 3 И Установлено <> 4 И Установлено <> 5)
Или (Нормализовано <> 0 И Нормализовано <> 1 И Нормализовано <> 2 И Нормализовано <> 3 И Нормализовано <> 4 И Нормализовано <> 5)
Или Нормализовано > Установлено Или ТипЗнч(Метки) <> Тип("Массив") Или Метки.Количество() <> 5 Тогда
ВызватьИсключение "Неизвестен этап собственного дерева или набор удерживаемых атрибутов";
КонецЕсли;
ПрочитатьСвойСчет85(Документ);
ПрочитатьСвойТекст85(Документ, "prefix", "A-");
ПрочитатьСвойТекст85(Документ, "suffix", "01");
ПроверитьСлужебнуюСтроку85(Документ, ЕстьЯчейка);
Идентификаторы = СтрРазделить("input,link,meta,embed,line", ",");
ИменаТипов = СтрРазделить("ЭлементВводаHTML,ЭлементСвязьHTML,ЭлементМетаHTML,ЭлементВставкаHTML,ЭлементЛинияHTML", ",");
Копия = Документ.КлонироватьУзел(Истина);
Если ТипЗнч(Копия) <> Тип("ДокументHTML") Или Копия.УзелИдентичен(Документ) Или Не Копия.УзелРавен(Документ) Тогда
ВызватьИсключение "Для проверки целого дерева нужна фактическая отдельная глубокая копия";
КонецЕсли;
Для Индекс = 0 По 4 Цикл
НужныеДети = ?(Индекс < Нормализовано, 1, ?(Индекс < Установлено, 2, 0));
Элемент = ПолучитьСвойУзел85(Документ, Идентификаторы[Индекс], ИменаТипов[Индекс]);
Атрибут = ПроверитьМетку85(Элемент, Документ, НужныеДети);
Если НужныеДети = 0 Тогда
Если Метки[Индекс] <> Неопределено Тогда
ВызватьИсключение "До attachment удерживаемый атрибут не должен подменять отсутствие настоящего";
КонецЕсли;
Иначе
Если ТипЗнч(Метки[Индекс]) <> Тип("АтрибутHTML") Или Не Атрибут.УзелИдентичен(Метки[Индекс]) Тогда
ВызватьИсключение "После каждой операции сохраняется идентичность именно своего установленного атрибута";
КонецЕсли;
КонецЕсли;
Если Элемент.ЕстьАтрибут("src") Или Элемент.ЕстьАтрибут("href") Или Элемент.ЕстьАтрибут("data") Тогда
ВызватьИсключение "В собственном шаблоне не допускаются адреса ресурсов";
КонецЕсли;
ЭлементКопии = ПолучитьСвойУзел85(Копия, Идентификаторы[Индекс], ИменаТипов[Индекс]);
АтрибутКопии = ПроверитьМетку85(ЭлементКопии, Копия, НужныеДети);
Если НужныеДети > 0 Тогда
Удалена = ЭлементКопии.УдалитьУзелАтрибута(АтрибутКопии);
Если ТипЗнч(Удалена) <> Тип("АтрибутHTML") Или Не Удалена.УзелИдентичен(АтрибутКопии) Тогда
ВызватьИсключение "Удаляем только точный добавленный атрибут проверочной копии";
КонецЕсли;
КонецЕсли;
ПроверитьМетку85(ЭлементКопии, Копия, 0);
КонецЦикла;
Если ТипЗнч(Исходный) <> Тип("ДокументHTML") Или Не Копия.УзелРавен(Исходный) Тогда
ВызватьИсключение "Кроме разрешённых собственных изменений изменилось остальное логическое дерево";
КонецЕсли;
Для Индекс = 0 По 4 Цикл
Элемент = ПолучитьСвойУзел85(Документ, Идентификаторы[Индекс], ИменаТипов[Индекс]);
НужныеДети = ?(Индекс < Нормализовано, 1, ?(Индекс < Установлено, 2, 0));
Атрибут = ПроверитьМетку85(Элемент, Документ, НужныеДети);
Если НужныеДети > 0 Тогда
Если Не Атрибут.УзелИдентичен(Метки[Индекс]) Тогда
ВызватьИсключение "Проверка отдельной копии не должна изменить живой атрибут";
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция ОчиститьСвойШаблон85() Экспорт
ПроверитьСтенд85();
Документ = ПрочитатьСвойHTML85();
Исходный = Документ.КлонироватьУзел(Истина);
Если ТипЗнч(Исходный) <> Тип("ДокументHTML") Или Исходный.УзелИдентичен(Документ) Или Не Исходный.УзелРавен(Документ) Тогда
ВызватьИсключение "Начальный снимок должен быть отдельным и равным целому собственному дереву";
КонецЕсли;
Метки = Новый Массив;
Для Индекс = 0 По 4 Цикл
Метки.Добавить(Неопределено);
КонецЦикла;
ПроверитьГраф85(Документ, Исходный, 0, 0, Метки, Истина);
Идентификаторы = СтрРазделить("input,link,meta,embed,line", ",");
ИменаТипов = СтрРазделить("ЭлементВводаHTML,ЭлементСвязьHTML,ЭлементМетаHTML,ЭлементВставкаHTML,ЭлементЛинияHTML", ",");
Для Индекс = 0 По 4 Цикл
Элемент = ПолучитьСвойУзел85(Документ, Идентификаторы[Индекс], ИменаТипов[Индекс]);
Создана = СоздатьСвоюМетку85(Документ);
ПроверитьГраф85(Документ, Исходный, Индекс, 0, Метки, Истина);
Заменена = Элемент.УстановитьУзелАтрибута(Создана);
Если Заменена <> Неопределено Тогда
ВызватьИсключение "Нельзя заменить существующий атрибут при подготовке своего шаблона";
КонецЕсли;
Метки[Индекс] = Создана;
ПроверитьГраф85(Документ, Исходный, Индекс + 1, 0, Метки, Истина);
КонецЦикла;
Ввод = ПолучитьСвойУзел85(Документ, "input", "ЭлементВводаHTML");
Связь = ПолучитьСвойУзел85(Документ, "link", "ЭлементСвязьHTML");
Мета = ПолучитьСвойУзел85(Документ, "meta", "ЭлементМетаHTML");
Вставка = ПолучитьСвойУзел85(Документ, "embed", "ЭлементВставкаHTML");
Линия = ПолучитьСвойУзел85(Документ, "line", "ЭлементЛинияHTML");
ДоВвода = Метки[0].ДочерниеУзлы.Количество();
ДоСвязи = Метки[1].ДочерниеУзлы.Количество();
ДоМета = Метки[2].ДочерниеУзлы.Количество();
ДоВставки = Метки[3].ДочерниеУзлы.Количество();
ДоЛинии = Метки[4].ДочерниеУзлы.Количество();
ПроверитьГраф85(Документ, Исходный, 5, 0, Метки, Истина);
Ввод.Нормализовать();
ПроверитьГраф85(Документ, Исходный, 5, 1, Метки, Истина);
ПослеВвода = Метки[0].ДочерниеУзлы.Количество();
ПроверитьГраф85(Документ, Исходный, 5, 1, Метки, Истина);
Связь.Нормализовать();
ПроверитьГраф85(Документ, Исходный, 5, 2, Метки, Истина);
ПослеСвязи = Метки[1].ДочерниеУзлы.Количество();
ПроверитьГраф85(Документ, Исходный, 5, 2, Метки, Истина);
Мета.Нормализовать();
ПроверитьГраф85(Документ, Исходный, 5, 3, Метки, Истина);
ПослеМета = Метки[2].ДочерниеУзлы.Количество();
ПроверитьГраф85(Документ, Исходный, 5, 3, Метки, Истина);
Вставка.Нормализовать();
ПроверитьГраф85(Документ, Исходный, 5, 4, Метки, Истина);
ПослеВставки = Метки[3].ДочерниеУзлы.Количество();
ПроверитьГраф85(Документ, Исходный, 5, 4, Метки, Истина);
Линия.Нормализовать();
ПроверитьГраф85(Документ, Исходный, 5, 5, Метки, Истина);
ПослеЛинии = Метки[4].ДочерниеУзлы.Количество();
ОжидаемыйПослеУдаления = ПодготовитьОжидаемоеДерево85(Исходный);
СлужебнаяЯчейка = ПроверитьСлужебнуюСтроку85(Документ, Истина);
СлужебнаяСтрока = ПолучитьСвойУзел85(Документ, "noteRow", "ЭлементСтрокаТаблицыHTML");
ЯчеекДо = СлужебнаяСтрока.Ячейки.Количество();
ПроверитьГраф85(Документ, Исходный, 5, 5, Метки, Истина);
СлужебнаяСтрока.УдалитьЯчейку();
ПроверитьГраф85(Документ, ОжидаемыйПослеУдаления, 5, 5, Метки, Ложь);
Если СлужебнаяЯчейка.ДокументВладелец <> Документ Или СлужебнаяЯчейка.РодительскийУзел <> Неопределено
Или СлужебнаяЯчейка.ТекстовоеСодержимое <> "Учебная служебная отметка A-01" Тогда
ВызватьИсключение "Только своя точная служебная ячейка должна отделиться без изменения содержимого";
КонецЕсли;
ЯчеекПосле = СлужебнаяСтрока.Ячейки.Количество();
Счет = ПрочитатьСвойСчет85(Документ);
Возврат Новый ФиксированнаяСтруктура("ДоВвода,ПослеВвода,ДоСвязи,ПослеСвязи,ДоМета,ПослеМета,ДоВставки,ПослеВставки,ДоЛинии,ПослеЛинии,ЯчеекДо,ЯчеекПосле,Количество,Цена,Сумма,Код",
ДоВвода, ПослеВвода, ДоСвязи, ПослеСвязи, ДоМета, ПослеМета, ДоВставки, ПослеВставки, ДоЛинии, ПослеЛинии,
ЯчеекДо, ЯчеекПосле, Счет.Количество, Счет.Цена, Счет.Сумма, Метки[0].Значение);
КонецФункцииСвоя команда, все16 скалярных полей и постоянный STOP
Общие → Общие формы → ПодготовкаHTMLШаблона85 → Модуль
Толстый управляемый клиент / &НаСервере
&НаКлиенте
Процедура ОчиститьШаблон85(Команда)
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если Остановка85 Или Завершено85 Или ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.ОчиститьШаблон85 Тогда
Остановка85 = Истина;
ВызватьИсключение "Допускается только своя команда и один запуск";
КонецЕсли;
Попытка
ОчиститьНаСервере85();
Если ТипЗнч(ДоВвода85) <> Тип("Число") Или ТипЗнч(ПослеВвода85) <> Тип("Число")
Или ТипЗнч(ДоСвязи85) <> Тип("Число") Или ТипЗнч(ПослеСвязи85) <> Тип("Число")
Или ТипЗнч(ДоМета85) <> Тип("Число") Или ТипЗнч(ПослеМета85) <> Тип("Число")
Или ТипЗнч(ДоВставки85) <> Тип("Число") Или ТипЗнч(ПослеВставки85) <> Тип("Число")
Или ТипЗнч(ДоЛинии85) <> Тип("Число") Или ТипЗнч(ПослеЛинии85) <> Тип("Число")
Или ТипЗнч(ЯчеекДо85) <> Тип("Число") Или ТипЗнч(ЯчеекПосле85) <> Тип("Число")
Или ТипЗнч(Количество85) <> Тип("Число") Или ТипЗнч(Цена85) <> Тип("Число") Или ТипЗнч(Сумма85) <> Тип("Число")
Или ТипЗнч(Код85) <> Тип("Строка") Тогда
ВызватьИсключение "Клиент должен получить только пятнадцать чисел и строку кода";
КонецЕсли;
Если Не Завершено85 Или ДоВвода85 <> 2 Или ПослеВвода85 <> 1 Или ДоСвязи85 <> 2 Или ПослеСвязи85 <> 1
Или ДоМета85 <> 2 Или ПослеМета85 <> 1 Или ДоВставки85 <> 2 Или ПослеВставки85 <> 1 Или ДоЛинии85 <> 2 Или ПослеЛинии85 <> 1
Или ЯчеекДо85 <> 1 Или ЯчеекПосле85 <> 0 Или Количество85 <> 3 Или Цена85 <> 250 Или Сумма85 <> 750 Или Код85 <> "A-01" Тогда
ВызватьИсключение "Не подтверждён весь результат своего шаблона";
КонецЕсли;
Исключение
Остановка85 = Истина;
Протокол85 = "Отказ, не успех. Возможно частичное изменение только своего дерева в памяти. Повтор, восстановление и продолжение запрещены.";
ВызватьИсключение;
КонецПопытки;
#Иначе
Остановка85 = Истина;
ВызватьИсключение "Нужен собственный толстый управляемый клиент";
#КонецЕсли
КонецПроцедуры
&НаСервере
Процедура ОчиститьНаСервере85()
Если Остановка85 Или Завершено85 Тогда
Остановка85 = Истина;
ВызватьИсключение "После ошибки или успеха повтор запрещён";
КонецЕсли;
Попытка
Пакет = HTMLШаблон85.ОчиститьСвойШаблон85();
ПроверитьПакетНаСервере85(Пакет);
Если Пакет.ДоВвода <> 2 Или Пакет.ПослеВвода <> 1 Или Пакет.ДоСвязи <> 2 Или Пакет.ПослеСвязи <> 1
Или Пакет.ДоМета <> 2 Или Пакет.ПослеМета <> 1 Или Пакет.ДоВставки <> 2 Или Пакет.ПослеВставки <> 1
Или Пакет.ДоЛинии <> 2 Или Пакет.ПослеЛинии <> 1 Или Пакет.ЯчеекДо <> 1 Или Пакет.ЯчеекПосле <> 0
Или Пакет.Количество <> 3 Или Пакет.Цена <> 250 Или Пакет.Сумма <> 750 Или Пакет.Код <> "A-01" Тогда
ВызватьИсключение "Нельзя принимать частичный результат";
КонецЕсли;
ДоВвода85 = Пакет.ДоВвода;
ПослеВвода85 = Пакет.ПослеВвода;
ДоСвязи85 = Пакет.ДоСвязи;
ПослеСвязи85 = Пакет.ПослеСвязи;
ДоМета85 = Пакет.ДоМета;
ПослеМета85 = Пакет.ПослеМета;
ДоВставки85 = Пакет.ДоВставки;
ПослеВставки85 = Пакет.ПослеВставки;
ДоЛинии85 = Пакет.ДоЛинии;
ПослеЛинии85 = Пакет.ПослеЛинии;
ЯчеекДо85 = Пакет.ЯчеекДо;
ЯчеекПосле85 = Пакет.ЯчеекПосле;
Количество85 = Пакет.Количество;
Цена85 = Пакет.Цена;
Сумма85 = Пакет.Сумма;
Код85 = Пакет.Код;
Протокол85 = "Код " + Код85 + ": пять атрибутов 2 → 1, служебная ячейка 1 → 0. Свой товар и остальное дерево сохранены; 3 x 250 = 750. Рендеринг и сеть не проверялись.";
Завершено85 = Истина;
Исключение
Остановка85 = Истина;
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
&НаСервере
Процедура ПроверитьПакетНаСервере85(Пакет)
Если ТипЗнч(Пакет) <> Тип("ФиксированнаяСтруктура") Тогда
ВызватьИсключение "До чтения полей нужен настоящий неизменяемый пакет";
КонецЕсли;
Если Пакет.Количество() <> 16 Тогда
ВызватьИсключение "Лишние и отсутствующие поля запрещены";
КонецЕсли;
Для Каждого ИмяПоля Из СтрРазделить("ДоВвода,ПослеВвода,ДоСвязи,ПослеСвязи,ДоМета,ПослеМета,ДоВставки,ПослеВставки,ДоЛинии,ПослеЛинии,ЯчеекДо,ЯчеекПосле,Количество,Цена,Сумма", ",") Цикл
Если Не Пакет.Свойство(ИмяПоля, ЗначениеПоля) Или ТипЗнч(ЗначениеПоля) <> Тип("Число") Тогда
ВызватьИсключение "Каждое из пятнадцати точных числовых полей должно существовать";
КонецЕсли;
КонецЦикла;
Если Не Пакет.Свойство("Код", ЗначениеПоля) Или ТипЗнч(ЗначениеПоля) <> Тип("Строка") Тогда
ВызватьИсключение "Последнее точное поле должно содержать строку своего кода";
КонецЕсли;
КонецПроцедурыНастоящее событие запуска открывает только свою форму
Конфигурация → Открыть модуль управляемого приложения
Толстый управляемый клиент
Процедура ПриНачалеРаботыСистемы()
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnHtmlTemplate6_42b9dd93-7d20-4c85-b94f-28e06b2b3d58\IB"";" Тогда
ВызватьИсключение "Откройте только свою точно подготовленную новую учебную File базу";
КонецЕсли;
ОткрытьФорму("ОбщаяФорма.ПодготовкаHTMLШаблона85", , , Истина);
#Иначе
ВызватьИсключение "Нужен толстый управляемый клиент 8.5.1.1343";
#КонецЕсли
КонецПроцедурыРазбор по шагам
Проверить точно свой стенд и получить настоящий ДокументHTML из собственной строки через ЧтениеHTML/ПостроительDOM. Все специальные классы и принадлежность одному документу проверяются, а reader закрывается на обеих ветках.
Из двух своих прочитанных ТекстHTML A-/01 получить реальные клоны. По одному общему производителю создать пять собственных атрибутов, фактически подтвердить их пустое состояние и добавить два текста по порядку. Значение A-01 получается из детей, а не задаётся присваиванием.
После каждой установки атрибута проверить весь документ. Выполнить пять отдельных Normalize на реальных input/link/meta/embed/hr; перед и после каждого перечитать весь граф. Векторы количества текстов: 22222 → 12222 → 11222 → 11122 → 11112 → 11111. Значения, владельцы и идентичность атрибутов сохраняются.
Отдельные глубокие копии без только пяти добавленных атрибутов должны совпадать с исходным целым логическим деревом. Настоящие атрибуты рабочего документа не удаляются; исходные span и товарные значения 3×250=750 сохраняются.
Перед реальным DeleteCell проверить singleton noteRow: одна настоящая собственная ячейка в Ячейки и в ДочерниеУзлы, точные тип/документ/идентичность/текст/родитель. Заранее построить отдельное ожидаемое дерево удалением exact noteCell только из копии. Рабочий DeleteCell вызывается без аргументов и без присваивания результата.
Подтвердить 1→0, отделение своей ячейки, её отсутствие в документе и полное равенство ожидаемому дереву. Сервер формы и клиент проверяют весь скалярный пакет; ошибка означает отказ и запрет повторения.
Как проверить результат
Будущий ручной протокол: пять пар До/После 2/1; ЯчеекДо=1, ЯчеекПосле=0; A-01, Количество=3, Цена=250, Сумма=750. Каждый оставшийся ребёнок атрибута — реальный ТекстHTML A-01, сами пять элементы без детей.
После удаления noteCell отсутствует в своём документе и отделена от родителя. Удаляется только служебная ячейка, не товарная. Всё остальное логическое дерево совпадает с отдельной заранее подготовленной ожидаемой копией.
Кредитуются ровно эти шесть primary API. IMG/COL Normalize2 не повторяются; нет Compare с неопределённым return, операций child lifecycle пустых элементов или успеха через пойманную ошибку.
Ожидаемый результат
Ожидаемый будущий результат: пять атрибутов 2→1, единственная служебная ячейка 1→0, прежние A-01 / 3 / 250 / 750 и остальной логический документ. Это не выполненный здесь запуск 1С и не визуальный результат.
На что обратить внимание
Тип конкретного HTML элемента и начальное состояние атрибута не выдумываются: они подтверждаются фактически; несовпадение останавливает урок. ЭлементВладелец не документирован как гарантированный Undefined.
Value setter сам удаляет детей и создаёт одного, поэтому здесь никогда не используется. Вызовы Normalize — процедуры без присваивания результата.
DeleteCell в установленной справке имеет нулевые аргументы и не объявляет return. Не добавляйте придуманный индекс и не переносите пример на многоклеточную строку.
Ячейки и ДочерниеУзлы — readonly реальные native коллекции; присваивание произвольного массива или подстановка рассчитанного count вместо настоящего состояния недопустимы.
УзелРавен проверяет логические имена, значения, атрибуты и порядок детей, но не побайтную сериализацию, служебные пользовательские данные или рендеринг. Документ и копии имеют отдельные идентичности; свои атрибуты проверяются по идентичности отдельно.
Ожидаемая копия подготовлена до DeleteCell другим штатным действием RemoveChild. Она не заменяет primary вызов в рабочем дереве. Живые атрибуты не удаляются/восстанавливаются, при ошибке не делается ремонт или повтор.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.