ЭлементВставкаHTML.Нормализовать

Методы · Normalize

Приводит все узлы Текст на полной глубине поддерева данного узла, включая узлы Атрибут, в "нормальную" форму, где только структуры (например, элементы, комментарии) разделяются текстовыми узлами, т.е. смежные текстовые узлы отсутствуют.

Синтаксис

Нормализовать()
Полное описание, параметры и ограничения

ЭлементВставкаHTML.Нормализовать (HTMLEmbedElement.Normalize)

ЭлементВставкаHTML (HTMLEmbedElement)

Нормализовать (Normalize)

Доступен, начиная с версии 8.2.

Синтаксис:
Нормализовать()
Описание:

Приводит все узлы Текст на полной глубине поддерева данного узла, включая узлы Атрибут, в "нормальную" форму, где только структуры (например, элементы, комментарии) разделяются текстовыми узлами, т.е. смежные текстовые узлы отсутствуют.

Доступность:

Тонкий клиент, мобильный клиент, сервер, толстый клиент, внешнее соединение, мобильное приложение (клиент), мобильное приложение (сервер), мобильный автономный сервер.

Использование в версии:

Доступен, начиная с версии 8.2.

Методическая информация

Практические примеры

Свой HTML-шаблон: пять атрибутов 2 → 1 и служебная ячейка 1 → 0

Подготовка собственного дерева товара без изменения кода A-01: пять настоящих атрибутов нормализуются, единственная служебная ячейка удаляется. Товарная строка и остальная структура полностью проверяются.

Где и как запускать

Серверный неглобальный модуль своей новой File CF 8.5.1.1343; толстая управляемая форма получает только проверенные числа и строку. Запуск 1С не проверялся.

Подготовка

  1. Вручную создайте НОВУЮ пустую файловую учебную ИБ только C:\1c-labs\OwnHtmlTemplate6_42b9dd93-7d20-4c85-b94f-28e06b2b3d58\IB. Имя конфигурации УчебныйHTMLШаблон85; ОсновнойРежимЗапуска=УправляемоеПриложение; РежимСовместимости=НеИспользовать. Windows 8.5.1.1343, толстый управляемый клиент. Точная строка соединения File="этот путь"; без дополнительных параметров. UUID в пути — маркер лаборатории, не доказательство идентичности базы.

  2. Установите отдельное свойство конфигурации Версия точно OwnHtmlTemplate6-7ac232f0-63a9-4fc8-a59e-b8c2dbfc6a41. Не добавляйте документы, справочники, регистры и расширения. Роль HTMLШаблон85Администратор с учебными правами Администрирование и АктивныеПользователи; один собственный пользователь HTMLШаблон85Ученик с этой ролью. Код читает настоящие UUID пользователя и соединения, не создаёт пользователей и не меняет права. После обновления учебной CF закройте Конфигуратор и остальные сеансы.

  3. Общие → Общие модули → HTMLШаблон85. Сервер=Да; Глобальный, ВызовСервера, Привилегированный, КлиентУправляемоеПриложение, КлиентОбычноеПриложение, ВнешнееСоединение=Нет. ПовторноеИспользованиеВозвращаемыхЗначений=НеИспользовать. Вставьте весь основной код; ОчиститьСвойШаблон85 экспортируется без аргументов. Все реальные HTML объекты создаются только здесь.

  4. Общие → Общие формы → ПодготовкаHTMLШаблона85. Добавьте Число15.2 для пятнадцати реквизитов: ДоВвода85, ПослеВвода85, ДоСвязи85, ПослеСвязи85, ДоМета85, ПослеМета85, ДоВставки85, ПослеВставки85, ДоЛинии85, ПослеЛинии85, ЯчеекДо85, ЯчеекПосле85, Количество85, Цена85, Сумма85. Код85 — Строка20, Протокол85 — неограниченная Строка, Остановка85/Завершено85 — Булево, исходно Ложь. Поля результата и многострочный протокол только для просмотра; флаги скрыты.

  5. Собственная команда ОчиститьШаблон85, Действие=ОчиститьШаблон85(Команда), кнопка командной панели связана с этой командой. Вставьте полный FORM. Сервер формы проверяет неизменяемый пакет, ровно16 полей, точные имена, наличие, все типы и значения ДО чтения прямых полей и переноса результата. Клиент повторно проверяет пятнадцать чисел и строку. Деревья, атрибуты и reader через RPC не передаются.

  6. Конфигурация → Открыть модуль управляемого приложения: вставьте APPLICATION. Настоящий ПриНачалеРаботыСистемы() откроет только эту учебную форму после проверки File/version/config. Startup и callback команды вручную не вызывайте; это реальные штатные события.

  7. Читается только фиксированная короткая строка основного кода. Два span A-/01, безопасные input/link/meta/embed/hr без адресов ресурсов, собственные товарная и служебная строки. Это серверное дерево в памяти: не поле HTML, не DOM браузера, не рендеринг, не отправка формы. ЧтениеHTML закрывается при успехе и ошибке; ПостроительDOM.Прочитать исполняется на сервере, не в тонком клиенте.

  8. Получение элемента по id описано общим типом ЭлементHTML, поэтому каждый специальный тип и документ проверяются фактически. Прочитанные ТекстHTML клонируются из своих span. СоздатьТекстовыйУзел, ТекстDOM и фиктивные конструкторы HTML подтипов не используются. Детей пяти пустым элементам не добавляем: два текста становятся детьми только пяти собственных data-study-code атрибутов.

  9. Начальные пустота и отсутствие владельца у нового атрибута — осторожное проверяемое условие, не гарантия. ЭлементВладелец в справке имеет тип ЭлементHTML. Неожиданный ответ или исключение означают отказ, не успех. Значение атрибута не присваивается: два текста дают A-01, пять конкретных Normalize должны фактически объединить их в один с тем же значением.

  10. Каждая граница перечитывает весь свой граф и удерживаемую идентичность атрибутов. Отдельный глубокий клон без ровно пяти добавленных атрибутов сравнивается с исходным целым логическим деревом. Удаление проверочных атрибутов затрагивает только копию; реальные атрибуты не удаляются и не восстанавливаются.

  11. Для DeleteCell допускается только noteRow с ровно одной своей noteCell: Ячейки — настоящая readonly КоллекцияЭлементовHTML, не произвольный Массив. Вызов ровно без аргументов и без присвоения результата, как в установленной справке. Выбор ячейки в многоклеточной строке справкой не объяснён и здесь не обещается. Перед вызовом на отдельной ожидаемой копии через RemoveChild убирается её точная служебная ячейка; рабочее дерево после DeleteCell должно полностью совпасть с этой копией.

  12. РодительскийУзел у ячейки в справке объявлен как ЭлементЯчейкаТаблицыHTML/Неопределено, хотя описание говорит о родителе. Код не обещает тип Row: проверяет фактическую идентичность своему noteRow, а после удаления отсутствие родителя. Служебная ячейка 1→0; товарные code/quantity/price и сумма750 сохраняются.

  13. Один запуск своей команды после обновления только новой учебной CF. При любой ошибке или неподходящем состоянии постоянный STOP и исключение: повтор, восстановление и продолжение запрещены. Ошибка может оставить частичные изменения только собственного дерева в памяти. Не сбрасывайте флаги в отладчике; новый ручной протокол начинайте после закрытия клиента.

  14. 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";
#КонецЕсли
КонецПроцедуры

Разбор по шагам

  1. Проверить точно свой стенд и получить настоящий ДокументHTML из собственной строки через ЧтениеHTML/ПостроительDOM. Все специальные классы и принадлежность одному документу проверяются, а reader закрывается на обеих ветках.

  2. Из двух своих прочитанных ТекстHTML A-/01 получить реальные клоны. По одному общему производителю создать пять собственных атрибутов, фактически подтвердить их пустое состояние и добавить два текста по порядку. Значение A-01 получается из детей, а не задаётся присваиванием.

  3. После каждой установки атрибута проверить весь документ. Выполнить пять отдельных Normalize на реальных input/link/meta/embed/hr; перед и после каждого перечитать весь граф. Векторы количества текстов: 22222 → 12222 → 11222 → 11122 → 11112 → 11111. Значения, владельцы и идентичность атрибутов сохраняются.

  4. Отдельные глубокие копии без только пяти добавленных атрибутов должны совпадать с исходным целым логическим деревом. Настоящие атрибуты рабочего документа не удаляются; исходные span и товарные значения 3×250=750 сохраняются.

  5. Перед реальным DeleteCell проверить singleton noteRow: одна настоящая собственная ячейка в Ячейки и в ДочерниеУзлы, точные тип/документ/идентичность/текст/родитель. Заранее построить отдельное ожидаемое дерево удалением exact noteCell только из копии. Рабочий DeleteCell вызывается без аргументов и без присваивания результата.

  6. Подтвердить 1→0, отделение своей ячейки, её отсутствие в документе и полное равенство ожидаемому дереву. Сервер формы и клиент проверяют весь скалярный пакет; ошибка означает отказ и запрет повторения.

Как проверить результат

  1. Будущий ручной протокол: пять пар До/После 2/1; ЯчеекДо=1, ЯчеекПосле=0; A-01, Количество=3, Цена=250, Сумма=750. Каждый оставшийся ребёнок атрибута — реальный ТекстHTML A-01, сами пять элементы без детей.

  2. После удаления noteCell отсутствует в своём документе и отделена от родителя. Удаляется только служебная ячейка, не товарная. Всё остальное логическое дерево совпадает с отдельной заранее подготовленной ожидаемой копией.

  3. Кредитуются ровно эти шесть primary API. IMG/COL Normalize2 не повторяются; нет Compare с неопределённым return, операций child lifecycle пустых элементов или успеха через пойманную ошибку.

Ожидаемый результат

Ожидаемый будущий результат: пять атрибутов 2→1, единственная служебная ячейка 1→0, прежние A-01 / 3 / 250 / 750 и остальной логический документ. Это не выполненный здесь запуск 1С и не визуальный результат.

На что обратить внимание

  1. Тип конкретного HTML элемента и начальное состояние атрибута не выдумываются: они подтверждаются фактически; несовпадение останавливает урок. ЭлементВладелец не документирован как гарантированный Undefined.

  2. Value setter сам удаляет детей и создаёт одного, поэтому здесь никогда не используется. Вызовы Normalize — процедуры без присваивания результата.

  3. DeleteCell в установленной справке имеет нулевые аргументы и не объявляет return. Не добавляйте придуманный индекс и не переносите пример на многоклеточную строку.

  4. Ячейки и ДочерниеУзлы — readonly реальные native коллекции; присваивание произвольного массива или подстановка рассчитанного count вместо настоящего состояния недопустимы.

  5. УзелРавен проверяет логические имена, значения, атрибуты и порядок детей, но не побайтную сериализацию, служебные пользовательские данные или рендеринг. Документ и копии имеют отдельные идентичности; свои атрибуты проверяются по идентичности отдельно.

  6. Ожидаемая копия подготовлена до DeleteCell другим штатным действием RemoveChild. Она не заменяет primary вызов в рабочем дереве. Живые атрибуты не удаляются/восстанавливаются, при ошибке не делается ремонт или повтор.

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

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.