THE BELL

Есть те, кто прочитали эту новость раньше вас.
Подпишитесь, чтобы получать статьи свежими.
Email
Имя
Фамилия
Как вы хотите читать The Bell
Без спама

Сегодня решил немного продолжить тему работы с XML-RPC в WordPress. Как это обычно со мной бывает, идея родилась в момент чтения случайного блога, вздумалось взглянуть на работу с постами в блоге и, заодно, попробовать написать что-нибудь под свои нужды.

Естественно программу я сегодня не выложу, но некоторые выкладки, листинги и идеи в посте будут присутствовать.

Вкратце работу с XML-RPC я рассматривал в посте “ ” Сегодня попробуем продвинуться дальше в своей работе и использовать несколько взаимосвязанных методов для получения определенной информации из блога.

Конкретизируем цель на сегодня : необходимо получить данные по постам в блоге, используя доступные методы из API WordPress .

Для достижения поставленной цели нам понадобятся следующие модули Delphi: XMLIntf , xmldom , XMLDoc и библиотека synapce или компонент Indy idHTTP (кому как угодно).

1. Тестируем соединение с блогом.

Полагаю, что первое, что следует сделать - это проверить корректность работы с блогом на предмет следующих возможных ошибок:

  1. В блоге отключена возможность использования XML-RPC
  2. Пользователь предоставил некорректные данные (url, логин или пароль).

Для проверки возможности работы с XML-RPC в блоге достаточно воспользоваться методом demo.sayHello . Если ответом будет строка “Hello” , значит всё в порядке и можно приступать к следующему шагу проверки. Для выполнения этой проверки нам потребуется выполнить три простенькие задачки:

  • сформировать правильный XML-документ
  • отправить запрос на сервер и получить ответ
  • проанализировать ответ

Формируем XML-документ, который должен выглядеть так:

demo.sayHello test

Для этого воспользуемся интерфейсом IXMLDocument:

[ ... ] var doc: IXMLDocument; //документ Root: IXMLNode; //корневой узел begin inherited Create; doc: = NewXMLDocument() ; //создаем пустой документ Root: = Doc. CreateElement ("methodCall" , "" ) ; //добавляем корневой узел Doc. DocumentElement : = Root; Root. AddChild ("methodName" ) . NodeValue : = "demo.sayHello" ; //добавляем название метода Root. AddChild ("params" ) . AddChild ("param" ) . AddChild ("value" ) . AddChild ("string" ) . NodeValue : = "test" ; //записываем параметры метода [ ... ]

Так как сам по себе XML-документ достаточно простой, то я позволил себе немного “похалявить” и последней строкой кода записал сразу все узлы и значение единственного параметра для нашего метода.
Теперь можно отправить документ на сервер и получить ответ:

[ ... ] with THTTPSend. Create do begin Doc. SaveToStream (Document) ; //записываем документ в тело запроса if HTTPMethod("POST" , aURL) then begin //запрос успешно отправлен и получен ответ end else begin //запрос не удался end ; end ; [ ... ]

Что мне нравится в Synapce , так это то, что не требуется лишних “телодвижений” в плане заполнения заголовков Content-Length , Content-Type и пр. Конечно никто не мешает заполнить все возможные заголовки самому, но можно обойтись и так, как показал я выше - всё на автомате.
Двигаемся дальше - проводим анализ ответа.
Позволю себе напомнить Вам, что удачная отправка запроса на сервер никак не свидетельствует о том, что мы успешно получили доступ к XML-RPC блога. Удачная отправка запроса свидетельствует только о том, что мы отправили запрос и получили ответ, а _что_ находится в ответе ошибка или нет - мы пока не знаем .
Чтобы пока не забивать голову лишними способами и методами парсинга ответа от сервера, предлагаю в данном случае остановиться на применении простой проверки:

[ ... ] Doc. LoadFromStream (Document, xetUTF_8) ; //записали XML-документ if Doc. DocumentElement . ChildNodes . FindNode ("fault" ) = nil then ShowMessage("XML-RPC работает исправно" ) [ ... ]

В соответствии со спецификацией XML-RPC сообщения об ошибках содержится в узле с названием fault. Следовательно, применительно к нашему случаю достаточно проверить наличие такого узла в ответном XML-документе - если его нет, то значит проверка прошла успешно, был сформирован корректный запрос и XML-RPC работает исправно.
Переходим к следующему шагу - проверке на корректность предоставленных данных пользователем и возможности работы пользователя с XML-RPC блога .
С XML-RPC блога имеет право работать только администратор, следовательно, необходимо узнать кто пробует получить доступ. Для этого воспользуемся методом wp.getUsersBlogs . Параметрами метода являются логин и пароль.
Но прежде, чем приступим к отправке запроса и получению ответа, думаю, стоит немного задуматься о будущем и предусмотреть работу с ошибками, формирование документов и т.д.
В предыдущей проверке, можно сказать, было баловство - простейших вариант работы типа:
отправил/получил/тут_же_разобрал/забыл/пошел_дальше.
Так как я планирую развивать модуль по работе с API WordPress и дальше, то есть смысл определиться со следующими моментами в работе:

  1. Сформировать “скелет” документа
  2. Записать в документ все необходимые параметры, учитывая типы данных
  3. Отправить запрос и получить ответ от сервера
  4. Проанализировать ответ и, если в ответе содержится сообщение об ошибке, то правильно его прочитать

Все эти четыре шага я сделал отдельными методами класса. Под “скелетом” документа я понимаю следующее содержимое:

MethodName

То есть часть документа, содержащая имя метода и узел params без содержимого. Дальше на останется только правильно заполнить список параметров. Чем мы сейчас и займемся.

Всего в XML-RPC предусмотрено использование шести простых типов данных:

  1. int и i4 - целые числаinteger)
  2. double - дробные числа
  3. string - строки
  4. base64 - закодированная строка
  5. dateTime.iso8601 - дата/время
  6. boolean

Заводим новый тип данных:

TSimpleType = (tsInt, tsI4, tsString, tsDouble, tsDateTime, tsBase64, tsBoolean) ;

С помощью значений этого типа будем определять тэг для значения параметра.

Так как операции создания “скелета” документа и добавления параметров метода разнесены по разным функциям, то создадим ещё один вспомогательный тип данных:

PXMLDocument = ^ IXMLDocument;

Теперь сам метод добавления параметра в документ:

procedure TBlog. SetParam (SimpleType: TSimpleType; Value: string ; Document: PXMLDocument) ; var Root: IXMLNode; begin if Document^ . IsEmptyDoc then Exit; //документ пуст Root: = Document^ . DocumentElement . ChildNodes . FindNode ("params" ) ; if Root= nil then Exit; //узел не найден case SimpleType of tsInt: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("int" ) . NodeValue : = Value; tsI4: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("i4" ) . NodeValue : = Value; tsString: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("string" ) . NodeValue : = Value; tsDouble: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("double" ) . NodeValue : = Value; tsDateTime: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("dateTime.iso8601" ) . NodeValue : = Value; tsBase64: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("base64" ) . NodeValue : = Value; tsBoolean: Root. AddChild ("param" ) . AddChild ("value" ) . AddChild ("boolean" ) . NodeValue : = Value; end ; end ;

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

Теперь про анализ сообщений об ошибке. Рассмотрим пример того, как выглядит сообщение об ошибке в XML-RPC:

faultCode 403 faultString Bad login/pass combination.

Сообщение об ошибке приходит нам в структуре. Причём, если считать, что теги member нумеруются от нуля, то каждый чётный элемент структуры - это код ошибки, а нечётный - текст ошибки. Следовательно метод обработки сообщений об ошибке может выглядеть так:

function TBlog. ParseErrors (aDocument: PXMLDocument) : TStringList; var i: integer ; List: IDOMNodeList; code: string ; begin List: = aDocument^ . DOMDocument . getElementsByTagName ("member" ) ; Result: = TStringList. Create ; for i: = 0 to List. length - 1 do begin case i mod 2 of 0 : code: = (List. item [ i] . lastChild . firstChild as IDOMNodeEx) . text ; //чётный элемент - читаем код ошибки 1 : //нечётный элемент - читаем текст ошибки и записываем результат Result. Add (code+ " " + (List. item [ i] . lastChild . firstChild as IDOMNodeEx) . text ) ; end ; end ; end ;

Здесь код и текст ошибки записывается в TStringList. Думаю, что при необходимости можно легко сделать, чтобы код и текст читались в разные списки или массивы. Нам пока это не требуется.

Отправку документа мы уже рассматривали, поэтому сразу привожу метод проверки данных на корректность:

function TBlog. CheckUserAccess (const aURL, aUser, aPassword: string ; var Error: string ) : boolean ; var Doc: IXMLDocument; begin Doc: = GetDocument("wp.getUsersBlogs" ) ; //создали "скелет" //добавляем параметры SetParam(tsString, aUser, @ Doc) ; SetParam(tsString, aPassword, @ Doc) ; SendQuery(@ Doc, aURL) ; //отправляем запрос if not Doc. IsEmptyDoc then //если документ записан корректно begin if Doc. DocumentElement . ChildNodes . FindNode ("fault" ) < > nil then //есть сообщение об ошибке begin Result: = false ; Error: = ParseErrors(@ Doc) . Strings [ 0 ] ; end else Result: = true ; end else Result: = false ; end ;

Если пришло сообщение об ошибке, то записываем сообщение в переменную Error. В данном случае структура содержит только одно сообщение об ошибке - поэтому я так легко прописал:

Error: = ParseErrors(@ Doc) . Strings [ 0 ] ;

Итак, две проверки сделаны и мы определили, что XML-RPC включен и работает исправно, а пользователь ввёл корректные данные логина и пароля и может работать с API WordPress. Что дальше? А дальше начинаем основную работу - получаем данные по комментариям в блоге.

2. Получаем данные о постах блога.

Итак, что предоставляет в наше распоряжение WordPress. Сначала сделаем кратки обзор методов из xmlrpc.php.

wp.getPostStatusList - выводит значения для статуса поста. По сути на выходе будем имеет четыре строки: ‘draft’, ‘pending’, ‘private’, ‘publish’.

Пока этот метод нам бесполезен.

blogger.getRecentPosts - эта функция уже из API Blogger, но поддерживается в WordPress. На выходе будем иметь последние посты блога, включая весь контент поста.

Можно использовать метод, НО работа программы будет замедлена так как придётся “тягать” по Сети пост целиком. А если попробуем получить список постов блога целиком, то, видимо придётся ложиться спать, не дождавшись результата. Следовательно - пока оставляем метод в стороне.

metaWeblog.getRecentPosts - аналогично предыдущему методу.

mt.getRecentPostTitles - метод из MovableType API. Судя по названию - то, что нам надо. Смотрим описание метода.

Метод возвращает список, содержащий заголовки постов блога. При этом контент в список не записывается.

Входные параметры:

  • String blogid
  • String username
  • String password
  • int numberOfPosts

blogid всегда равен 1 (см. описание в xmlrpc.php)

numberOfPosts - количество постов, которые необходимо вывести в список. Если параметр имеет значение больше, чем количество постов в блоге, то метод возвращает список всех постов.

Осталось узнать, что из себя представляет этот список. А на выходе мы будем иметь массив структур, включающий в себя:

  • дату создания элемента
  • userid
  • postid
  • заголовок.

Замечательно. Воспользуемся этим методом, а заодно научимся анализировать сложные структуры ответа.

Про создание запроса, думаю, писать не стоит. Процедура аналогична той, что рассмотрена выше. А на анализе ответа сервера остановимся подробно. Стем как выглядит тип struct (структура) мы познакомились при парсинге ответа, содержащего ошибку авторизации. Посмотрим, что из себя представляет массив.

Массивы не имеют названия и описываются тегом . Он содержит один элемент и один или несколько дочерних элементов , где задаются данные. В качестве элементов массива могут выступать любые другие типы в произвольном порядке, а также другие массивы - что позволяет описывать многомерные массивы. Так же можно описывать массив структур. Например, массив из четырех элементо будет выглядеть так:

34 Привет, Мир! 0 -34

У нас на выходе из метода mt.getRecentPostTitles
будет содержаться массив структур , причём одна структура - это информация по одному посту блога. Следовательно, чтение данных по постам блога можно условно разделить на следующие шаги:

  1. Выделяем из XML-документа все элементы . firstChild . childNodes ; //получили все members для 1 value for j: = 0 to Members. length - 1 do begin with Result[ i] do case j of 0 : dateCreated: = (Members[ j] . lastChild . firstChild as IDOMNodeEx) . text ; 1 : user_id: = StrToInt ((Members[ j] . lastChild . firstChild as IDOMNodeEx) . text ) ; 2 : id: = StrToInt ((Members[ j] . lastChild . firstChild as IDOMNodeEx) . text ) ; 3 : title: = (Members[ j] . lastChild . firstChild as IDOMNodeEx) . text ; end ; end ; end ; [ ... ]

    Соответственно, если получено сообщение об ошибке, то можно воспользоваться рассмотренной ранее функцией.

    На сегодня всё. В следующий раз продолжим работу с API и попробуем получить все комментарии из блога.

Язык XML все чаще используется для хранения информации, обмена ею между приложениями и Web-узлами. Во многих приложениях этот язык применяется в качестве базового для хранения данных, в других - для экспортирования и импортирования XML-данных. Из этого следует, что разработчикам пора задуматься над тем, как можно использовать XML-данные в собственных приложениях.

В этой статье мы рассмотрим XML Document Object Model (DOM) и ее реализацию фирмой Microsoft - Microsoft XML DOM.

XML DOM - это объектная модель, предоставляющая в распоряжение разработчика объекты для загрузки и обработки XML-файлов. Объектная модель состоит из следующих основных объектов: XMLDOMDocument, XMLDOMNodeList, XMLDOMNode, XMLDOMNamedNodeMap и XMLDOMParseError. Каждый из этих объектов (кроме XMLDOMParseError) содержит свойства и методы, позволяющие получать информацию об объекте, манипулировать значениями и структурой объекта, а также перемещаться по структуре XML-документа.

Рассмотрим основные объекты XML DOM и приведем несколько примеров их использования в Borland Delphi.

Использование XML DOM в Borland Delphi

Для того чтобы использовать Microsoft XML DOM в Delphi-приложениях, необходимо подключить к проекту соответствующую библиотеку типов. Для этого мы выполняем команду Project | Import Type Library и в диалоговой панели Import Type Library выбираем библиотеку Microsoft XML version 2.0 (Version 2.0), которая обычно находится в файле Windows\System\MSXML.DLL

После нажатия кнопки Create Unit будет создан интерфейсный модуль MSXML_TLB, который позволит нам воспользоваться объектами XML DOM: DOMDocument, XMLDocument, XMLHTTPRequest и рядом других, реализованных в библиотеке MSXML.DLL. Ссылка на модуль MSXML_TLB должна быть указана в списке Uses.

Устройство XML DOM

Document Object Model представляет XML-документ в виде древовидной структуры, состоящей из ветвей. Программные интерфейсы XML DOM позволяют приложениям перемещаться по дереву документа и манипулировать его ветвями. Каждая ветвь может иметь специфический тип (DOMNodeType), согласно которому определяются родительская и дочерние ветви. В большинстве XML-документов можно встретить ветви типа element, attribute и text. Атрибуты (attribute) представляют собой особый вид ветви и не являются дочерними ветвями. Для управления атрибутами используются специальные методы, предоставляемые объектами XML DOM.

Помимо реализации рекомендованных World Wide Web Consortium (W3C) интерфейсов, Microsoft XML DOM содержит методы, поддерживающие XSL, XSL Patterns, Namespaces и типы данных. Например, метод SelectNodes позволяет использовать синтаксис шаблонов XSL (XSL Pattern Syntax) для поиска ветвей по определенному контексту, а метод TransformNode поддерживает использование XSL для выполнения трансформаций.

Тестовый XML-документ

В качестве примера XML-документа возьмем каталог музыкальных CD-ROM, который имеет следующую структуру :

Empire Burlesque Bob Dylan USA Columbia 10.90 1985 Hide your heart Bonnie Tylor UK CBS Records 9.90 1988 ... Unchain my heart Joe Cocker USA EMI 8.20 1987

Теперь мы готовы приступить к рассмотрению объектной модели XML DOM, знакомство с которой начнем с объекта XMLDOMDocument.

XML-документ - объект XMLDOMDocument

Работа с XML-документом начинается с его загрузки. Для этого мы используем метод Load, который имеет всего один параметр, указывающий URL загружаемого документа. При загрузке файлов с локального диска указывается только полное имя файла (протокол file:/// в данном случае можно опустить). Если XML-документ хранится в виде строки, для загрузки такого документа следует использовать метод LoadXML.

Для управления способом загрузки документа (синхронный или асинхронный) используется свойство Async. По умолчанию это свойство имеет значение True, указывающее на то, что документ загружается асинхронно и управление возвращается приложению еще до полной загрузки документа. В противном случае документ загружается синхронно, и тогда приходится проверять значение свойства ReadyState, чтобы узнать, загрузился документ или нет. Также можно создать обработчик события OnReadyStateChange, который получит управление при изменении значения свойства ReadyState.

Ниже показано, как загрузить XML-документ, используя метод Load:

Uses ... MSXML_TLB ... procedure TForm1.Button1Click(Sender: TObject); var XMLDoc: IXMLDOMDocument; begin XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.Load(‘C:\DATA\DATA.xml’); // // Здесь располагается код, манипулирующий // XML-документом и его ветвями // XMLDoc:= Nil; end;

После того как документ загружен, мы можем обратиться к его свойствам. Так, свойство NodeName будет содержать значение #document, свойство NodeTypeString - значение document, свойство URL - значение file:///C:/DATA/DATA.xml.

Обработка ошибoк

Особый интерес представляют свойства, связанные с обработкой документа при его загрузке. Так, свойство ParseError возвращает объект XMLDOMParseError, содержащий информацию об ошибке, возникшей в процессе обработки документа.

Чтобы написать обработчик ошибки, можно добавить следующий код:

Var XMLError: IXMLDOMParseError; ... XMLDoc.Load(‘C:\DATA\DATA.xml’); XMLError:= XMLDoc.ParseError; If XMLError.ErrorCode <> 0 Then // // Здесь мы обрабатываем ошибку // Else Memo1.Lines.Add(XMLDoc.XML); ... XMLDoc:= Nil;

Чтобы узнать, какая информация возвращается в случае ошибки, изменим следующий элемент каталога:

Empire Burlesque Bob Dylan USA Columbia 10.90 1985

убрав закрывающий элемент во второй строке:</p><p> <CD> <TITLE>Empire Burlesque <ARTIST>Bob Dylan</ARTIST> <COUNTRY>USA</COUNTRY> <COMPANY>Columbia</COMPANY> <PRICE>10.90</PRICE> <YEAR>1985</YEAR> </CD> </p><p>Теперь напишем код, возвращающий значения свойств объекта XMLDOMParseError:</p><p>XMLError:= XMLDoc.ParseError; If XMLError.ErrorCode <> 0 Then With XMLError, Memo1.Lines do begin Add(‘Файл: ‘ + URL); Add(‘Код: ‘ + IntToStr(ErrorCode)); Add(‘Ошибка: ‘ + Reason); Add(‘Текст: ‘ + SrcText); Add(‘Строка: ‘ + IntToStr(Line)); Add(‘Позиция: ‘ + IntToStr(LinePos)); end Else Memo1.Lines.Add(XMLDoc.XML); End; </p><p>и выполним наше приложение. В результате получаем следующую информацию об ошибке .</p> <p>Как видно из приведенного примера, возвращаемой объектом XMLDOMParseError информации вполне достаточно для того, чтобы локализовать ошибку и понять причину ее возникновения.</p> <p>Теперь восстановим закрывающий элемент <TITLE> в нашем документе и продолжим обсуждение XML DOM.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Доступ к дереву документа</h2> <p>Для доступа к дереву документа можно либо получить корневой элемент и затем перебрать его дочерние ветви, либо найти какую-то специфическую ветвь. В первом случае мы получаем корневой элемент через свойство DocumentElement, которое возвращает объект типа XMLDOMNode. Ниже показано, как воспользоваться свойством DocumentElement для того, чтобы получить содержимое каждого дочернего элемента:</p><p>Var Node: IXMLDOMNode; Root: IXMLDOMElement; I: Integer; ... Root:= XMLDoc.DocumentElement; For I:= 0 to Root.ChildNodes.Length-1 do Begin Node:= Root.ChildNodes.Item[I]; Memo1.Lines.Add(Node.Text); End; </p><p>Для нашего XML-документа мы получим следующий текст .</p> <p>Если нас интересует какая-то специфическая ветвь или ветвь уровнем ниже первой дочерней ветви, мы можем воспользоваться либо методом NodeFromID, либо методом GetElementByTagName объекта XMLDOMDocument.</p> <p>Метод NodeFromID требует указания уникального идентификатора, определенного в XML Schema или Document Type Definition (DTD), и возвращает ветвь с этим идентификатором.</p> <p>Метод GetElementByTagName требует указания строки со специфическим элементом (тэгом) и возвращает все ветви с данным элементом. Ниже показано, как использовать данный метод для нахождения всех исполнителей в нашем каталоге CD-ROM:</p><p>Nodes: IXMLDOMNodeList; Node: IXMLDOMNode; ... Nodes:= XMLDoc.GetElementsByTagName(‘ARTIST’); For I:= 0 to Nodes.Length-1 do Begin Node:= Nodes.Item[I]; Memo1.Lines.Add(Node.Text); End; </p><p>Для нашего XML-документа мы получим следующий текст </p> <p>Отметим, что метод SelectNodes объекта XMLDOMNode обеспечивает более гибкий способ для доступа к ветвям документа. Но об этом чуть ниже.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Ветвь документа - объект XMLDOMNode</h2> <p>Объект XMLDOMNode представляет собой ветвь документа. Мы уже сталкивались с этим объектом, когда получали корневой элемент документа:</p><p>Root:= XMLDoc.DocumentElement; </p><p>Для получения информации о ветви XML-документа можно использовать свойства объекта XMLDOMNode (табл. 1).</p> <p>Для доступа к данным, хранимым в ветви, обычно используют либо свойство NodeValue (доступно для атрибутов, текстовых ветвей, комментариев, инструкций по обработке и секций CDATA), либо свойство Text, возвращающее текстовое содержимое ветви, либо свойство NodeTypedValue. Последнее, однако, может использоваться только для ветвей с типизованными элементами.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h3> Перемещение по дереву документа</h3> <p>Объект XMLDOMNode предоставляет множество способов для перемещения по дереву документа. Например, для доступа к родительской ветви используется свойство ParentNode (тип XMLDOMNode), доступ к дочерним ветвям осуществляется через свойства ChildNodes (тип XMLDOMNodeList), FirstChild и LastChild (тип XMLDOMNode) и т.д. Свойство OwnerDocument возвращает объект типа XMLDOMDocument, идентифицирующий сам XML-документ. Перечисленные выше свойства позволяют легко перемещаться по дереву документа.</p> <p>Теперь переберем все ветви XML-документа:</p><p>Root:= XMLDoc.DocumentElement; For I:= 0 to Root.ChildNodes.Length-1 do Begin Node:= Root.ChildNodes.Item[I]; If Node.HasChildNodes Then GetChilds(Node,0); End; </p><p>Как уже отмечалось выше, SelectNodes объекта XMLDOMNode обеспечивает более гибкий способ доступа к ветвям документа. Кроме того, существует метод SelectSingleNode, возвращающий только первую ветвь документа. Оба эти метода позволяют задавать XSL-шаблоны для поиска ветвей.</p> <p>Рассмотрим процесс использования метода SelectNodes для извлечения всех ветвей, у которых есть ветвь CD и подветвь PRICE:</p><p>Root:= XMLDoc.DocumentElement; Nodes:= Root.SelectNodes(‘CD/PRICE’); </p><p>В коллекцию Nodes будут помещены все подветви PRICE ветви CD. К обсуждению XSL-шаблонов вернемся чуть позже.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h3> Манипуляция дочерними ветвями</h3> <p>Для манипуляции дочерними ветвями мы можем воспользоваться методами объекта XMLDOMNode (табл. 2).</p> <p>Для того чтобы полностью удалить запись о первом диске, необходимо выполнить следующий код :</p><p>Var XMLDoc: IXMLDOMDocument; Root: IXMLDOMNode; Node: IXMLDOMNode; XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.Load(‘C:\DATA\DATA.xml’); // Получить корневой элемент Root:= XMLDoc.DocumentElement; Node:= Root; // Удалить первую дочернюю ветвь Node.RemoveChild(Node.FirstChild); </p><p>Обратите внимание на то, что в данном примере мы удаляем первую дочернюю ветвь. Как удалить первый элемент первой дочерней ветви, показано ниже :</p><p>Var XMLDoc: IXMLDOMDocument; Root: IXMLDOMNode; Node: IXMLDOMNode; XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.Load(‘C:\DATA\DATA.xml’); // Получить корневой элемент Root:= XMLDoc.DocumentElement; // и первую дочернюю ветвь Node:= Root.FirstChild; // Удалить первую дочернюю ветвь Node.RemoveChild(Node.FirstChild); </p><p>В приведенном выше примере мы удалили не первую ветвь <CD>…</CD>, а первый элемент ветви - <TITLE>….

Теперь добавим новую ветвь. Ниже приведен код, показывающий, как добавить новую запись о музыкальном CD-ROM :

Var NewNode: IXMLDOMNode; Child: IXMLDOMNode; ... // Создадим новую ветвь - NewNode:= XMLDoc.CreateNode(1, ‘CD’, ‘’); // Добавим элемент Child:= XMLDoc.CreateNode(1,‘TITLE’,‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘Pink Floyd’; // Добавим элемент <ARTIST> Child:= XMLDoc.CreateNode(1, ‘ARTIST’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘Division Bell’; // Добавим элемент <COUNTRY> Child:= XMLDoc.CreateNode(1, ‘COUNTRY’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘UK’; // Добавим элемент <COMPANY> Child:= XMLDoc.CreateNode(1, ‘COMPANY’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘EMI Records Ltd.’; // Добавим элемент <PRICE>Child:= XMLDoc.CreateNode(1, ‘PRICE’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ’11.99"; // Добавим элемент <YEAR> Child:= XMLDoc.CreateNode(1, ‘YEAR’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘1994’; // И добавим ветвь Root.AppendChild(NewNode); ... </p><p>Приведенный выше код показывает следующую последовательность действий по добавлению новой ветви:</p> <ul><li>Создание новой ветви методом CreateNode: <ul><li>создание элемента методом CreateNode;</li> <li>добавление элемента к ветви методом AppendChild;</li> <li>установка значения элемента через свойство Text;</li> <li>… повторить для всех элементов.</li> </ul></li> <li>Добавление новой ветви к документу методом AppendChild.</li> </ul><p>Напомним, что метод AppendChild добавляет ветвь в конец дерева. Для того чтобы добавить ветвь в конкретное место дерева, необходимо использовать метод InsertBefore.</p> <h2> Набор ветвей - объект XMLDOMNodeList</h2> <p>Объект XMLNodeList содержит список ветвей, который может быть построен с помощью методов SelectNodes или GetElementsByTagName, а также получен из свойства ChildNodes.</p> <p>Мы уже рассматривали использование этого объекта в примере, приведенном в разделе «Перемещение по дереву документа». Здесь же мы приведем некоторые теоретические замечания.</p> <p>Число ветвей в списке может быть получено как значение свойства Length. Ветви имеют индексы от 0 до Length-1, и каждая отдельная ветвь доступна через элемент массива Item с соответствующим индексом.</p> <p>Перемещение по списку ветвей также может осуществляться с помощью метода NextNode, возвращающего следующую ветвь в списке, или Nil, если текущая ветвь - последняя. Чтобы вернуться к началу списка, следует вызвать метод Reset.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Создание и сохранение документов</h2> <p>Итак, мы рассмотрели, как можно добавлять ветви и элементы в существующие XML-документы. Теперь создадим XML-документ «на лету». Прежде всего напомним, что документ может быть загружен не только из URL, но и из обычной строки. Ниже показано, как создать корневой элемент, который затем может использоваться для динамического построения остальных элементов (что мы уже рассмотрели в разделе «Манипуляция дочерними ветвями»):</p><p>Var XMLDoc: IXMLDOMDocument; Root: IXMLDOMNode; Node: IXMLDOMNode; S: WideString; ... S:= ‘<CATALOG></CATALOG>’; XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.LoadXML(S); Root:= XMLDoc.DocumentElement; Node:= XMLDoc.CreateNode(1, ‘CD’, ‘’); Root.AppendChild(Node); Memo1.Lines.Add(XMLDoc.XML); ... XMLDoc:= Nil; </p><p>После построения XML-документа сохраним его в файле с помощью метода Save. Например:</p> <p>XMLDoc.Save(‘C:\DATA\NEWCD.XML’);</p> <p>Помимо сохранения в файле метод Save позволяет сохранять XML-документ в новом объекте XMLDOMDocument. В этом случае происходит полная обработка документа и, как следствие, проверка его структуры и синтаксиса. Ниже показано, как сохранить документ в другом объекте:</p><p>Procedure TForm1.Button2Click(Sender: TObject); var XMLDoc2: IXMLDOMDocument; begin XMLDoc2:= CoDOMDocument.Create; XMLDoc.Save(XMLDoc2); Memo2.Lines.Add(XMLDoc2.XML); ... XMLDoc2:= Nil; end; </p><p>В заключение отметим, что метод Save также позволяет сохранять XML-документ в другие COM-объекты, поддерживающие интерфейсы IStream, IPersistStream или IPersistStreamInit.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Использование XSL-шаблонов</h2> <p>Обсуждая метод SelectNodes объекта XMLDOMNode, мы упомянули о том, что он обеспечивает более гибкий способ доступа к ветвям документа. Гибкость заключается в том, что в качестве критерия для выбора ветвей можно указать XSL-шаблон. Такие шаблоны предоставляют мощный механизм для поиска информации в XML-документах. Например, для того, чтобы получить список всех названий музыкальных CD-ROM в нашем каталоге, можно выполнить следующий запрос:</p><p>Чтобы узнать, диски каких исполнителей выпущены в США, запрос формируется следующим образом:</p><p>Nodes:= Root.SelectNodes(‘CD/ARTIST’); </p><p>Ниже показано, как найти первый диск в каталоге:</p><p>Nodes:= Root.SelectNodes(‘CD/TITLE’); </p><p>и последний:</p><p>Nodes:= Root.SelectNodes(‘CD/TITLE’); </p><p>Чтобы найти диски Боба Дилана, можно выполнить следующий запрос:</p><p>Nodes:= Root.SelectNodes(‘CD[$any$ ARTIST= ”Bob Dylan”]/TITLE’); </p><p>а чтобы получить список дисков, выпущенных после 1985 года, мы выполняем следующий запрос:</p><p>Nodes:= Root.SelectNodes(‘CD/TITLE’); </p><p>Более подробное обсуждение синтаксиса XSL требует отдельной публикации. Чтобы заинтриговать читателей и подтолкнуть к дальнейшим исследованиям, приведу всего один небольшой пример возможного использования XSL. Допустим, нам необходимо преобразовать наш каталог в обычную HTML-таблицу. Пользуясь традиционными способами, мы должны перебрать все ветви дерева и для каждого полученного элемента сформировать соответствующие тэги <TD>…</TD>.</p> <p>Используя XSL, мы просто создаем шаблон (или таблицу стилей), в котором указываем, что и как надо преобразовать. Затем накладываем этот шаблон на наш каталог - и готово: перед нами текст XSL-шаблона, преобразующего каталог в таблицу (листинг 2).</p> <p>Код для наложения XSL-шаблона на наш каталог выглядит так:</p><p>Procedure TForm1.Button2Click(Sender: TObject); var XSLDoc: IXMLDOMDocument; begin XSLDoc:= CoDOMDocument.Create; XSLDoc.Load(‘C:\DATA\DATA.xsl’); Memo2.Text:= XMLDoc.TransformNode(XSLDoc); XSLDoc:= Nil; end; </p><p>Завершая наше обсуждение XSL, следует сказать, что в настоящее время этот язык активно используется для трансформации между различными XML-документами, а также для форматирования документов.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Заключение</h2> <p>По вполне понятным причинам в одной статье невозможно рассмотреть все объекты Microsoft XML DOM и привести примеры их использования. Здесь мы лишь коснулись основных вопросов использования XML DOM в приложениях. В табл. 3 показаны все объекты, реализованные в Microsoft XML DOM.</p> <p>КомпьютерПресс 12"2000</p> <p>Несмотря на то, что тема работы с XML в Delphi довольно широко обсуждалась в Интернете, вопросы на эту тему довольно часто возникают на всевозможных форумах. <br></p><p>Я тоже уже писал , но хотел бы вернуться к реальному случаю быстрого разбора XML файла и извлечения данных, который я сегодня проделал на работе. Получение необходимых данных у меня заняло не более 5 минут времени.<br></p><p>Предыстория . Сегодня понадобилось обработать данные об установленных программах на компьютерах пользователей (да, да, пиратов выявляем:)). Технический отдел предоставил мне такую информацию содраную с ничего не подозревающих пользователей по сети с использованием WMI . Программа, которой они пользовались выдает отчеты в формате XML. Соответственно - мне притащили гору XML файлов с довольно сложной структурой из которых мне необходимо было вытащить только название установленных программных продуктов.<br><span><br><p>Обработка . Просмотрев пару файлов вручную, понял что так и состариться не долго, и решил написать небольшой конвертер. Запустив Delphi - выбрал в репозитарии объект XML DataBinding и скормил ему один из файлов. Все настройки и параметры я оставил по умолчанию и в результате у меня сформировался модуль с большим количеством классов и интерфейсов для доступа к элементам этого XML файла. Я не стал долго разбираться со структурой классов, сразу же перешел к написанию конвертера.<br></p><p>В новом консольном приложении написал довольно простой код:<br></p> <br>program XML2TXT;</p><p>uses<br> Forms,<br> Classes, SysUtils,<br> SoftwareXML in "SoftwareXML.pas";</p><p>procedure CovertXML2Text;<br>var<br> softbase: IXMLSTDSoftwareType;<br> i: integer;<br> sr: TSearchRec;<br> CurDir: string;<br> ExportFile: TStringList;<br>begin<br> CurDir:= IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));<br> if FindFirst(CurDir+"*.xml", faAnyFile, sr) = 0 then<br> repeat<br> ExportFile:= TStringList.Create;<br> softbase:= LoadSTDSoftware(Pchar(CurDir+sr.Name));<br> for i:= 0 to softbase.InstalledSoftware.source.software.Count - 1 do<br> ExportFile.Add(softbase.InstalledSoftware.source.software[i].DisplayName);<br> ExportFile.Sort;<br> ExportFile.SaveToFile(CurDir + softbase.InstalledSoftware.Source.servername+".txt");<br> ExportFile.Free;<br> until FindNext(sr) 0;<br>end;</p><p>begin<br> Application.Initialize;<br> CovertXML2Text;<br>end. <br></p><p>В результате которого у меня образовался по одному текстовичку на каждый компьютер в сетке, содержащий список установленного ПО.<br></p><p>Чувствую что данный код потребует пояснений. Например, зачем я в консольном приложении использовал модуль Forms и вызывал процедуру Application.Initialize;?<br></p><p>На самом деле все просто - это небольшой хак, позволяющий использовать XML Data Binding в консольном приложении. Потому как в нем упорно отказывался инициализироваться класс для работы с XML. В истинных причинах пока не разбирался - сегодня было важно время, я и так 4 из 5 минут потратил на борьбу с этой ошибкой. :) Думаю позже разобраться с этой проблемой и написать в чем истинная причина.<br></p><p>Странный класс softbase был создан на основе XML файла - так назывался корневой элемент, а softbase.InstalledSoftware.source.software[i].DisplayName - просто навигация по вложенным элементам до нужного и получение его значения. <br></p><p>Вот собственно так выглядит один из самых быстрых способов работы с XML в Delphi.<br></p> <p>Последнее время много внимания уделяется построение систем электронного бизнеса, или как их еще называют - B2B (business to business). Учитывая рекомендации по построению обменных потоковых систем координирующего интернет-технологий органа - WWW Consortium: акцент сделан в сторону XML-технологий и построение систем обмена XML-документами.</p> <p>Преимущество использования XML в электронном бизнесе - высокая эффективность B2B систем при низких затратах на ее создание за счет четкого и наглядного представления структурированной информации, возможность использования современных сетевых протоколов и создания бизнес-систем реального времени.</p> <p>Независимость представления информации в виде XML документов позволяет разным, участвующим в электронном бизнесе, фирмам производить независимое друг от друга ПО.</p> <p>Во всех системах обмен, как правило, строится по одинаковой схеме, с использованием HTTP запросов. В качестве протокола защиты информации применяется протокол SSL (но это отдельная тема).</p> <p>Один из возможных вариантов обработки XML сообщения является построение BIN/CGI (ISAPI)-приложений или COM (серверных) компонент, формирующих или обрабатывающих XML-документы.</p> <p>С одной стороны, приложение выступает в качестве клиента, которое в режиме POST выдает HTTP запрос, с другой стороны, находится WEB сервер на стороне которого осуществляется обработка запроса и выдача ответа. В информационном обмене используются XML-документы.</p> <p>Один из наиболее эффективных вариантов реализации - использование существующего XML-парсера, поддерживающего DOM модель. Такой парсер является дистрибутивной поставкой Win`98 или составной частью IE 4,7 и выше (для Win`95) и представляет COM сервер, находящийся в библиотеке msxml.dll.</p> <p>Модель компонентных объектов (COM) - представляет инкапсулированные данные и методы в единую сущность и способ доступа к ним через систему интерфейсов. Средствами Delphi достаточно просто осуществить доступ к классам COM-объекта (в одном COM-сервере может быть включено несколько классов). Доступ к объектам осуществляется путем инициализации экземпляра класса через систему интерфейсов. Описание интерфейсов осуществляется языком определения интерфейсов (IDL), которое возможно осуществить средствами среды автоматически.</p> <p>Средствами Delphi осуществляется импорт из COM-сервера <i>msxml.dll </i>, строится файлы описания интерфейса IDL и файл бинарного описания типов библиотеки - TLB. Данная операция осуществляется через системное меню: <b>Project | Type Library Import: </b>(рисунок 1). Далее появляется диалоговое окно (рисунок 2), в котором необходимо выбрать COM-объект (в нашем случае объект зарегистрирован под именем "Microsoft.XMLDom (Version 2.0)") и создать TLB-файл (кнопка <b>Create Unit </b>). Используя TLB-файл, среда генерирует "паскалевский" файл описания COM-сервера - MSXML_TLB.pas</p> <p>В файле MSXML_TLB.pas описаны все интерфейсы, константы и соклассы COM-сервера.</p> <p>Для доступа к объектам COM-элемента, необходимо в директиве <b>USES </b>добавить имя файла описания библиотеки (MSXML_TLB.pas). Ниже представлена простейшая программа, использующая DOM стандартный анализатор msxml.dll, которая загружает XML-документ и отображает его в элементе текстового поля Memo1.</p> <b>uses </b> Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, OleServer, MSXML_TLB, StdCtrls; <b>type </b> TForm1 = <b>class </b>(TForm) Button1: TButton; Memo1: TMemo; <b>procedure </b> Button1Click(Sender: TObject); <b> end; </b> <b>var </b> Form1: TForm1; <b>implementation </b> {$R *.DFM} <b>Procedure </b> TForm1.Button1Click(Sender: Tobject); <span> // объявление сокласса объекта DOMDocument; </span> <b>var </b> coDoc: CoDOMDocument; <span> // класс, согласованный с интерфейсом IDOMDocument; </span> <b>var </b> Doc: IXMLDOMDocument; <b>begin </b> <span> // создание экземпляра объекта DOMDocument; </span> Doc:= coDoc.Create; <span> // вызов метода Load экземпляра объекта DOMDocument; </span> Doc.load("data.xml"); <span> // доступ к свойстве xml экземпляра объекта DOMDocument; </span> Memo1.Text:=Doc.xml; <b>end; </b> <b>end. </b> <h2>Концепция DOM - объектная модель документа</h2> <p>Каждый XML документ представляется в виде набора множества объектов (классов), с помощью которых возможен доступ к отдельным элементам (полям объекта). DOM - интерфейс описывает доступ как к простым объектам типа DOMString или CharacterData, так и к частям или отдельным элементам XML документа: DOMFragmentElement, DOMNode, DOMElement.</p> <p>Ниже приведены самые важные свойства и методы объектов XMLDOMDocument, XMLDOMNode, XMLDOMNodeList. Необходимо отметить, что представленные ниже методы и функции объектов DOM модели (Document Object Model) используются Microsoft XML-анализатором msxml.dll и несколько шире, чем утвержденная W3C Консорциумом DOM модель.</p> <p>Более полное описание интерфейса DOM объектов можно найти на </p> <table cellspacing="0" cellpadding="4" width="500" border="1"><tbody><tr><td valign="top" colspan="2"> Объект XMLDOMDocument </td> </tr><tr><td valign="top" colspan="2">Представляет верхний уровень объектной иерархии и содержит методы для работы с документом: его загрузки, анализа, создания в нем элементов, атрибутов, комментариев и т.д. . </td> </tr><tr><td valign="top" colspan="2"><b>Свойства </b> </td> </tr><tr><td valign="top" width="39%"><b>Async </b> </td> <td valign="top" width="61%">Свойство идентифицирующее текущий режим обработки </td> </tr><tr><td valign="top" width="39%" height="19"><b>ParseError </b> </td> <td valign="top" width="61%" height="19">Возвращает ссылку на объект обработки ошибки XMLDOMParseError </td> </tr><tr><td valign="top" width="39%"><b>Включение - выключение верификации документа. </b> </td> <td> </td> </tr><tr><td valign="top" width="39%"><b>url </b> </td> <td valign="top" width="61%">Возвращает URL документа </td> </tr><tr><td valign="top" width="39%"><b>documentElement </b> </td> <td valign="top" width="61%">Содержит ссылку на корневой элемент документа в виде объекта XMLDOMElement. </td> </tr><tr><td valign="top" colspan="2"><b>Методы </b> </td> </tr><tr><td valign="top" width="39%"><b>load(url) <br>loadXML(xmlString) </b> </td> <td valign="top" width="61%">Загружает XML документ, </td> </tr><tr><td valign="top" width="39%"><b>save(objTarget) </b> </td> <td valign="top" width="61%">Сохраняет XML документ в файле </td> </tr><tr><td valign="top" width="39%"><b>abort </b> </td> <td valign="top" width="61%">Прерывание процесса загрузки и обработки документа. </td> </tr><tr><td valign="top" width="39%"><b>createAttribute (name) </b> </td> <td valign="top" width="61%">Создает для текущего элемента новый атрибут с указанным именем. </td> </tr><tr><td valign="top" width="39%"><b>createNode(Type, name, nameSpaceURI) </b> </td> <td valign="top" width="61%">Создает узел указанного типа и названия </td> </tr><tr><td valign="top" width="39%"><b>createElement(tagName) </b> </td> <td valign="top" width="61%">Создает элемент документа с указанным названием. </td> </tr><tr><td valign="top" width="39%"><b>createTextNode(data) </b> </td> <td valign="top" width="61%">Создает текст внутри документа </td> </tr><tr><td valign="top" width="39%"><b>getElementsByTagName(tagname) </b> </td> <td valign="top" width="61%">Возвращает ссылку на коллекцию элементов документа с заданным именем </td> </tr><tr><td valign="top" width="39%"><b>nodeFromID(idString) </b> </td> <td valign="top" width="61%">Поиск элемента по идентификатору </td> </tr></tbody></table><br><table cellspacing="0" cellpadding="4" width="500" border="1"><tbody><tr><td valign="top" colspan="2"> <b>Объект XMLDOMNode </b> </td> </tr><tr><td valign="top" colspan="2">Объект XMLDOMNode, реализующий базовый DOM интерфейс <b>Node </b>, предназначен для манипулирования с отдельным узлом дерева документа. Его свойства и методы позволяют получать и изменять полную информацию о текущем узле - его тип, название, полное название, его содержимое, список дочерних элементов и т.д. </td> </tr><tr><td valign="top" colspan="2"><b>Свойства </b> </td> </tr><tr><td valign="top" width=" "><b>nodeName, baseName </b> </td> <td valign="top" width="65%">Возвращает название текущего узла. </td> </tr><tr><td valign="top" width="35%"><b>prefix </b> </td> <td valign="top" width="65%">Возвращает Namespace префикс. </td> </tr><tr><td valign="top" width="35%"><b>dataType </b> </td> <td valign="top" width="65%">Определяет тип содержимого текущего узла </td> </tr><tr><td valign="top" width="35%"><b>nodeType, nodeTypeString </b> </td> <td valign="top" width="65%">Возвращает тип текущего узла: </td> </tr><tr><td valign="top" width="35%"><b>attributes </b> </td> <td valign="top" width="65%">Возвращает список атрибутов текущего узла в виде коллекции XMLDOMNamedNodeMap. </td> </tr><tr><td valign="top" width="35%"><b>text </b> </td> <td valign="top" width="65%">Возвращает содержимое текущего поддерева в виде текста </td> </tr><tr><td valign="top" width="35%"><b>xml </b> </td> <td valign="top" width="65%">Возвращает XML-представление текущего поддерева. </td> </tr><tr><td valign="top" width="35%"><b>nodeValue </b> </td> <td valign="top" width="65%">Возвращает содержимое текущего узла. </td> </tr><tr><td valign="top" width="35%"><b>childNodes </b> </td> <td valign="top" width="65%">Возвращает список дочерних элементов в виде XMLDOMNodeList. </td> </tr><tr><td valign="top" width="35%"><b>firstChild, lastChild </b> </td> <td valign="top" width="65%">Возвращает первый/последний дочерний элемент </td> </tr><tr><td valign="top" width="35%"><b>previousSibling ,nextSibling </b> </td> <td valign="top" width="65%">Возвращает предыдущий /следующий сестринский элемент. </td> </tr><tr><td valign="top" width="35%"><b>parentNode </b> </td> <td valign="top" width="65%">Содержит ссылку на родительский элемент. </td> </tr><tr><td valign="top" width="35%"><b>ownerDocument </b> </td> <td valign="top" width="65%">Возвращает указатель на документ, в котором находится текущий узел. </td> </tr><tr><td valign="top" colspan="2"><b>Методы </b> </td> </tr><tr><td valign="top" width="35%"><b>appendChild(newChild) </b> </td> <td valign="top" width="65%">Добавляет текущему узлу новый дочерний элемент. </td> </tr><tr><td valign="top" width="35%"><b>insertBefore(newChild, refChild) </b> </td> <td valign="top" width="65%">Вставляет дочерний узел, располагая его в текущем поддереве "левее" узла, указанного параметром refChild. </td> </tr><tr><td valign="top" width="35%"><b>cloneNode (deep) </b> </td> <td valign="top" width="65%">Создание копии текущего элемента. </td> </tr><tr><td valign="top" width="35%"><b>getAttribute </b><b>(name)<br> </b><b>getAttributeNode </b><b><span> (name)<br>setAttribute(name, value)<br>setAttributeNode(XMLDOMAttribute) </span> </b> </td> <td valign="top" width="65%">Доступ к атрибутам (создание, чтение, запись) объекта. Name - имя атрибута, value - его значение. Возращает значение объект XMLDOMAttribute. </td> </tr><tr><td valign="top" width="35%"><b>replaceChild(newChild, oldChild) removeChild(oldChild) </b> </td> <td valign="top" width="65%">Замена объекта oldChild текущего списка дочерних объектов на newChild. Удаление объекта oldChild </td> </tr><tr><td valign="top" width="35%"><b>selectNodes(patternString) selectSingleNode(patternString) </b> </td> <td valign="top" width="65%">Возвращает объект XMLDOMNodeList, выбранное по шаблону поиска или первый узел </td> </tr><tr><td valign="top" width="35%"><b>transformNode(stylesheet)<br>transformNodeToObject(stylesheet, outputObject) </b> </td> <td valign="top" width="65%">Назначает стилевую таблицу для поддерева текущего узла и возвращает строку - результат обработки. В качестве параметра передается ссылка на объект DOMDocument, в котором находятся XSL инструкции. </td> </tr></tbody></table><br><h2>Использование XML в бизнесе.</h2> <p>Для более ясной картины необходимо пояснение, а зачем все это нужно с тем, что бы понять, как это работает:</p> <p>При построении B2B или корпоративной ERP системы, при организации информационного обмена XML-документами между предприятиями или филиалами пр-я, используются эффективно себя зарекомендовавшая система передачи информации на основе имеющихся WEB серверов по HTTP протоколам.</p> <p>С одной стороны, приложение выступает в качестве клиента, которое в режиме POST выдает HTTP запрос, с другой стороны, находится WEB сервер, на стороне которого осуществляется обработка запроса и выдача ответа. В качестве обмена используются XML-документы.</p> <p>Например, в простой корпоративной ERP системе бухгалтерской программе (АСУ Бухучет) необходимо сформировать некий запрос на выписку накладной и передать его в филиал, который имеет склад (АСУ Склад). АРМ Аналогичная постановка задачи при создании В2В системы, когда Предприятие А запрашивает наличие продукции (делает заказ на приобретение) у Поставщика В.</p> <p>Предприятие А и его программа выступает в качестве клиента. Склад обслуживает Поставщик В, у которого находится складской комплекс БД на SQL сервере. Обмен осуществляется через корпоративный WEB сервер Поставщика В.</p> <p>Ниже представлен следующий типовой алгоритм обмена:</p> <br>Рисунок 3. <ol><li><b>Предприятие А </b>инициирует <b>процесс А </b>(заказ продукции), который выступает в качестве WEB-клиента.</li><li><b>Процесс А </b>формирует XML документ (например запрос- накладная) и передает его как POST запрос http на WEB сервер Поставщика В. В качестве URI - используется идентификатор ресурса обрабатывающего приложения. URI может быть одинаковым как для всех типов документов, либо индивидуальным для каждого типа. Все зависит от структуры B2B (WEB) сервера.</li><li>WEB сервер анализирует запрос и порождает серверный <b>Процесс В </b>, передавая в качестве параметра тело XML документа. <br>Процессе В запускается WEB-сервером и обрабатывается либо как ASP страница,CGI (ISAPI) - приложение или JAVA севрлет (серверное приложение)</li><li><b>Процесс В </b>- формирует запрос на SQL-сервер базы данных.</li><li>SQL-сервер производит необходимые операции в БД, формирует ответ и возвращает его <b>Процессу В </b>.</li><li>По ответу от SQL-сервера <b>Процесс В </b> формирует XML документ (ответ) и возращает его как на ответ на http запрос клиентскому приложению.</li><li>Далее, в зависимости от ситуации на стороне клиента формируется либо новый http запрос, либо заканчивается сеанс.</li> </ol><h2>Несколько слов об организации документооборота.</h2> <p>Общим правилом разработки системы обмена XML документами является:</p><ul><li><b>во-первых </b>- разработка схемы потоков электронных документов и их структуры;</li><li><b>во-вторых </b>- разработка таблиц функций процессов (подпроцессов) т.е. какую функцию по отношению к какому XML-документу будет реализовывать каждый процесс.</li> </ul><p>Каждый XML документ, подобно HTML документу, должен состоять из заголовка сообщения (информация заключенная тагами) и тела сообщения (для запроса эта информация обрамленная тагами для ответа на запрос). Для того, чтобы XML документ был правильно сформирован, необходимо его две составные части "Заголовок" и "Запрос" обрамить тегами, например. Вид типового документа представлен ниже:</p> <p>Заголовок (Рисунок 4), в отличие HTML документа, должен содержать разного рода служебную информацию, в том числе информацию о типе передаваемого документа и процессе его обработки. В информационную обработку поступает тело документа, т.е. содержательная часть обрамленная тагами. Следует отметить, что структуру заголовков должна быть единой для всех типов документов.</p> <p>Для запущенного сервером Процесса, алгоритм обработки предпочтительно (но не обязательно) строить следующим образом:</p> <img src='https://i2.wp.com/codenet.ru/np-includes/upload/2005/01/05/128666.jpg' height="500" width="408" loading=lazy><br>Рисунок 6. <h2>Некоторые принципиальные моменты при создании клиентской части</h2> <p>Как уже пояснялось, при создание XML-документа используется его представление в виде DOM модели. Ниже приведен пример части текста Delphi программы создания заголовка xml сообщения.</p> <b>procedure </b> TThread1.HeaderCreate(Sender: Tobject); <b>var </b> <span> // объявление сокласса, необходим для создания </span> coDoc: CoDomDocument ; <span> // объекта XMLDomDocument </span> Doc: DomDocument ; r: IXMLDOMElement; Node: IXMLDOMElement; // DOMText txt: IXMLDOMText; // DOMAttribute attr: IXMLDOMAttribute; <b>begin </b> <span> // создание документа DOM </span> Doc:=coDoc.Create; Doc.Set_async(false); <span> // начальная инициация DOM документа </span> Doc.LoadXML("<Header/>"); <span> // создание DOMElement (таг <<b>Sender </b>>) </span> Node:= Doc.createElement ("Sender"); <span> // создание текстового узла "<b>ООО "Тайфун" </b>" </span> txt:= Doc.createTextNode("ООО "Тайфун""); <span> // присвоение узлу <<b>Sender </b>> значение </span> <span> // текстового узла "<b>ООО "Тайфун" </b>" </span> Node.appendChild(txt); <span> // добавление элемента <<b>Sender </b>> в корень документа как дочернего </span> r.appendChild(Node); <span> <<b>From </b>> </span> Node:= Doc.createElement ("From"); txt:= Doc.createTextNode("http://tayfun.ru/xml/default.asp"); Node.appendChild(txt); r.appendChild(Node); <span> // аналогичные операции для тага <<b>To </b>> </span> Node:= Doc.createElement ("To"); txt:= Doc.createTextNode("http://irbis.ru"); Node.appendChild(txt); r.appendChild(Node); <span> // создание DOMElement () </span> Node:= Doc.createElement ("TypeDocument"); <span> // создание узла XMLDOMAttribute </span> Att:= Doc.createAttribute ("Id ", " Order"); <span> // <TypeDocument Id="Order"/> </span> Node.appendChild(Att); r.appendChild(Node); <b>end; </b> <p>Следует отметить, что объявление переменной coDoc: CoDomDocument и Doc: DomDocument , а также ее создание методом Create (Doc:=coDoc.Create;) осуществляется один раз. Объявление переменной находится в секции описания глобальных переменных, а не в локальной процедуре, как было продемонстрировано для наглядности в данном примере (т.е. одна глобальная переменная типа DomDocument на один программный модуль).</p> <p>Результатом работы вышеприведенной программы будет созданный заголовок, применительно к нашему примеру xml-документа: изображен на рисунке 5.</p> <img src='https://i2.wp.com/codenet.ru/np-includes/upload/2005/01/05/128662.gif' height="116" width="298" loading=lazy><br>Рисунок 5.<p><img src='https://i0.wp.com/codenet.ru/np-includes/upload/2005/01/05/128664.gif' height="179" width="385" loading=lazy><br>Рисунок 6. </p><p>Основное преимущество передачи информации в виде XML-документов в том, что существует возможно формировать сообщение, используя независимые структуры таблиц в СУБД как на принимаемой, так и на передаваемой стороне. Используя наш пример, пусть требуется передать информацию об инвойсах Предприятия А, из СУБД имеющий структуру, изображенную на рисунке 6</p> <p>Для формирования xml-документа, содержащего инвойс первоначально строится SQL-запрос (запрос А) с информацией о самом инвойсе:</p> <b>SELECT </b> * FROM Invoice_General <b>WHERE </b> InvoiceNum = :num <b>SELECT </b> Goods,Qulity,Price, HZ_cod <b>FROM </b> Goods <b>WHERE </b> InvoiceNum = :num <span> // :num - параметр, который задает номер инвойса. </span> <p>Ниже представлена часть программы, формирующей тело xml-документа:</p> <b>procedure </b> TThread1.DataBodyCreate(Sender: Tobject); <b>var </b> <span> // объявление сокласса и объекта XMLDomDocument </span> //coDoc: CoDomDocument ; <span> // должно быть глобальным, для всего модуля. </span> //Doc: DomDocument ; <span> // объявление объектов DOMElement </span> r: IXMLDOMElement; // DOMElement; Node, Node2: IXMLDOMElement; Node3, Node4: IXMLDOMElement; // DOMText txt: IXMLDOMText; str: String; <span> // InvoiceNumber: <b>integer; </b> - глобальная переменная - // имеет значение 987654 // queryA, queryB: <b>String; </b> - глобальная переменная, // имеет значение,соответствующее запросу // queryA - запрос А генеральная информацией об инвойсе // queryB - запрос B информация о товарах, описываемых в // инвойсе (см. текст) </span> <b>begin </b> Query.Close; <span> // см. по тексту "запрос А" </span> Query.Text:= queryA; <span> // выполнение запроса </span> Query.ExecSQL; Query.Open; <span> // получение адреса корневого элемента </span> r:=Doc.Get_documentElement; Node2:= Doc.createElement (" Request "); <span> // создание DOMElement (таг) </span> Node:= Doc.createElement ("Invoice"); <span> // добавление элемента в корень </span> r.appendChild(Node2); <span> // добавление элемента в </span> Node2. appendChild(Node); <span> // создание DOMElement (таг) </span> Node3:= Doc.createElement ("Depurture") ; <span> // добавление элемента в </span> Node. appendChild(Node3); <span> // обращение к полю "Depurture" запроса </span> str:= Query.FieldByName("Depurture").AsString; <span> // создание текстового узла = значению поля </span><span> // присвоение узлу значение </span> <span> // текстового узла, переменной str </span> Node.appendChild(txt); <span> // аналогичные операции для тага <Destination>, <DataSend>, // <DataDepurture>, <Currency> // <DestinationCompany> (поле DB "Consignee") </span> Node:= Doc.createElement ("Destination"); <span> // имя поля БД может и не совпадать с именем </span> str:= Query.FieldByName("Consignee ").AsString; <span> // тага, в этом преимущество использования </span> txt:= Doc.createTextNode(str); <span> // DOM интерфейса перед СУБД, имеющим поддержку XML-интерфейса, // типа ORACLE 8i или Ms SQL 2000 </span> Node.appendChild(txt); ... <span> // формирование запроса на спецификацию по товарам </span> <span> // закрывает запрос для доступа </span> Query.Close; <span> // см. по тексту "запрос В", информац. О товарах </span> Query.Text:= queryВ; <span> // присваивание значения параметров </span> Query.Params.AsInteger:= InvoiceNumber; <span> // выполнение запроса </span> Query2.ExecSQL; <span> // открытие доступа к данным запроса </span> Query.Open; <span> // создание DOMElement (таг) </span> Node3:= Doc.createElement (" Imems") ; <span> // добавление элемента в </span> Node. appendChild(Node3); <span> // цикл по всем строкам запроса </span> <b>while </b> <b>not </b> Eof.Query <b>do </b> begin Node4:= Doc.createElement ("Imem"); <span> // добавление элемента в </span> Node3.appendChild(Node4); <span> // формирование данных для тага </span> str:= Query.FieldByName("Price").AsString; txt:= Doc.createTextNode(str); Node.appendChild(txt); ... <span>// аналогичные операции для тагов <HZ_Cod>, <Quality>, <GoodName> </span> <b>end; </b> <b>end; </b> <p>В результате выполнения данной процедуры формируется следующий текст XML-документа:</p> <table width="100%"><tbody><tr><td align="middle"><br><img src='https://i1.wp.com/codenet.ru/np-includes/upload/2005/01/05/128661.gif' width="100%" loading=lazy></td> </tr></tbody></table><p>Для формирования запроса используется метод Open объекта <b>IXMLHttpRequest </b>:</p> <b>procedure </b> Open(const bstrMethod, - тип метода ="POST" bstrUrl, - Url адрес сервера varAsync, - режим связи асинхронный/синхронный = true bstrUser, - имя пользователя для аутентификации bstrPassword) - пароль <h2>Создании серверной части обработки документа</h2> <p>Как было отмечено ранее, обработка HTTP запроса может осуществляться либо CGI-приложениями, либо Java-сервлетами. Возможен и вариант написания ASP-страниц. Но в этом случае передача данных возможна только методом "GET" через строку запроса. Хотя, обработка HTTP запроса ASP-страниц работает более эффективнее, чем CGI-приложением. Однако, на мой взгляд, без разницы, как обрабатывать, а важнее решить вопрос - как построить программу обработки, а не какими средствами.</p> <p>Если из предыдущей главы мы рассмотрели варианты формирования XML-документ, то задача серверного приложения обратная - разбор XML-документов. Ниже представлена часть программы, осуществляющей разбор xml-документа:</p> <b>procedure </b> Tthread1.DataParser(Sender: Tobject); <b>var </b> <span>// объявление объектов DOMElement </span> r,FNode: IXMLDOMElement; Str,Filename: String; parm: String; <span>// объявление сокласса и </span> CoDocXML, CoDocXSL, CoDocResult: CoDomDocument ; <span>// объекта XMLDomDocument </span> XMLDoc, XSLDoc, ResultDoc: DomDocument ; <span>// HttpStr: String; - глобальная переменная, содержащая строку HTTP запроса </span> <b>Begin </b> XMLDoc:=coDocXML.Create; XMLDoc.LoadXML(HttpStr); <span> // получение адреса корневого элемента </span> r:=Doc.Get_documentElement; <span> // получение значения элемента </span> FNode:= r.SelectSingleNode("//TypeDocument"); <span> // получение значения атрибута id="Order" </span> FileName:= FNode.GetAttibute("id"); <span> // и формирование имени файла Order.xsl </span> FileName:= FileName+".xsl"; <span> // создание документа XSLDoc </span> XSLDoc:=coDocXSL.Create; XSLDoc.LoadXML(FileName); <span> // создание документа XMLDoc </span> ResultDoc:=coDocResult.Create; <span> // установка синхронного режима обработки </span> ResultDoc.Set_async(false); <span> // установка проверки разбора </span> ResultDoc.validateOnParse:= true; <span> // разбор XMLDoc по XSL-шаблону </span> XMLDoc.transformNodeToObject(XSLDoc, ResultDoc); <span> // переменной Str присваивается текстовое значение </span> <span> // результирующего документа. </span> Str:= ResultDoc.text; <span> // поиск элемента </span> FNode:= r.SelectSingleNode("//InvoiceNumber"); <span> // и получение значения элемента </span> parm:= FNode.text; <span> // закрывает запрос для доступа </span> Query.Close; Query.Text:= Str; <span> // присваивание значения параметра </span> Query.Params.AsString:= parm; <span> // выполнение запроса </span> Query.ExecSQL; <b>end; </b> <p>Вся изюминка разбора заключается в применении XSL-шаблона, который сформирован для каждого типа документа индивидуально. Результатом разбора является строка SQL-запроса. В последствие выполнение сформированной строки SQL-запроса осуществит необходимые изменения данных в СУБД.</p> <p>Преимущество использования разбора через шаблон еще и в том, что получается некая гибкость данных, и получается полная независимость работы алгоритма от программного кода. Ниже приведен используемый для обработки документа типа ORDER текст XSL-шаблона:</p><p> <!-- файл Order.xsl --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <xsl:for-each select="//header"> INSERT into TABREG (FROM, TO, TYPEDOC,body) VALUES(" <xsl:value-of select="from" />", "<xsl:value-of select="to" />", "<xsl:value-of select="TypeDocument/@id" />") </xsl:for-each> <xsl:for-each select="//item"> INSERT into GOODS (invoiceNumber, name, price, quality) VALUES(" :num", "<xsl:value-of select="name" />", "<xsl:value-of select="price" />", "<xsl:value-of select="quality" /> ") </xsl:for-each> </xsl:template> </xsl:stylesheet> </p><p>Поясняя вышеприведенный пример, надо отметить, что использование пары тагов и носит формальный характер, т.к. после разбора в результирующем XML-документе формально должен присутствовать хотя бы один узел. Метод ResultDoc.text присваивает текстовое значение полученного в ходе разбора XML-документа ResultDoc. В этом случае значением является все то, что обрамлено пары тегов и, т.е. сформированный нами SQL-запрос.</p> <p>Другой особенностью написания программы надо отметить возможность использования SQL-параметра <b>:num. </b> Использование параметра позволяет упростить текст xsl-шаблона. Определение значение соответствующих элементов узлов XML-документа определяется первоначально выбора по имени соответствующего узла, например:</p><h2>Коротко об XSL</h2> <p>Аббревиатура XSL происходит от eXtensible Stylesheet Language - язык форматирования таблиц стилей (XML данных). Как понятно из заголовка eXtensible Stylesheet Language (XSL) используется для форматирования XML данных. По определению W3C XSL состоит из двух частей:</p> <ul><li>XSLT - XSL Transformation. Язык, используемый для преобразования или форматирования (трансформирования) XML документов. Таким образом, при помощи XSLT мы можем получить разные разрезы множества данных и формы представления данных.</li><li>Элементы форматирования. К этим элементам относятся все элементы типографического оформления данных, после их обработки их при помощи XSL. Используется только для формирования HTML страниц.</li> </ul><p>При помощи XSLT мы можем отобрать нужные нам данные из XML файла, и оформить их в виде для предоставления пользователю. Например, в нашем случае мы преобразовали XML данные в виде SQL запроса. Классическое применение XSL - это, как правило форматирование данных в виде HTML страниц или более редкое представление в виде RTF файлов.</p> <p>XSL файл описывает шаблон (template), согласно которому будет совершаться преобразование XML данных. Возвращаясь к xsl-шаблонам, в XSLT можно выделить следующие элементы (директивы):</p> <table cellspacing="0" cellpadding="4" width="500" border="1"><tbody><tr><td valign="top" width="31%"> <b>XSL-директивы </b> </td><th align="middle" width="69%"> <b>описание </b> </th> </tr><tr><td>xsl:apply-templates </td> <td>Директива, указывающая на применение соответствующих шаблонов атрибуту select="имя шаблона" </td> </tr><tr><td>xsl:attribute </td> <td>создает дерево атрибутов и добавляет его в выходной элемент, параметр name="имя атрибута", namespace - URI на пространство имен (префикс пространства имен) </td> </tr><tr><td>xsl:call-template </td> <td>вызывает шаблон, атрибуту name=" URI на шаблон" </td> </tr><tr><td>xsl:choose<br>xsl:when<br>xsl:otherwise </td> <td>осуществление выбора по условию xsl:when expr="вычисление выражения на script ", <br>language="language-name" <br>test= "вычисляемое выражение" </td> </tr><tr><td>xsl:comment </td> <td>генерирует комментарий в выходной документ </td> </tr><tr><td>xsl:copy<br>xsl:copy-of </td> <td>копирует текущей узел в выходной источник или вставляет фрагмент документа в узел, где атрибут select="имя узла источника" </td> </tr><tr><td>xsl:element </td> <td>создает выходной элемент по имени, атрибут name="имя элемента", namespace="uri ссылка на пространство имен" </td> </tr><tr><td>xsl:for-each </td> <td>повторно применяет шаблон ко всем узлам списка узлов, атрибут select задает список узлов </td> </tr><tr><td>xsl:if </td> <td>проверка условия, задается атрибутом test в виде выражения </td> </tr><tr><td>xsl:include </td> <td>включает внешний шаблон, атрибут href = "URI reference" </td> </tr><tr><td>xsl:output </td> <td>специфицирует выходной результат, атрибут method может иметь значения "xml", "html" или "text" </td> </tr><tr><td>xsl:param </td> <td>специфицирует значение параметров, атрибут name="имя параметра", select = "значение" </td> </tr><tr><td>xsl:processing-instruction </td> <td>создает инструкцию обработки, атрибут name="имя процесс инструкции" </td> </tr><tr><td>xsl:sort </td> <td>сортирует множество узлов, атрибуты select = "имя узла", data-type = тип данных {"text" | "number" | Qname}, order = направление сортировки {"ascending" | "descending"} </td> </tr><tr><td>xsl:stylesheet </td> <td>определяет документ xsl-шаблонов, является корневым элементом для XSLT </td> </tr><tr><td>xsl:template </td> <td>определяет xsl-шаблон, атрибут name= " URI префикс на имя шаблона", match= "указание на узел, к которому применяется шаблон" </td> </tr><tr><td>xsl:text </td> <td>генерирует текст в выходной поток, атрибут disable-output-escaping = "yes" или "no", указывает на возможность генерации символов ESC </td> </tr><tr><td>xsl:value-of </td> <td>вставляет значение выбранного узла как текст, атрибут select= "указатель на узел" из которого берут значение </td> </tr><tr><td>xsl:variable </td> <td>специфицирует значение границ переменных, атрибут name = "имя переменной", select = "вычисление значения переменной" </td> </tr><tr><td>xsl:with-param </td> <td>применяет параметр к шаблону, атрибут name ="имя параметра", select = выражение для вычисления текущего контекста, значение по умолчанию "." </td> </tr></tbody></table><h2>Заключение</h2> <p>В заключение, необходимо отметить, что использование стандартного XML-парсера <i>msxml.dll </i>является не единственным средством разбора и создания XML-документов. Например, для создание XML документов эффективно использовать компоненты <b>TPageProduser </b>и <b>TТableProduser </b>. Но, данная статья лишь подчеркивает широту и возможность применения на практике DOM модели.</p> <p>Автор будет очень благодарен за отзывы об актуальности темы, общем содержании, стиле изложения, а также всем остальным комментариям, которые помогут в дальнейшем улучшить качество написания сборника статей и выпуску книги, освещающую тему практической стороны использования XML-документов в электронной коммерции. Более подробную информацию о практической стороне применения электронных документов можно почерпнуть на авторском сайте www.eDocs.al.ru Также на авторском сайте планируется разместить исходные тексты и примеры.</p> <p>Язык XML все чаще используется для хранения информации, обмена ею между приложениями и Web-узлами. Во многих приложениях этот язык применяется в качестве базового для хранения данных, в других - для экспортирования и импортирования XML-данных. Из этого следует, что разработчикам пора задуматься над тем, как можно использовать XML-данные в собственных приложениях.</p> <p>В этой статье мы рассмотрим XML Document Object Model (DOM) и ее реализацию фирмой Microsoft - Microsoft XML DOM.</p> <p>XML DOM - это объектная модель, предоставляющая в распоряжение разработчика объекты для загрузки и обработки XML-файлов. <a href="/beeline/obektnaya-model-dokumenta-dom-rabota-s-dom-modelyu-vzaimodeistvie-s-konsolyu.html">Объектная модель</a> состоит из следующих основных объектов: XMLDOMDocument, XMLDOMNodeList, XMLDOMNode, XMLDOMNamedNodeMap и XMLDOMParseError. Каждый из этих объектов (кроме XMLDOMParseError) содержит свойства и методы, позволяющие получать информацию об объекте, манипулировать значениями и структурой объекта, а также перемещаться по структуре XML-документа.</p> <p>Рассмотрим основные объекты XML DOM и приведем несколько примеров их использования в Borland Delphi.</p> <h2> Использование XML DOM в Borland Delphi </h2> <p>Для того чтобы использовать Microsoft XML DOM в Delphi-приложениях, необходимо подключить к проекту соответствующую библиотеку типов. Для этого мы выполняем команду Project | Import Type Library и в диалоговой панели Import Type Library выбираем библиотеку Microsoft XML version 2.0 (Version 2.0), которая обычно находится в файле Windows\System\MSXML.DLL</p> <p>После нажатия кнопки Create Unit будет создан интерфейсный модуль MSXML_TLB, который позволит нам воспользоваться объектами XML DOM: DOMDocument, XMLDocument, XMLHTTPRequest и рядом других, реализованных в библиотеке MSXML.DLL. Ссылка на модуль MSXML_TLB должна быть указана в списке Uses.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Устройство XML DOM</h2> <p>Document Object Model представляет XML-документ в виде древовидной структуры, состоящей из ветвей. <a href="/beeline/winstep-xtreme-temy-winstep-xtreme-programmnyi-paket-dlya-izmeneniya-interfeisa-windows-v.html">Программные интерфейсы</a> XML DOM позволяют приложениям перемещаться по дереву документа и манипулировать его ветвями. Каждая ветвь может иметь специфический тип (DOMNodeType), согласно которому определяются родительская и дочерние ветви. В большинстве XML-документов можно встретить ветви типа element, attribute и text. Атрибуты (attribute) представляют собой особый вид ветви и не являются дочерними ветвями. Для управления атрибутами используются специальные методы, предоставляемые объектами XML DOM.</p> <p>Помимо реализации рекомендованных World Wide Web Consortium (W3C) интерфейсов, Microsoft XML DOM содержит методы, поддерживающие XSL, XSL Patterns, Namespaces и типы данных. Например, метод SelectNodes позволяет использовать синтаксис шаблонов XSL (XSL Pattern Syntax) для поиска ветвей по определенному контексту, а метод TransformNode поддерживает использование XSL для выполнения трансформаций.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Тестовый XML-документ</h2> <p>В качестве примера XML-документа возьмем каталог музыкальных CD-ROM, который имеет следующую структуру :</p><p> <?xml version=”1.0"?> <CATALOG> <CD> <TITLE>Empire Burlesque Bob Dylan USA Columbia 10.90 1985 Hide your heart Bonnie Tylor UK CBS Records 9.90 1988 ... Unchain my heart Joe Cocker USA EMI 8.20 1987

Теперь мы готовы приступить к рассмотрению объектной модели XML DOM, знакомство с которой начнем с объекта XMLDOMDocument.

XML-документ - объект XMLDOMDocument

Работа с XML-документом начинается с его загрузки. Для этого мы используем метод Load, который имеет всего один параметр, указывающий URL загружаемого документа. При загрузке файлов с локального диска указывается только полное имя файла (протокол file:/// в данном случае можно опустить). Если XML-документ хранится в виде строки, для загрузки такого документа следует использовать метод LoadXML.

Для управления способом загрузки документа (синхронный или асинхронный) используется свойство Async. По умолчанию это свойство имеет значение True, указывающее на то, что документ загружается асинхронно и управление возвращается приложению еще до полной загрузки документа. В противном случае документ загружается синхронно, и тогда приходится проверять значение свойства ReadyState, чтобы узнать, загрузился документ или нет. Также можно создать обработчик события OnReadyStateChange, который получит управление при изменении значения свойства ReadyState.

Ниже показано, как загрузить XML-документ, используя метод Load:

Uses ... MSXML_TLB ... procedure TForm1.Button1Click(Sender: TObject); var XMLDoc: IXMLDOMDocument; begin XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.Load(‘C:\DATA\DATA.xml’); // // Здесь располагается код, манипулирующий // XML-документом и его ветвями // XMLDoc:= Nil; end;

После того как документ загружен, мы можем обратиться к его свойствам. Так, свойство NodeName будет содержать значение #document, свойство NodeTypeString - значение document, свойство URL - значение file:///C:/DATA/DATA.xml.

Обработка ошибoк

Особый интерес представляют свойства, связанные с обработкой документа при его загрузке. Так, свойство ParseError возвращает объект XMLDOMParseError, содержащий информацию об ошибке, возникшей в процессе обработки документа.

Чтобы написать обработчик ошибки, можно добавить следующий код:

Var XMLError: IXMLDOMParseError; ... XMLDoc.Load(‘C:\DATA\DATA.xml’); XMLError:= XMLDoc.ParseError; If XMLError.ErrorCode <> 0 Then // // Здесь мы обрабатываем ошибку // Else Memo1.Lines.Add(XMLDoc.XML); ... XMLDoc:= Nil;

Чтобы узнать, какая информация возвращается в случае ошибки, изменим следующий элемент каталога:

Empire Burlesque Bob Dylan USA Columbia 10.90 1985

убрав закрывающий элемент во второй строке:</p><p> <CD> <TITLE>Empire Burlesque <ARTIST>Bob Dylan</ARTIST> <COUNTRY>USA</COUNTRY> <COMPANY>Columbia</COMPANY> <PRICE>10.90</PRICE> <YEAR>1985</YEAR> </CD> </p><p>Теперь напишем код, возвращающий значения свойств объекта XMLDOMParseError:</p><p>XMLError:= XMLDoc.ParseError; If XMLError.ErrorCode <> 0 Then With XMLError, Memo1.Lines do begin Add(‘Файл: ‘ + URL); Add(‘Код: ‘ + IntToStr(ErrorCode)); Add(‘Ошибка: ‘ + Reason); Add(‘Текст: ‘ + SrcText); Add(‘Строка: ‘ + IntToStr(Line)); Add(‘Позиция: ‘ + IntToStr(LinePos)); end Else Memo1.Lines.Add(XMLDoc.XML); End; </p><p>и выполним наше приложение. В результате получаем следующую информацию об ошибке .</p> <p>Как видно из приведенного примера, возвращаемой объектом XMLDOMParseError информации вполне достаточно для того, чтобы локализовать ошибку и понять причину ее возникновения.</p> <p>Теперь восстановим закрывающий элемент <TITLE> в нашем документе и продолжим обсуждение XML DOM.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Доступ к дереву документа</h2> <p>Для доступа к дереву документа можно либо получить корневой элемент и затем перебрать его дочерние ветви, либо найти какую-то специфическую ветвь. В первом случае мы получаем корневой элемент через свойство DocumentElement, которое возвращает объект типа XMLDOMNode. Ниже показано, как воспользоваться свойством DocumentElement для того, чтобы получить содержимое каждого дочернего элемента:</p><p>Var Node: IXMLDOMNode; Root: IXMLDOMElement; I: Integer; ... Root:= XMLDoc.DocumentElement; For I:= 0 to Root.ChildNodes.Length-1 do Begin Node:= Root.ChildNodes.Item[I]; Memo1.Lines.Add(Node.Text); End; </p><p>Для нашего XML-документа мы получим следующий текст .</p> <p>Если нас интересует какая-то специфическая ветвь или ветвь уровнем ниже первой дочерней ветви, мы можем воспользоваться либо методом NodeFromID, либо методом GetElementByTagName объекта XMLDOMDocument.</p> <p>Метод NodeFromID требует указания уникального идентификатора, определенного в XML Schema или Document Type Definition (DTD), и возвращает ветвь с этим идентификатором.</p> <p>Метод GetElementByTagName требует указания строки со специфическим элементом (тэгом) и возвращает все ветви с данным элементом. Ниже показано, как использовать данный метод для нахождения всех исполнителей в нашем каталоге CD-ROM:</p><p>Nodes: IXMLDOMNodeList; Node: IXMLDOMNode; ... Nodes:= XMLDoc.GetElementsByTagName(‘ARTIST’); For I:= 0 to Nodes.Length-1 do Begin Node:= Nodes.Item[I]; Memo1.Lines.Add(Node.Text); End; </p><p>Для нашего XML-документа мы получим следующий текст </p> <p>Отметим, что метод SelectNodes объекта XMLDOMNode обеспечивает более гибкий способ для доступа к ветвям документа. Но об этом чуть ниже.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Ветвь документа - объект XMLDOMNode</h2> <p>Объект XMLDOMNode представляет собой ветвь документа. Мы уже сталкивались с этим объектом, когда получали корневой элемент документа:</p><p>Root:= XMLDoc.DocumentElement; </p><p>Для получения информации о ветви XML-документа можно использовать свойства объекта XMLDOMNode (табл. 1).</p> <p>Для доступа к данным, хранимым в ветви, обычно используют либо свойство NodeValue (доступно для атрибутов, текстовых ветвей, комментариев, инструкций по обработке и секций CDATA), либо свойство Text, возвращающее текстовое содержимое ветви, либо свойство NodeTypedValue. Последнее, однако, может использоваться только для ветвей с типизованными элементами.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h3> Перемещение по дереву документа</h3> <p>Объект XMLDOMNode предоставляет множество способов для перемещения по дереву документа. Например, для доступа к родительской ветви используется свойство ParentNode (тип XMLDOMNode), доступ к дочерним ветвям осуществляется через свойства ChildNodes (тип XMLDOMNodeList), FirstChild и LastChild (тип XMLDOMNode) и т.д. Свойство OwnerDocument возвращает объект типа XMLDOMDocument, идентифицирующий сам XML-документ. Перечисленные выше свойства позволяют легко перемещаться по дереву документа.</p> <p>Теперь переберем все ветви XML-документа:</p><p>Root:= XMLDoc.DocumentElement; For I:= 0 to Root.ChildNodes.Length-1 do Begin Node:= Root.ChildNodes.Item[I]; If Node.HasChildNodes Then GetChilds(Node,0); End; </p><p>Как уже отмечалось выше, SelectNodes объекта XMLDOMNode обеспечивает более гибкий способ доступа к ветвям документа. Кроме того, существует метод SelectSingleNode, возвращающий только первую ветвь документа. Оба эти метода позволяют задавать XSL-шаблоны для поиска ветвей.</p> <p>Рассмотрим процесс использования метода SelectNodes для извлечения всех ветвей, у которых есть ветвь CD и подветвь PRICE:</p><p>Root:= XMLDoc.DocumentElement; Nodes:= Root.SelectNodes(‘CD/PRICE’); </p><p>В коллекцию Nodes будут помещены все подветви PRICE ветви CD. К обсуждению XSL-шаблонов вернемся чуть позже.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h3> Манипуляция дочерними ветвями</h3> <p>Для манипуляции дочерними ветвями мы можем воспользоваться методами объекта XMLDOMNode (табл. 2).</p> <p>Для того чтобы полностью удалить запись о первом диске, необходимо выполнить следующий код :</p><p>Var XMLDoc: IXMLDOMDocument; Root: IXMLDOMNode; Node: IXMLDOMNode; XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.Load(‘C:\DATA\DATA.xml’); // Получить корневой элемент Root:= XMLDoc.DocumentElement; Node:= Root; // Удалить первую дочернюю ветвь Node.RemoveChild(Node.FirstChild); </p><p>Обратите внимание на то, что в данном примере мы удаляем первую дочернюю ветвь. Как удалить первый элемент первой дочерней ветви, показано ниже :</p><p>Var XMLDoc: IXMLDOMDocument; Root: IXMLDOMNode; Node: IXMLDOMNode; XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.Load(‘C:\DATA\DATA.xml’); // Получить корневой элемент Root:= XMLDoc.DocumentElement; // и первую дочернюю ветвь Node:= Root.FirstChild; // Удалить первую дочернюю ветвь Node.RemoveChild(Node.FirstChild); </p><p>В приведенном выше примере мы удалили не первую ветвь <CD>…</CD>, а первый элемент ветви - <TITLE>….

Теперь добавим новую ветвь. Ниже приведен код, показывающий, как добавить новую запись о музыкальном CD-ROM :

Var NewNode: IXMLDOMNode; Child: IXMLDOMNode; ... // Создадим новую ветвь - NewNode:= XMLDoc.CreateNode(1, ‘CD’, ‘’); // Добавим элемент Child:= XMLDoc.CreateNode(1,‘TITLE’,‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘Pink Floyd’; // Добавим элемент <ARTIST> Child:= XMLDoc.CreateNode(1, ‘ARTIST’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘Division Bell’; // Добавим элемент <COUNTRY> Child:= XMLDoc.CreateNode(1, ‘COUNTRY’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘UK’; // Добавим элемент <COMPANY> Child:= XMLDoc.CreateNode(1, ‘COMPANY’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘EMI Records Ltd.’; // Добавим элемент <PRICE>Child:= XMLDoc.CreateNode(1, ‘PRICE’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ’11.99"; // Добавим элемент <YEAR> Child:= XMLDoc.CreateNode(1, ‘YEAR’, ‘’); // Добавим элемент NewNode.AppendChild(Child); // И установим его значение Child.Text:= ‘1994’; // И добавим ветвь Root.AppendChild(NewNode); ... </p><p>Приведенный выше код показывает следующую последовательность действий по добавлению новой ветви:</p> <ul><li>Создание новой ветви методом CreateNode: <ul><li>создание элемента методом CreateNode;</li> <li>добавление элемента к ветви методом AppendChild;</li> <li>установка значения элемента через свойство Text;</li> <li>… повторить для всех элементов.</li> </ul></li> <li>Добавление новой ветви к документу методом AppendChild.</li> </ul><p>Напомним, что метод AppendChild добавляет ветвь в конец дерева. Для того чтобы добавить ветвь в конкретное место дерева, необходимо использовать метод InsertBefore.</p> <h2> Набор ветвей - объект XMLDOMNodeList</h2> <p>Объект XMLNodeList содержит список ветвей, который может быть построен с помощью методов SelectNodes или GetElementsByTagName, а также получен из свойства ChildNodes.</p> <p>Мы уже рассматривали использование этого объекта в примере, приведенном в разделе «Перемещение по дереву документа». Здесь же мы приведем некоторые теоретические замечания.</p> <p>Число ветвей в списке может быть получено как значение свойства Length. Ветви имеют индексы от 0 до Length-1, и каждая отдельная ветвь доступна через элемент массива Item с соответствующим индексом.</p> <p>Перемещение по списку ветвей также может осуществляться с помощью метода NextNode, возвращающего следующую ветвь в списке, или Nil, если текущая ветвь - последняя. Чтобы вернуться к началу списка, следует вызвать метод Reset.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Создание и сохранение документов</h2> <p>Итак, мы рассмотрели, как можно добавлять ветви и элементы в существующие XML-документы. Теперь создадим XML-документ «на лету». Прежде всего напомним, что документ может быть загружен не только из URL, но и из обычной строки. Ниже показано, как создать корневой элемент, который затем может использоваться для динамического построения остальных элементов (что мы уже рассмотрели в разделе «Манипуляция дочерними ветвями»):</p><p>Var XMLDoc: IXMLDOMDocument; Root: IXMLDOMNode; Node: IXMLDOMNode; S: WideString; ... S:= ‘<CATALOG></CATALOG>’; XMLDoc:= CoDOMDocument.Create; XMLDoc.Async:= False; XMLDoc.LoadXML(S); Root:= XMLDoc.DocumentElement; Node:= XMLDoc.CreateNode(1, ‘CD’, ‘’); Root.AppendChild(Node); Memo1.Lines.Add(XMLDoc.XML); ... XMLDoc:= Nil; </p><p>После построения XML-документа сохраним его в файле с помощью метода Save. Например:</p> <p>XMLDoc.Save(‘C:\DATA\NEWCD.XML’);</p> <p>Помимо сохранения в файле метод Save позволяет сохранять XML-документ в новом объекте XMLDOMDocument. В этом случае происходит полная обработка документа и, как следствие, проверка его структуры и синтаксиса. Ниже показано, как сохранить документ в другом объекте:</p><p>Procedure TForm1.Button2Click(Sender: TObject); var XMLDoc2: IXMLDOMDocument; begin XMLDoc2:= CoDOMDocument.Create; XMLDoc.Save(XMLDoc2); Memo2.Lines.Add(XMLDoc2.XML); ... XMLDoc2:= Nil; end; </p><p>В заключение отметим, что метод Save также позволяет сохранять XML-документ в другие COM-объекты, поддерживающие интерфейсы IStream, IPersistStream или IPersistStreamInit.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Использование XSL-шаблонов</h2> <p>Обсуждая метод SelectNodes объекта XMLDOMNode, мы упомянули о том, что он обеспечивает более гибкий способ доступа к ветвям документа. Гибкость заключается в том, что в качестве критерия для выбора ветвей можно указать XSL-шаблон. Такие шаблоны предоставляют мощный механизм для поиска информации в XML-документах. Например, для того, чтобы получить список всех названий музыкальных CD-ROM в нашем каталоге, можно выполнить следующий запрос:</p><p>Чтобы узнать, диски каких исполнителей выпущены в США, запрос формируется следующим образом:</p><p>Nodes:= Root.SelectNodes(‘CD/ARTIST’); </p><p>Ниже показано, как найти первый диск в каталоге:</p><p>Nodes:= Root.SelectNodes(‘CD/TITLE’); </p><p>и последний:</p><p>Nodes:= Root.SelectNodes(‘CD/TITLE’); </p><p>Чтобы найти диски Боба Дилана, можно выполнить следующий запрос:</p><p>Nodes:= Root.SelectNodes(‘CD[$any$ ARTIST= ”Bob Dylan”]/TITLE’); </p><p>а чтобы получить список дисков, выпущенных после 1985 года, мы выполняем следующий запрос:</p><p>Nodes:= Root.SelectNodes(‘CD/TITLE’); </p><p>Более подробное обсуждение синтаксиса XSL требует отдельной публикации. Чтобы заинтриговать читателей и подтолкнуть к дальнейшим исследованиям, приведу всего один небольшой пример возможного использования XSL. Допустим, нам необходимо преобразовать наш каталог в обычную HTML-таблицу. Пользуясь традиционными способами, мы должны перебрать все ветви дерева и для каждого полученного элемента сформировать соответствующие тэги <TD>…</TD>.</p> <p>Используя XSL, мы просто создаем шаблон (или таблицу стилей), в котором указываем, что и как надо преобразовать. Затем накладываем этот шаблон на наш каталог - и готово: перед нами текст XSL-шаблона, преобразующего каталог в таблицу (листинг 2).</p> <p>Код для наложения XSL-шаблона на наш каталог выглядит так:</p><p>Procedure TForm1.Button2Click(Sender: TObject); var XSLDoc: IXMLDOMDocument; begin XSLDoc:= CoDOMDocument.Create; XSLDoc.Load(‘C:\DATA\DATA.xsl’); Memo2.Text:= XMLDoc.TransformNode(XSLDoc); XSLDoc:= Nil; end; </p><p>Завершая наше обсуждение XSL, следует сказать, что в настоящее время этот язык активно используется для трансформации между различными XML-документами, а также для форматирования документов.</p> <table border="0" width="100%"><tr><td width="50%"> </td> <td width="50%"> </td> </tr></table><h2> Заключение</h2> <p>По вполне понятным причинам в одной статье невозможно рассмотреть все объекты Microsoft XML DOM и привести примеры их использования. Здесь мы лишь коснулись основных вопросов использования XML DOM в приложениях. В табл. 3 показаны все объекты, реализованные в Microsoft XML DOM.</p> <p>КомпьютерПресс 12"2000</p> <script>document.write("<img style='display:none;' src='//counter.yadro.ru/hit;artfast_after?t44.1;r"+ escape(document.referrer)+((typeof(screen)=="undefined")?"": ";s"+screen.width+"*"+screen.height+"*"+(screen.colorDepth? screen.colorDepth:screen.pixelDepth))+";u"+escape(document.URL)+";h"+escape(document.title.substring(0,150))+ ";"+Math.random()+ "border='0' width='1' height='1' loading=lazy>");</script> </div> <div class="bsb-wrap bsb-bold bsb-after-post php-mode" data-post-id="4848" data-share-url="/eshhe-odin-nalog-chem-zamenit-nalog-na-modernizatsiyu/"> <div class="bsb-items"> <div class="bsb-item bsb-facebook bsb-no-count" data-id="facebook"> <a href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Fqipu.ru%2Fmts%2Fispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" class="bsb-link" target="_blank" data-wpel-link="internal"> <i class="bsb-icon icon icon-facebook"></i> <span class="bsb-label">Share</span> </a> </div> <div class="bsb-item bsb-telegram bsb-no-count" data-id="telegram"> <a href="tg://msg?text=https%3A%2F%2Fqipu.ru%2Fmts%2Fispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" class="bsb-link" target="_blank" data-wpel-link="internal"> <i class="bsb-icon icon icon-telegram"></i> <span class="bsb-label">Share</span> </a> </div> <div class="bsb-item bsb-twitter bsb-no-count" data-id="twitter"> <a href="https://twitter.com/share?url=https%3A%2F%2Fqipu.ru%2Fmts%2Fispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" class="bsb-link" target="_blank" data-wpel-link="internal"> <i class="bsb-icon icon icon-twitter"></i> <span class="bsb-label">Tweet</span> </a> </div> <div class="bsb-item bsb-vkontakte bsb-no-count" data-id="vkontakte"> <a href="https://vk.com/share.php?url=https%3A%2F%2Fqipu.ru%2Fmts%2Fispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" class="bsb-link" target="_blank" data-wpel-link="internal"> <i class="bsb-icon icon icon-vkontakte"></i> <span class="bsb-label">Like</span> </a> </div> <div class="bsb-item bsb-fb-messenger bsb-no-count" data-id="fb-messenger"> <a href="fb-messenger://share/?link=https%3A%2F%2Fqipu.ru%2Fmts%2Fispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" class="bsb-link" target="_blank" data-wpel-link="internal"> <i class="bsb-icon icon icon-fb-messenger"></i> <span class="bsb-label">Like</span> </a> </div> </div> </div> </div> </div> </article> <section class="section-carousel"> <div class="post-carousel"> <h3 class="title-block">Читайте также</h3> <div class="owl-container owl-loop" data-columns="3" data-padding="20"> <div class="owl-carousel"> <article class="post-29365 post type-post status-publish format-standard has-post-thumbnail hentry category-morning-news"> <div class="post-thumbnail"> <img width="720" height="378" src="/uploads/67b0d745843045ce4f3948610e7ee13b.jpg" class="attachment-md size-md wp-post-image" alt="Создание расширенного скриншота" / loading=lazy> <div class="post-more"><a href="/mobile-tips/ne-rabotaet-skrinshot-tremya-palcami-na-xiaomi-sozdanie.html" class="btn-link" data-wpel-link="internal"><span>Читать</span></a></div> <ul class="post-meta"></ul> <a href="/mobile-tips/ne-rabotaet-skrinshot-tremya-palcami-na-xiaomi-sozdanie.html" data-wpel-link="internal"></a> </div> <h2 class="entry-title"><a href="/mobile-tips/ne-rabotaet-skrinshot-tremya-palcami-na-xiaomi-sozdanie.html" data-wpel-link="internal">Создание расширенного скриншота</a></h2> <ul class="post-meta"> <li class="meta-date"> <time class="entry-date published updated" datetime=""> 2024-05-14 02:48:06 </time> </li> </ul> </article> <!-- /next_post --> <article class="post-29364 post type-post status-publish format-standard has-post-thumbnail hentry category-morning-news"> <div class="post-thumbnail"> <img width="720" height="378" src="/uploads/955dffcccfaf05e0aa0e4b0352283653.jpg" class="attachment-md size-md wp-post-image" alt="Как работает импорт и экспорт закладок Firefox и как перенести закладки в Firefox из другого браузера" / loading=lazy> <div class="post-more"><a href="/mts/eksport-nastroek-firefox-kak-rabotaet-import-i-eksport-zakladok-firefox-i-kak.html" class="btn-link" data-wpel-link="internal"><span>Читать</span></a></div> <ul class="post-meta"></ul> <a href="/mts/eksport-nastroek-firefox-kak-rabotaet-import-i-eksport-zakladok-firefox-i-kak.html" data-wpel-link="internal"></a> </div> <h2 class="entry-title"><a href="/mts/eksport-nastroek-firefox-kak-rabotaet-import-i-eksport-zakladok-firefox-i-kak.html" data-wpel-link="internal">Как работает импорт и экспорт закладок Firefox и как перенести закладки в Firefox из другого браузера</a></h2> <ul class="post-meta"> <li class="meta-date"> <time class="entry-date published updated" datetime=""> 2024-05-14 02:48:06 </time> </li> </ul> </article> <!-- /next_post --> <article class="post-29363 post type-post status-publish format-standard has-post-thumbnail hentry category-morning-news"> <div class="post-thumbnail"> <img width="720" height="378" src="/uploads/1f94450bd0150ac4eb554ef85efa12d2.jpg" class="attachment-md size-md wp-post-image" alt="Конвертер в fb2 — бесплатная программа htmlDocs2fb2 Как из ворда перевести в fb2" / loading=lazy> <div class="post-more"><a href="/beeline/onlain-konvertery-fb2-konverter-v-fb2-besplatnaya-programma-htmldocs2fb2-kak-iz.html" class="btn-link" data-wpel-link="internal"><span>Читать</span></a></div> <ul class="post-meta"></ul> <a href="/beeline/onlain-konvertery-fb2-konverter-v-fb2-besplatnaya-programma-htmldocs2fb2-kak-iz.html" data-wpel-link="internal"></a> </div> <h2 class="entry-title"><a href="/beeline/onlain-konvertery-fb2-konverter-v-fb2-besplatnaya-programma-htmldocs2fb2-kak-iz.html" data-wpel-link="internal">Конвертер в fb2 — бесплатная программа htmlDocs2fb2 Как из ворда перевести в fb2</a></h2> <ul class="post-meta"> <li class="meta-date"> <time class="entry-date published updated" datetime=""> 2024-05-13 02:48:20 </time> </li> </ul> </article> <!-- /next_post --> </div> <div class="owl-dots"></div> </div> </div> </section> </main> </div> </div> </div> </div> <footer class="site-footer"> <style> .footer-section { width: 830px; margin: 0 auto; padding: 0; font-family: 'Noto Sans', sans-serif; } .site-footer { background-color: #222; } @media (max-width: 1025px) { .footer-section { max-width: 690px; } } @media (max-width: 760px) { .footer-section { width: 90%; } } .site-footer a:hover { color: #FF3400; } .white-a { color: #969696!important; } .white-a:hover { color: #fff!important; } .white-a:hover g { opacity: 1; } </style> <div class="footer-section"> <div class="footer" > <div class="footer__menu"> <div class="footer__menu__logo" style="background:none;"><img src="/logo/logo.png" loading=lazy></div> <ul> <li><a href="/category/megaphone/">Мегафон</a></li> <li><a href="/category/mts/">Мтс</a></li> <li><a href="/category/beeline/">Билайн</a></li> <li><a href="/category/tele2/">ТЕЛЕ2</a></li> </ul> <ul> <li><a href="/category/megaphone/">Мегафон</a></li> <li><a href="/category/mts/">Мтс</a></li> <li><a href="/category/beeline/">Билайн</a></li> <li><a href="/category/tele2/">ТЕЛЕ2</a></li> </ul> </div> <div class="footer__social"> <a href="https://www.facebook.com/sharer/sharer.php?u=https://qipu.ru/mts/ispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" target="_blank" class="footer__social--facebook white-a" data-wpel-link="external"></a> <a href="https://www.twitter.com/share?url=https://qipu.ru/mts/ispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" target="_blank" class="footer__social--tw white-a" data-wpel-link="external"></a> <a href="https://vk.com/share.php?url=https://qipu.ru/mts/ispolzovanie-xml-v-srede-delphi-ispolzovanie-xml-document-object-model.html" target="_blank" class="footer__social--vk white-a" data-wpel-link="external"></a> <a href="https://youtube.com/" target="_blank" class="footer__social--yt white-a" data-wpel-link="external"></a> </div> <div class="footer__info" > <p>© qipu.ru, 2024. Персональный помощник в мире сотовой связи <br> .</p> </div> </div> </div> </footer> </div> </div> <a href="#top" class="scroll-to-top hidden-sm-down" data-wpel-link="internal"></a> <div class="site-search" id="search"> <button type="button" class="close"></button> <div class="form-container"> <div class="container"> <div class="row"> <div class="col-lg-6 offset-lg-3"> <form role="search" method="get" class="search-form form" action="/"> <label class="sr-only">Search for:</label> <div class="input-group"> <input type="search" value="" name="s" class="search-field form-control" placeholder="Что вы ищете?" required> <span class="input-group-btn"> <button type="submit" class="search-submit btn btn-primary btn-effect"><span>Search</span><span><i class="icon icon-search"></i></span></button> </span> </div> </form> <p>Введите ключевые слова.</p> </div> </div> </div> </div> </div> <style type="text/css"> .wpmchimpa-overlay-bg.wpmchimpselector { display: none; top: 0; left: 0; height:100%; width: 100%; cursor: pointer; z-index: 999999; background: #000; background: rgba(0,0,0,0.40); background:rgba(0,0,0,0.7);cursor: default; position: fixed!important; } .wpmchimpa-overlay-bg #wpmchimpa-main *{ transition: all 0.5s ease; } .wpmchimpa-overlay-bg .wpmchimpa-mainc, .wpmchimpa-overlay-bg .wpmchimpa-maina{ -webkit-transform: translate(0,0); height:100%;} .wpmchimpa-overlay-bg #wpmchimpa-main { position: absolute; top: 50%; left: 50%; border-radius: 2px; -webkit-transform: translate(-50%, -50%); -moz-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); -o-transform: translate(-50%, -50%); transform: translate(-50%, -50%); width: calc(100% - 20px); max-width:650px; background: #27313B; text-align: center; background-color:#ffffff;} #wpmchimpa-main #wpmchimpa-newsletterform{ } #wpmchimpa-main #wpmchimpa{ width: calc(100% - 20px); max-width: 400px; margin: 0 auto; } #wpmchimpa div{ position:relative; } #wpmchimpa h3{ line-height: 24px; margin-top:20px; color: #F4233C; font-size: 24px; font-family:Tahoma, Geneva, sans-serif;font-size:35px;line-height:35px;font-weight:normal;font-style:normal;color:#f34b38;} #wpmchimpa .wpmchimpa_para{ margin-top: 15px; } #wpmchimpa .wpmchimpa_para,#wpmchimpa .wpmchimpa_para * { font-size: 15px; color: #959595; font-family:Tahoma, Geneva, sans-serif;font-size:15px;} #wpmchimpa form{ margin: 20px auto; } #wpmchimpa .formbox > div:first-of-type{ width: 65%; float: left; } #wpmchimpa .formbox > div:first-of-type + div{ width: 35%; float: left; text-align: center; } #wpmchimpa .formbox input[type="text"]{ border-radius: 3px 0 0 3px; } #wpmchimpa .wpmchimpa-field{ position: relative; width:100%; margin: 0 auto 10px auto; text-align: left; } #wpmchimpa .inputicon{ display: none; } #wpmchimpa .wpmc-ficon .inputicon { display: block; width: 40px; height: 40px; position: absolute; top: 0; left: 0; pointer-events: none; } #wpmchimpa .wpmc-ficon input[type="text"], #wpmchimpa .wpmc-ficon input[type="text"] ~ .inputlabel{ padding-left: 40px; } #wpmchimpa .wpmc-ficon [wpmcfield="email"] ~ .inputicon { background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMzZDNkM2QiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTUxMiwzODRjMCwxMS4yMTktMy4xNTYsMjEuNjI1LTguMjE5LDMwLjc4MUwzNDIuMTI1LDIzMy45MDZMNTAyLjAzMSw5NGM2LjIxOSw5Ljg3NSw5Ljk2OSwyMS40NjksOS45NjksMzRWMzg0eiBNMjU2LDI2Ni43NUw0NzguNSw3Mi4wNjNjLTkuMTI1LTUtMTkuNDA2LTguMDYzLTMwLjUtOC4wNjNINjRjLTExLjEwOSwwLTIxLjM5MSwzLjA2My0zMC40ODQsOC4wNjNMMjU2LDI2Ni43NXogTTMxOC4wMzEsMjU0Ljk2OSBsLTUxLjUsNDUuMDk0QzI2My41MTYsMzAyLjY4OCwyNTkuNzY2LDMwNCwyNTYsMzA0cy03LjUxNi0xLjMxMy0xMC41MzEtMy45MzhsLTUxLjUxNi00NS4wOTRMMzAuMjUsNDM4LjE1NiBDNDAuMDYzLDQ0NC4zMTMsNTEuNTYzLDQ0OCw2NCw0NDhoMzg0YzEyLjQzOCwwLDIzLjkzOC0zLjY4OCwzMy43NS05Ljg0NEwzMTguMDMxLDI1NC45Njl6IE05Ljk2OSw5NEMzLjc1LDEwMy44NzUsMCwxMTUuNDY5LDAsMTI4IHYyNTZjMCwxMS4yMTksMy4xNDEsMjEuNjI1LDguMjE5LDMwLjc4MWwxNjEuNjQxLTE4MC45MDZMOS45NjksOTR6Ii8+PC9zdmc+') no-repeat center} #wpmchimpa .wpmc-ficon [wpmcfield="FNAME"] ~ .inputicon { background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMzZDNkM2QiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNjEyIDYxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjEyIDYxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PHBhdGggZD0iTTMwNi4wMDEsMzI1Ljk4OGM5MC41NjMtMC4wMDUsMTIzLjE0Ny05MC42ODIsMTMxLjY3OS0xNjUuMTY3QzQ0OC4xODgsNjkuMDYsNDA0Ljc5OSwwLDMwNi4wMDEsMCBjLTk4Ljc4MiwwLTE0Mi4xOTUsNjkuMDU1LTEzMS42NzksMTYwLjgyQzE4Mi44NjIsMjM1LjMwNCwyMTUuNDM2LDMyNS45OTUsMzA2LjAwMSwzMjUuOTg4eiIvPjxwYXRoIGQ9Ik01NTAuOTgxLDU0MS45MDhjLTAuOTktMjguOTA0LTQuMzc3LTU3LjkzOS05LjQyMS04Ni4zOTNjLTYuMTExLTM0LjQ2OS0xMy44ODktODUuMDAyLTQzLjk4My0xMDcuNDY1IGMtMTcuNDA0LTEyLjk4OC0zOS45NDEtMTcuMjQ5LTU5Ljg2NS0yNS4wODFjLTkuNjk3LTMuODEtMTguMzg0LTcuNTk0LTI2LjUzNy0xMS45MDFjLTI3LjUxOCwzMC4xNzYtNjMuNCw0NS45NjItMTA1LjE4Niw0NS45NjQgYy00MS43NzQsMC03Ny42NTItMTUuNzg2LTEwNS4xNjctNDUuOTY0Yy04LjE1Myw0LjMwOC0xNi44NCw4LjA5My0yNi41MzcsMTEuOTAxYy0xOS45MjQsNy44MzItNDIuNDYxLDEyLjA5Mi01OS44NjMsMjUuMDgxIGMtMzAuMDk2LDIyLjQ2My0zNy44NzMsNzIuOTk2LTQzLjk4MywxMDcuNDY1Yy01LjA0NSwyOC40NTQtOC40MzMsNTcuNDg5LTkuNDIyLDg2LjM5MwljLTAuNzY2LDIyLjM4NywxMC4yODgsMjUuNTI1LDI5LjAxNywzMi4yODRjMjMuNDUzLDguNDU4LDQ3LjY2NiwxNC43MzcsNzIuMDQxLDE5Ljg4NGM0Ny4wNzcsOS45NDEsOTUuNjAzLDE3LjU4MiwxNDMuOTIxLDE3LjkyNCBjNDguMzE4LTAuMzQzLDk2Ljg0NC03Ljk4MywxNDMuOTIxLTE3LjkyNGMyNC4zNzUtNS4xNDUsNDguNTktMTEuNDI0LDcyLjA0MS0xOS44ODQJQzU0MC42OTQsNTY3LjQzNSw1NTEuNzQ3LDU2NC4yOTcsNTUwLjk4MSw1NDEuOTA4eiIvPjwvZz48L3N2Zz4=') no-repeat center} #wpmchimpa .wpmc-ficon [wpmcfield="LNAME"] ~ .inputicon { background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMzZDNkM2QiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNjEyIDYxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjEyIDYxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PHBhdGggZD0iTTMwNi4wMDEsMzI1Ljk4OGM5MC41NjMtMC4wMDUsMTIzLjE0Ny05MC42ODIsMTMxLjY3OS0xNjUuMTY3QzQ0OC4xODgsNjkuMDYsNDA0Ljc5OSwwLDMwNi4wMDEsMCBjLTk4Ljc4MiwwLTE0Mi4xOTUsNjkuMDU1LTEzMS42NzksMTYwLjgyQzE4Mi44NjIsMjM1LjMwNCwyMTUuNDM2LDMyNS45OTUsMzA2LjAwMSwzMjUuOTg4eiIvPjxwYXRoIGQ9Ik01NTAuOTgxLDU0MS45MDhjLTAuOTktMjguOTA0LTQuMzc3LTU3LjkzOS05LjQyMS04Ni4zOTNjLTYuMTExLTM0LjQ2OS0xMy44ODktODUuMDAyLTQzLjk4My0xMDcuNDY1IGMtMTcuNDA0LTEyLjk4OC0zOS45NDEtMTcuMjQ5LTU5Ljg2NS0yNS4wODFjLTkuNjk3LTMuODEtMTguMzg0LTcuNTk0LTI2LjUzNy0xMS45MDFjLTI3LjUxOCwzMC4xNzYtNjMuNCw0NS45NjItMTA1LjE4Niw0NS45NjQgYy00MS43NzQsMC03Ny42NTItMTUuNzg2LTEwNS4xNjctNDUuOTY0Yy04LjE1Myw0LjMwOC0xNi44NCw4LjA5My0yNi41MzcsMTEuOTAxYy0xOS45MjQsNy44MzItNDIuNDYxLDEyLjA5Mi01OS44NjMsMjUuMDgxIGMtMzAuMDk2LDIyLjQ2My0zNy44NzMsNzIuOTk2LTQzLjk4MywxMDcuNDY1Yy01LjA0NSwyOC40NTQtOC40MzMsNTcuNDg5LTkuNDIyLDg2LjM5MwljLTAuNzY2LDIyLjM4NywxMC4yODgsMjUuNTI1LDI5LjAxNywzMi4yODRjMjMuNDUzLDguNDU4LDQ3LjY2NiwxNC43MzcsNzIuMDQxLDE5Ljg4NGM0Ny4wNzcsOS45NDEsOTUuNjAzLDE3LjU4MiwxNDMuOTIxLDE3LjkyNCBjNDguMzE4LTAuMzQzLDk2Ljg0NC03Ljk4MywxNDMuOTIxLTE3LjkyNGMyNC4zNzUtNS4xNDUsNDguNTktMTEuNDI0LDcyLjA0MS0xOS44ODQJQzU0MC42OTQsNTY3LjQzNSw1NTEuNzQ3LDU2NC4yOTcsNTUwLjk4MSw1NDEuOTA4eiIvPjwvZz48L3N2Zz4=') no-repeat center} #wpmchimpa .wpmchimpa-field textarea, #wpmchimpa .wpmchimpa-field select, #wpmchimpa input[type="text"]{ text-align: left; width: 100%; height: 40px; border-radius:3px; background: #fff; padding: 0 10px; color: #353535; font-size:17px; outline:0; display: block; border: 1px solid #efefef; font-family:Tahoma, Geneva, sans-serif;font-size:15px;color:#696565;} #wpmchimpa .wpmchimpa-field.wpmchimpa-multidrop select{ height: 100px; } #wpmchimpa .wpmchimpa-field.wpmchimpa-drop:before{ content: ''; width: 40px; height: 40px; position: absolute; right: 0; top: 0; pointer-events: none; background: no-repeat center; background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMwMDAiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTZweCIgaGVpZ2h0PSIxNnB4IiB2aWV3Qm94PSIwIDAgOTQuODU5IDk0Ljg1OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgOTQuODU5IDk0Ljg1OSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTE3LjMxMiw0MS43NTdoNjAuMjM2YzAuNzcsMCwxLjQ2OS0wLjQ0LDEuODAyLTEuMTMzYzAuMzM0LTAuNjkxLDAuMjQxLTEuNTE0LTAuMjM3LTIuMTE0TDQ4Ljk5NCwwLjc1MyBDNDguNjE0LDAuMjc3LDQ4LjAzOSwwLDQ3LjQzMSwwcy0xLjE4NCwwLjI3Ny0xLjU2MywwLjc1M0wxNS43NDgsMzguNTFjLTAuNDc5LDAuNjAxLTAuNTcyLDEuNDIzLTAuMjM4LDIuMTE0IEMxNS44NDMsNDEuMzE3LDE2LjU0Miw0MS43NTcsMTcuMzEyLDQxLjc1N3oiLz4gPHBhdGggZD0iTTc3LjU0Nyw1My4xMDJIMTcuMzEyYy0wLjc2OSwwLTEuNDY5LDAuNDM5LTEuODAyLDEuMTMzYy0wLjMzNCwwLjY5MS0wLjI0MSwxLjUxNCwwLjIzOCwyLjExM2wzMC4xMTksMzcuNzU4IGMwLjM4LDAuNDc2LDAuOTU1LDAuNzUzLDEuNTYzLDAuNzUzczEuMTg0LTAuMjc3LDEuNTYzLTAuNzUzTDc5LjExLDU2LjM0OGMwLjQ3OS0wLjYwMSwwLjU3MS0xLjQyMiwwLjIzNy0yLjExMyBDNzkuMDE2LDUzLjU0MSw3OC4zMTcsNTMuMTAyLDc3LjU0Nyw1My4xMDJ6Ii8+PC9zdmc+'); } #wpmchimpa input[type="text"] ~ .inputlabel{ position: absolute; top: 0; left: 0; right: 0; pointer-events: none; width: 100%; line-height: 40px; color: rgba(0,0,0,0.6); font-size: 17px; font-weight:500; padding: 0 10px; white-space: nowrap; font-family:Tahoma, Geneva, sans-serif;font-size:15px;color:#696565;} #wpmchimpa input[type="text"]:valid + .inputlabel{ display: none; } #wpmchimpa select.wpmcerror, #wpmchimpa input[type="text"].wpmcerror{ border-color: red; } #wpmchimpa .wpmchimpa-check, #wpmchimpa .wpmchimpa-radio{ clear: both; } #wpmchimpa .wpmchimpa-check *, #wpmchimpa .wpmchimpa-radio *{ color: #fff; font-family:Tahoma, Geneva, sans-serif;font-size:14px;font-weight:normal;font-style:normal;color:#3d3d3d;} #wpmchimpa .wpmchimpa-item{ width:100%; display: inline-block; vertical-align: top; } #wpmchimpa .wpmchimpa-item input { display: none; } #wpmchimpa .wpmchimpa-item span { cursor: pointer; display: inline-block; position: relative; padding-left: 35px; line-height: 20px; margin-right: 10px; } #wpmchimpa .wpmchimpa-item span:before, #wpmchimpa .wpmchimpa-item span:after { content: ''; display: inline-block; width: 12px; height: 12px; left: 0; top: 4px; position: absolute; } #wpmchimpa .wpmchimpa-item span:before { border:1px solid #ccc; border-radius: 1px; background-color: #fff; -webkit-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out; border: 1px solid#f34b38;background: #ffffff;} #wpmchimpa .wpmchimpa-item input[type='checkbox'] + span:hover:after, #wpmchimpa input[type='checkbox']:checked + span:after { content:''; width: 14px; height: 14px; background: no-repeat center; background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMwMDAwMDAiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iOHB4IiBoZWlnaHQ9IjhweCIgdmlld0JveD0iMCAwIDIzMCAyMzAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDIzMCAyMzAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik05Mi4zNTYsMjIzLjU0OWM3LjQxLDcuNSwyMy45MTQsNS4wMTQsMjUuNjkxLTYuNzc5YzExLjA1Ni03My4yMTcsNjYuMzc4LTEzNC45ODUsMTA4LjI0My0xOTMuMTg5IEMyMzcuODk4LDcuNDUyLDIxMS4yMDctNy44NywxOTkuNzUsOC4wNjdDMTYxLjQ5Myw2MS4yNDksMTEzLjI3NCwxMTcuMjEsOTQuNDEsMTgxLjc0NCBjLTIxLjU1Ny0yMi4wMzEtNDMuMjAxLTQzLjg1My02Ny4zNzktNjMuMjEyYy0xNS4zMTItMTIuMjY1LTM3LjIxNSw5LjM0My0yMS43MzgsMjEuNzM3IEMzNi43OTQsMTY1LjUwMSw2NC4wMTcsMTk0LjkyNCw5Mi4zNTYsMjIzLjU0OXoiLz48L3N2Zz4='); } #wpmchimpa .wpmchimpa-item input[type='checkbox']:not(:checked) + span:hover:after { opacity: 0.5; } #wpmchimpa .wpmchimpa-item input[type='radio'] + span:before { border-radius: 50%; width: 12px; height: 12px; top: 4px; } #wpmchimpa input[type='radio']:checked + span:after { background: #000000; width: 8px; height: 8px; top: 6px; left: 2px; border-radius: 50%; } #wpmchimpa .wpmcinfierr{ display: block; height: 10px; text-align: left; line-height: 10px; margin-bottom: -10px; font-size: 10px; color: red; pointer-events: none; font-family:Tahoma, Geneva, sans-serif;} #wpmchimpa .wpmchimpa-subs-button{ border-radius: 0 3px 3px 0; width: 100%; color: #fff; font-size: 17px; border: 1px solid #FA0B38; background-color: #FF1F43; height: 40px; line-height: 40px; text-align: center; cursor: pointer; position: relative; top: 0; font-family:Tahoma, Geneva, sans-serif;font-size:16px;font-weight:normal;font-style:normal;color:#ffffff;background-color:#f34b38;} #wpmchimpa .wpmchimpa-subs-button::before{ content: 'Подписаться'; } #wpmchimpa .wpmchimpa-subs-button:hover{ background-color: #FA0B38; color:#ffffff;background-color:#75c462;} #wpmchimpa .wpmchimpa-subs-button.subsicon:before{ padding-left: 40px; } #wpmchimpa .wpmchimpa-subs-button.subsicon::after{ content:''; position: absolute; height: 40px; width: 40px; top: 0; left: 0; pointer-events: none; } .wpmchimpa-overlay-bg.signalshow .wpmchimpa-subs-button::after, .wpmchimpa-overlay-bg.signalshow .wpmchimpa-subs-button::before{ display: none; } #wpmchimpa-main .wpmchimpa-signal { display: none; z-index: 1; top: 5px; left: calc(50% - 20px); position: absolute; -webkit-transform: scale(0.8); -ms-transform: scale(0.8); transform: scale(0.8); } .wpmchimpa-overlay-bg.signalshow #wpmchimpa-main .wpmchimpa-signal { display: inline-block; } #wpmchimpa-main .wpmchimpa-feedback{ text-align: center; position: relative; color: #ccc; font-size: 10px; height: 12px; margin-top: -12px; font-family:Tahoma, Geneva, sans-serif;color:#3d3d3d;} #wpmchimpa-main .wpmchimpa-tag{ margin: 5px auto; } #wpmchimpa-main .wpmchimpa-tag, #wpmchimpa-main .wpmchimpa-tag *{ color:#fff; font-size: 10px; font-family:Tahoma, Geneva, sans-serif;font-size:10px;color:#3d3d3d;} #wpmchimpa-main .wpmchimpa-tag:before{ content:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMzZDNkM2QiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTBweCIgaGVpZ2h0PSIxMHB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTQxNy41NjYsMjA5LjgzaC05LjQ4NHYtNDQuMzg4YzAtODIuMDk5LTY1LjE1MS0xNTAuNjgxLTE0Ni41ODItMTUyLjE0NWMtMi4yMjQtMC4wNC02LjY3MS0wLjA0LTguODk1LDAgYy04MS40MzIsMS40NjQtMTQ2LjU4Miw3MC4wNDYtMTQ2LjU4MiwxNTIuMTQ1djQ0LjM4OGgtOS40ODVDODEuOTIyLDIwOS44Myw3MCwyMjQuOTEyLDcwLDI0My41Mzl2MjIyLjYzMiAgQzcwLDQ4NC43NzcsODEuOTIyLDUwMCw5Ni41MzksNTAwaDMyMS4wMjhjMTQuNjE3LDAsMjYuNTM5LTE1LjIyMywyNi41MzktMzMuODI5VjI0My41MzkgIEM0NDQuMTA1LDIyNC45MTIsNDMyLjE4NCwyMDkuODMsNDE3LjU2NiwyMDkuODN6IE0yODcuMTI5LDM1NC42Mjl2NjcuMjdjMCw3LjcwNC02LjQ0OSwxNC4yMjItMTQuMTU5LDE0LjIyMmgtMzEuODM0ICBjLTcuNzEsMC0xNC4xNTktNi41MTgtMTQuMTU5LTE0LjIyMnYtNjcuMjdjLTcuNDc3LTcuMzYxLTExLjgzLTE3LjUzNy0xMS44My0yOC43OTVjMC0yMS4zMzQsMTYuNDkxLTM5LjY2NiwzNy40NTktNDAuNTEyICBjMi4yMjItMC4wOSw2LjY3My0wLjA5LDguODk1LDBjMjAuOTY4LDAuODQ2LDM3LjQ1OSwxOS4xNzgsMzcuNDU5LDQwLjUxMkMyOTguOTU5LDMzNy4wOTIsMjk0LjYwNSwzNDcuMjY4LDI4Ny4xMjksMzU0LjYyOXogTTM0NS41NzIsMjA5LjgzSDI2MS41aC04Ljg5NWgtODQuMDcydi00NC4zODhjMC00OC45MDUsMzkuNzQ0LTg5LjM0Miw4OC41MTktODkuMzQyczg4LjUyLDQwLjQzNyw4OC41Miw4OS4zNDJWMjA5LjgzeiIvPjwvc3ZnPg=='); margin: 5px; top: 1px; position:relative; } #wpmchimpa-main .wpmchimpa-social{ display: inline-block; margin: 12px auto 0; height: 90px; width: 100%; background: rgba(75, 75, 75, 0.3); box-shadow: 0px 1px 1px 1px rgba(116, 116, 116, 0.94); } #wpmchimpa-main .wpmchimpa-social::before{ content: 'Subscribe with'; font-size: 13px; color: #ADACB2; width: 100%; display: block; margin: 15px auto 5px; } #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc{ display: inline-block; width:40px; height: 40px; border-radius: 2px; cursor: pointer; -webkit-transition: all 0.1s ease; transition: all 0.1s ease; -webkit-backface-visibility:hidden; border:1px solid #262E43; border-color: #ffffff;} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc::before{ content: ''; display: block; width:40px; height: 40px; background: no-repeat center; } #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-fb { display:none;} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-fb::before { background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI5Ni4yOTYsNTEySDIwMC4zNlYyNTZoLTY0di04OC4yMjVsNjQtMC4wMjlsLTAuMTA0LTUxLjk3NkMyMDAuMjU2LDQzLjc5NCwyMTkuNzczLDAsMzA0LjU1NiwwaDcwLjU4OHY4OC4yNDJoLTQ0LjExNSBjLTMzLjAxNiwwLTM0LjYwNCwxMi4zMjgtMzQuNjA0LDM1LjM0MmwtMC4xMzEsNDQuMTYyaDc5LjM0NmwtOS4zNTQsODguMjI1TDI5Ni4zNiwyNTZMMjk2LjI5Niw1MTJ6Ii8+PC9zdmc+')} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-fb:hover:before { background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMyZDYwOWIiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI5Ni4yOTYsNTEySDIwMC4zNlYyNTZoLTY0di04OC4yMjVsNjQtMC4wMjlsLTAuMTA0LTUxLjk3NkMyMDAuMjU2LDQzLjc5NCwyMTkuNzczLDAsMzA0LjU1NiwwaDcwLjU4OHY4OC4yNDJoLTQ0LjExNSBjLTMzLjAxNiwwLTM0LjYwNCwxMi4zMjgtMzQuNjA0LDM1LjM0MmwtMC4xMzEsNDQuMTYyaDc5LjM0NmwtOS4zNTQsODguMjI1TDI5Ni4zNiwyNTZMMjk2LjI5Niw1MTJ6Ii8+PC9zdmc+')} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-gp { display:none;} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-gp:before { background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI3My4zNzIsMzAyLjQ5OGMtNS4wNDEtNi43NjItMTAuNjA4LTEzLjA0NS0xNi43LTE4Ljg0MmMtNi4wOTEtNS44MDQtMTIuMTgzLTExLjA4OC0xOC4yNzEtMTUuODQ1IGMtNi4wOTItNC43NTctMTEuNjU5LTkuMzI5LTE2LjcwMi0xMy43MDljLTUuMDQyLTQuMzc0LTkuMTM1LTguOTQ1LTEyLjI3NS0xMy43MDJjLTMuMTQtNC43NTctNC43MTEtOS42MS00LjcxMS0xNC41NTggYzAtNi44NTUsMi4xOS0xMy4yNzgsNi41NjctMTkuMjc0YzQuMzc3LTUuOTk2LDkuNzA3LTExLjc5OSwxNS45ODYtMTcuNDE3YzYuMjgtNS42MTcsMTIuNTU5LTExLjc1MywxOC44NDQtMTguNDE1IGM2LjI3Ni02LjY2NSwxMS42MDQtMTUuNDY1LDE1Ljk4NS0yNi40MTJjNC4zNzMtMTAuOTQ0LDYuNTYzLTIzLjQ1OCw2LjU2My0zNy41NDJjMC0xNi43NS0zLjcxMy0zMi44MzUtMTEuMTM2LTQ4LjI1IGMtNy40MjMtMTUuNDE4LTE3Ljg5LTI3LjQxMi0zMS40MDUtMzUuOTc2aDM4LjU0TDMwMy4yLDBIMTc4LjQ0MWMtMTcuNjk5LDAtMzUuNDk4LDEuOTA2LTUzLjM4NCw1LjcyIGMtMjYuNDUzLDUuOS00OC43MjMsMTkuMzY4LTY2LjgwNiw0MC4zOTdDNDAuMTcxLDY3LjE1LDMxLjEyOSw5MC45OSwzMS4xMjksMTE3LjYzN2MwLDI4LjE3MSwxMC4xMzgsNTEuNTgzLDMwLjQwNiw3MC4yMzMgYzIwLjI2OSwxOC42NDksNDQuNTg1LDI3Ljk3OCw3Mi45NDUsMjcuOTc4YzUuNzEsMCwxMi4zNzEtMC40NzgsMTkuOTg1LTEuNDI3Yy0wLjM4MSwxLjUyMS0xLjA0MywzLjU2Ny0xLjk5Nyw2LjEzNiBzLTEuNzE1LDQuNjItMi4yODYsNi4xNGMtMC41NywxLjUyMS0xLjA0NywzLjM3NS0xLjQyNSw1LjU2NmMtMC4zODIsMi4xOS0wLjU3MSw0LjQyOC0wLjU3MSw2LjcxIGMwLDEyLjU2Myw2LjA4NiwyNi43NDQsMTguMjcxLDQyLjU0MWMtMTQuNDY1LDAuMzg3LTI4LjczNywxLjY3LTQyLjgyNSwzLjg2Yy0xNC4wODQsMi4xOS0yOC44MzMsNS42MTYtNDQuMjUyLDEwLjI4IGMtMTUuNDE3LDQuNjYxLTI5LjIxNywxMS40Mi00MS4zOTYsMjAuMjdjLTEyLjE4Miw4Ljg1NC0yMS4zMTcsMTkuMzY2LTI3LjQwOCwzMS41NDlDMy41MzMsMzYxLjU1OSwwLjAxLDM3NC40MDUsMC4wMSwzODYuMDE3IGMwLDEyLjc1MSwyLjg1NywyNC4zMTQsOC41NjUsMzQuNjljNS43MDgsMTAuMzY5LDEzLjAzNSwxOC44NDIsMjEuOTgyLDI1LjQwNmM4Ljk0NSw2LjU3LDE5LjI3MywxMi4wODMsMzAuOTc4LDE2LjU2MiBjMTEuNzA0LDQuNDcsMjMuMzE1LDcuNjU5LDM0LjgyOSw5LjU2MmMxMS41MTYsMS45MDMsMjIuODg4LDIuODU0LDM0LjExOSwyLjg1NGM1MS4wMDcsMCw5MC45ODEtMTIuNDY0LDExOS45MDktMzcuMzk3IGMyNi42NDgtMjMuMjIzLDM5Ljk3MS01MC4wNjIsMzkuOTcxLTgwLjUxN2MwLTEwLjg1NS0xLjU3LTIwLjk4NC00LjcxMi0zMC40MDlDMjgyLjUxLDMxNy4zMzcsMjc4LjQyLDMwOS4yNTQsMjczLjM3MiwzMDIuNDk4eiBNMTYzLjMxMSwxOTguNzIyYy05LjcwNywwLTE4LjkzNy0yLjQ3NS0yNy42OTQtNy40MjZjLTguNzU3LTQuOTUtMTYuMTgtMTEuMzc0LTIyLjI3LTE5LjI3MyBjLTYuMDg4LTcuODk4LTExLjQxOC0xNi43OTYtMTUuOTg3LTI2LjY5NWMtNC41NjctOS44OTYtNy45NDQtMTkuNzkyLTEwLjEzNS0yOS42OTJjLTIuMTktOS44OTUtMy4yODQtMTkuMzE4LTMuMjg0LTI4LjI2NSBjMC0xOC4yNzEsNC44NTQtMzMuOTc0LDE0LjU2Mi00Ny4xMDhjOS43MDUtMTMuMTM0LDIzLjQxMS0xOS43MDEsNDEuMTEyLTE5LjcwMWMxMi41NjMsMCwyMy45MzUsMy44OTksMzQuMTE4LDExLjcwNCBjMTAuMTgzLDcuODA0LDE4LjE3NywxNy43MDEsMjMuOTg0LDI5LjY5MmM1LjgwMiwxMS45OTEsMTAuMjc3LDI0LjQwNywxMy40MTcsMzcuMjU3YzMuMTQsMTIuODQ3LDQuNzExLDI0Ljk4Myw0LjcxMSwzNi40MDMgYzAsMTkuMDM2LTQuMTM5LDM0LjMxNy0xMi40MTksNDUuODMzQzE5NS4xNDQsMTkyLjk2NCwxODEuNzc1LDE5OC43MjIsMTYzLjMxMSwxOTguNzIyeiBNMjQyLjI1MSw0MTMuMTIzIGMtNS4yMyw4Ljk0OS0xMi4zMTksMTUuOTQtMjEuMjY3LDIwLjk4MWMtOC45NDYsNS4wNDgtMTguNTA5LDguNzU4LTI4LjY5MywxMS4xNGMtMTAuMTgzLDIuMzg1LTIwLjg4OSwzLjU3Mi0zMi4xMiwzLjU3MiBjLTEyLjE4MiwwLTI0LjI3LTEuNDMxLTM2LjI1OC00LjI4NGMtMTEuOTktMi44NTEtMjMuNDU5LTcuMTg3LTM0LjQwMy0xMi45OTFjLTEwLjk0NC01LjgtMTkuNzk1LTEzLjc5OC0yNi41NTEtMjMuOTgyIGMtNi43NTctMTAuMTg0LTEwLjEzNS0yMS43NDQtMTAuMTM1LTM0LjY5YzAtMTEuNDE5LDIuNTY4LTIxLjYwMSw3LjcwOC0zMC41NWM1LjE0Mi04Ljk0NSwxMS43MDktMTYuMDg0LDE5LjcwMi0yMS40MDggYzcuOTk0LTUuMzMyLDE3LjMxOS05LjcxMywyNy45NzktMTMuMTMxYzEwLjY2LTMuNDMzLDIwLjkzNy01LjgwOCwzMC44MzMtNy4xMzljOS44OTUtMS4zMzUsMTkuOTg1LTEuOTk1LDMwLjI2Mi0xLjk5NSBjNi4yODMsMCwxMS4wNDMsMC4xOTEsMTQuMjc3LDAuNTY3YzEuMTQzLDAuNzY3LDQuMDQzLDIuNzU5LDguNzA4LDUuOTk2czcuODA0LDUuNDI4LDkuNDIzLDYuNTcgYzEuNjE1LDEuMTM3LDQuNTY3LDMuMzI2LDguODUsNi41NjNjNC4yODEsMy4yMzcsNy4zMjcsNS42NjEsOS4xMzUsNy4yNzljMS44MDMsMS42MTgsNC40MjEsNC4wNDUsNy44NDksNy4yNzkgYzMuNDI0LDMuMjM3LDUuOTQ4LDYuMDQzLDcuNTY2LDguNDIyYzEuNjE1LDIuMzc4LDMuNjE2LDUuMjgsNS45OTYsOC43MDJjMi4zOCwzLjQzMyw0LjA0Myw2LjcxNSw0Ljk5OCw5Ljg1NSBjMC45NDgsMy4xNDIsMS44NTQsNi41NjcsMi43MDcsMTAuMjc3YzAuODU1LDMuNzIsMS4yODMsNy41NjksMS4yODMsMTEuNTdDMjUwLjEwNSwzOTMuNzEzLDI0Ny40ODcsNDA0LjE4MiwyNDIuMjUxLDQxMy4xMjN6Ii8+IDxwb2x5Z29uIHBvaW50cz0iNDAxLjk5OCw3My4wODkgNDAxLjk5OCwwIDM2NS40NDksMCAzNjUuNDQ5LDczLjA4OSAyOTIuMzU4LDczLjA4OSAyOTIuMzU4LDEwOS42MzYgMzY1LjQ0OSwxMDkuNjM2ICAzNjUuNDQ5LDE4Mi43MjUgNDAxLjk5OCwxODIuNzI1IDQwMS45OTgsMTA5LjYzNiA0NzUuMDgxLDEwOS42MzYgNDc1LjA4MSw3My4wODkiLz48L3N2Zz4=')} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-gp:hover:before { background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNlYjQwMjYiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI3My4zNzIsMzAyLjQ5OGMtNS4wNDEtNi43NjItMTAuNjA4LTEzLjA0NS0xNi43LTE4Ljg0MmMtNi4wOTEtNS44MDQtMTIuMTgzLTExLjA4OC0xOC4yNzEtMTUuODQ1IGMtNi4wOTItNC43NTctMTEuNjU5LTkuMzI5LTE2LjcwMi0xMy43MDljLTUuMDQyLTQuMzc0LTkuMTM1LTguOTQ1LTEyLjI3NS0xMy43MDJjLTMuMTQtNC43NTctNC43MTEtOS42MS00LjcxMS0xNC41NTggYzAtNi44NTUsMi4xOS0xMy4yNzgsNi41NjctMTkuMjc0YzQuMzc3LTUuOTk2LDkuNzA3LTExLjc5OSwxNS45ODYtMTcuNDE3YzYuMjgtNS42MTcsMTIuNTU5LTExLjc1MywxOC44NDQtMTguNDE1IGM2LjI3Ni02LjY2NSwxMS42MDQtMTUuNDY1LDE1Ljk4NS0yNi40MTJjNC4zNzMtMTAuOTQ0LDYuNTYzLTIzLjQ1OCw2LjU2My0zNy41NDJjMC0xNi43NS0zLjcxMy0zMi44MzUtMTEuMTM2LTQ4LjI1IGMtNy40MjMtMTUuNDE4LTE3Ljg5LTI3LjQxMi0zMS40MDUtMzUuOTc2aDM4LjU0TDMwMy4yLDBIMTc4LjQ0MWMtMTcuNjk5LDAtMzUuNDk4LDEuOTA2LTUzLjM4NCw1LjcyIGMtMjYuNDUzLDUuOS00OC43MjMsMTkuMzY4LTY2LjgwNiw0MC4zOTdDNDAuMTcxLDY3LjE1LDMxLjEyOSw5MC45OSwzMS4xMjksMTE3LjYzN2MwLDI4LjE3MSwxMC4xMzgsNTEuNTgzLDMwLjQwNiw3MC4yMzMgYzIwLjI2OSwxOC42NDksNDQuNTg1LDI3Ljk3OCw3Mi45NDUsMjcuOTc4YzUuNzEsMCwxMi4zNzEtMC40NzgsMTkuOTg1LTEuNDI3Yy0wLjM4MSwxLjUyMS0xLjA0MywzLjU2Ny0xLjk5Nyw2LjEzNiBzLTEuNzE1LDQuNjItMi4yODYsNi4xNGMtMC41NywxLjUyMS0xLjA0NywzLjM3NS0xLjQyNSw1LjU2NmMtMC4zODIsMi4xOS0wLjU3MSw0LjQyOC0wLjU3MSw2LjcxIGMwLDEyLjU2Myw2LjA4NiwyNi43NDQsMTguMjcxLDQyLjU0MWMtMTQuNDY1LDAuMzg3LTI4LjczNywxLjY3LTQyLjgyNSwzLjg2Yy0xNC4wODQsMi4xOS0yOC44MzMsNS42MTYtNDQuMjUyLDEwLjI4IGMtMTUuNDE3LDQuNjYxLTI5LjIxNywxMS40Mi00MS4zOTYsMjAuMjdjLTEyLjE4Miw4Ljg1NC0yMS4zMTcsMTkuMzY2LTI3LjQwOCwzMS41NDlDMy41MzMsMzYxLjU1OSwwLjAxLDM3NC40MDUsMC4wMSwzODYuMDE3IGMwLDEyLjc1MSwyLjg1NywyNC4zMTQsOC41NjUsMzQuNjljNS43MDgsMTAuMzY5LDEzLjAzNSwxOC44NDIsMjEuOTgyLDI1LjQwNmM4Ljk0NSw2LjU3LDE5LjI3MywxMi4wODMsMzAuOTc4LDE2LjU2MiBjMTEuNzA0LDQuNDcsMjMuMzE1LDcuNjU5LDM0LjgyOSw5LjU2MmMxMS41MTYsMS45MDMsMjIuODg4LDIuODU0LDM0LjExOSwyLjg1NGM1MS4wMDcsMCw5MC45ODEtMTIuNDY0LDExOS45MDktMzcuMzk3IGMyNi42NDgtMjMuMjIzLDM5Ljk3MS01MC4wNjIsMzkuOTcxLTgwLjUxN2MwLTEwLjg1NS0xLjU3LTIwLjk4NC00LjcxMi0zMC40MDlDMjgyLjUxLDMxNy4zMzcsMjc4LjQyLDMwOS4yNTQsMjczLjM3MiwzMDIuNDk4eiBNMTYzLjMxMSwxOTguNzIyYy05LjcwNywwLTE4LjkzNy0yLjQ3NS0yNy42OTQtNy40MjZjLTguNzU3LTQuOTUtMTYuMTgtMTEuMzc0LTIyLjI3LTE5LjI3MyBjLTYuMDg4LTcuODk4LTExLjQxOC0xNi43OTYtMTUuOTg3LTI2LjY5NWMtNC41NjctOS44OTYtNy45NDQtMTkuNzkyLTEwLjEzNS0yOS42OTJjLTIuMTktOS44OTUtMy4yODQtMTkuMzE4LTMuMjg0LTI4LjI2NSBjMC0xOC4yNzEsNC44NTQtMzMuOTc0LDE0LjU2Mi00Ny4xMDhjOS43MDUtMTMuMTM0LDIzLjQxMS0xOS43MDEsNDEuMTEyLTE5LjcwMWMxMi41NjMsMCwyMy45MzUsMy44OTksMzQuMTE4LDExLjcwNCBjMTAuMTgzLDcuODA0LDE4LjE3NywxNy43MDEsMjMuOTg0LDI5LjY5MmM1LjgwMiwxMS45OTEsMTAuMjc3LDI0LjQwNywxMy40MTcsMzcuMjU3YzMuMTQsMTIuODQ3LDQuNzExLDI0Ljk4Myw0LjcxMSwzNi40MDMgYzAsMTkuMDM2LTQuMTM5LDM0LjMxNy0xMi40MTksNDUuODMzQzE5NS4xNDQsMTkyLjk2NCwxODEuNzc1LDE5OC43MjIsMTYzLjMxMSwxOTguNzIyeiBNMjQyLjI1MSw0MTMuMTIzIGMtNS4yMyw4Ljk0OS0xMi4zMTksMTUuOTQtMjEuMjY3LDIwLjk4MWMtOC45NDYsNS4wNDgtMTguNTA5LDguNzU4LTI4LjY5MywxMS4xNGMtMTAuMTgzLDIuMzg1LTIwLjg4OSwzLjU3Mi0zMi4xMiwzLjU3MiBjLTEyLjE4MiwwLTI0LjI3LTEuNDMxLTM2LjI1OC00LjI4NGMtMTEuOTktMi44NTEtMjMuNDU5LTcuMTg3LTM0LjQwMy0xMi45OTFjLTEwLjk0NC01LjgtMTkuNzk1LTEzLjc5OC0yNi41NTEtMjMuOTgyIGMtNi43NTctMTAuMTg0LTEwLjEzNS0yMS43NDQtMTAuMTM1LTM0LjY5YzAtMTEuNDE5LDIuNTY4LTIxLjYwMSw3LjcwOC0zMC41NWM1LjE0Mi04Ljk0NSwxMS43MDktMTYuMDg0LDE5LjcwMi0yMS40MDggYzcuOTk0LTUuMzMyLDE3LjMxOS05LjcxMywyNy45NzktMTMuMTMxYzEwLjY2LTMuNDMzLDIwLjkzNy01LjgwOCwzMC44MzMtNy4xMzljOS44OTUtMS4zMzUsMTkuOTg1LTEuOTk1LDMwLjI2Mi0xLjk5NSBjNi4yODMsMCwxMS4wNDMsMC4xOTEsMTQuMjc3LDAuNTY3YzEuMTQzLDAuNzY3LDQuMDQzLDIuNzU5LDguNzA4LDUuOTk2czcuODA0LDUuNDI4LDkuNDIzLDYuNTcgYzEuNjE1LDEuMTM3LDQuNTY3LDMuMzI2LDguODUsNi41NjNjNC4yODEsMy4yMzcsNy4zMjcsNS42NjEsOS4xMzUsNy4yNzljMS44MDMsMS42MTgsNC40MjEsNC4wNDUsNy44NDksNy4yNzkgYzMuNDI0LDMuMjM3LDUuOTQ4LDYuMDQzLDcuNTY2LDguNDIyYzEuNjE1LDIuMzc4LDMuNjE2LDUuMjgsNS45OTYsOC43MDJjMi4zOCwzLjQzMyw0LjA0Myw2LjcxNSw0Ljk5OCw5Ljg1NSBjMC45NDgsMy4xNDIsMS44NTQsNi41NjcsMi43MDcsMTAuMjc3YzAuODU1LDMuNzIsMS4yODMsNy41NjksMS4yODMsMTEuNTdDMjUwLjEwNSwzOTMuNzEzLDI0Ny40ODcsNDA0LjE4MiwyNDIuMjUxLDQxMy4xMjN6Ii8+IDxwb2x5Z29uIHBvaW50cz0iNDAxLjk5OCw3My4wODkgNDAxLjk5OCwwIDM2NS40NDksMCAzNjUuNDQ5LDczLjA4OSAyOTIuMzU4LDczLjA4OSAyOTIuMzU4LDEwOS42MzYgMzY1LjQ0OSwxMDkuNjM2ICAzNjUuNDQ5LDE4Mi43MjUgNDAxLjk5OCwxODIuNzI1IDQwMS45OTgsMTA5LjYzNiA0NzUuMDgxLDEwOS42MzYgNDc1LjA4MSw3My4wODkiLz48L3N2Zz4=')} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-ms { display:none;} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-ms::before { background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgMTcgMTciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE3IDE3IiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJNOC4wMjEsMS4zODV2Ni40MzdoOC43OTFWMC4xMjdMOC4wMjEsMS4zODV6IE0wLDcuODIyaDYuNjQ4VjEuNDk0TDAsMi40NDNWNy44MjJ6IE0wLDE0LjE3NSBsNi42NDgsMS4wMzFWOC42M0gwVjE0LjE3NXogTTguMDIxLDE1LjMyMWw4Ljc5MSwxLjM2NFY4LjYzSDguMDIxVjE1LjMyMXoiLz48L3N2Zz4=')} #wpmchimpa-main .wpmchimpa-social .wpmchimpa-soc.wpmchimpa-ms:hover:before { background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMwMEJDRjIiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgMTcgMTciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE3IDE3IiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJNOC4wMjEsMS4zODV2Ni40MzdoOC43OTFWMC4xMjdMOC4wMjEsMS4zODV6IE0wLDcuODIyaDYuNjQ4VjEuNDk0TDAsMi40NDNWNy44MjJ6IE0wLDE0LjE3NSBsNi42NDgsMS4wMzFWOC42M0gwVjE0LjE3NXogTTguMDIxLDE1LjMyMWw4Ljc5MSwxLjM2NFY4LjYzSDguMDIxVjE1LjMyMXoiLz48L3N2Zz4=')} #wpmchimpa-main .wpmchimpa-close-button{ position: absolute; display: block; top: 0; right: 0; width: 25px; text-align: center; cursor: pointer; } #wpmchimpa-main .wpmchimpa-close-button::before{ content: "\00D7"; font-size: 25px; line-height: 25px; font-weight: 100; color: #999; opacity: 0.4; color:;} #wpmchimpa-main .wpmchimpa-close-button:hover:before{ opacity: 1; } #wpmchimpa-main .wpmchimpa-feedback.wpmchimpa-done{ font-size: 15px; margin: 10px; height: auto;} #wpmchimpa-main .wpmchimpa-feedback.wpmchimpa-done:before{ content:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSIxNDIuOCwzMjMuODUgMzUuNywyMTYuNzUgMCwyNTIuNDUgMTQyLjgsMzk1LjI1IDQ0OC44LDg5LjI1IDQxMy4xLDUzLjU1Ii8+PC9zdmc+'); width: 40px; height: 40px; border-radius: 20px; line-height: 46px; display: block; background-color: #01E169; margin: 40px auto; } .wpmc_2col #wpmchimpa .wpmc_colsplit{ width: calc(50% - 5px); display: inline-block; float: left; } .wpmc_2col #wpmchimpa .wpmc_coleven{ margin-left: 10px; } @media only screen and (max-width: 600px){ .wpmc_2col #wpmchimpa .wpmc_colsplit{ width: 100%; margin-left: 0; } } .animated { -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-fill-mode: both; animation-fill-mode: both; } @-webkit-keyframes bounceInDown { 0%, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: translate3d(0, -3000px, 0); transform: translate3d(0, -3000px, 0); } 60% { opacity: 1; -webkit-transform: translate3d(0, 25px, 0); transform: translate3d(0, 25px, 0); } 75% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 90% { -webkit-transform: translate3d(0, 5px, 0); transform: translate3d(0, 5px, 0); } 100% { -webkit-transform: none; transform: none; } } @keyframes bounceInDown { 0%, 60%, 75%, 90%, 100% { -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { opacity: 0; -webkit-transform: translate3d(0, -3000px, 0); transform: translate3d(0, -3000px, 0); } 60% { opacity: 1; -webkit-transform: translate3d(0, 25px, 0); transform: translate3d(0, 25px, 0); } 75% { -webkit-transform: translate3d(0, -10px, 0); transform: translate3d(0, -10px, 0); } 90% { -webkit-transform: translate3d(0, 5px, 0); transform: translate3d(0, 5px, 0); } 100% { -webkit-transform: none; transform: none; } } .bounceInDown { -webkit-animation-name: bounceInDown; animation-name: bounceInDown; } .animatedout { -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-fill-mode: both; animation-fill-mode: both; } @-webkit-keyframes rollOut { 0% { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); } } @keyframes rollOut { 0% { opacity: 1; } 100% { opacity: 0; -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); } } .rollOut { -webkit-animation-name: rollOut; animation-name: rollOut; } @-webkit-keyframes zoomIn { 0% { opacity: 0; -webkit-transform: scale3d(.3, .3, .3); transform: scale3d(.3, .3, .3); } 50% { opacity: 1; } } </style> <div class="wpmchimpa-reset wpmchimpa-overlay-bg wpmchimpselector chimpmatecss"> <div class="wpmchimpa-maina bounceInDown animated" wpmcexitanim> <div class="wpmchimpa-mainc"> <div id="wpmchimpa-main"> <div id="wpmchimpa-newsletterform" class="wpmchimpa-wrapper"> <div class="wpmchimpa" id="wpmchimpa"> <h3>THE BELL</h3> <div class="wpmchimpa_para"><div style="text-align: center;">Есть те, кто прочитали эту новость раньше вас.</div><div style="text-align: center;">Подпишитесь, чтобы получать статьи свежими.<img src="http://" loading=lazy></div></div> <form wpmc_suc wpmc_msg="Спасибо! ПРОВЕРЬТЕ ПОЧТУ" action="" method="post"> <input type="hidden" name="action" value="wpmchimpa_add_email_ajax"/> <input type="hidden" name="wpmcform" value="1"/> <div class="formbox wpmchimpa-field"><div class="wpmchimpa-text wpmc-ficon"><input type="text" name="email" wpmctype="email" wpmcfield="email" wpmcreq="true" required/><span class="inputlabel">Email</span><span class="inputicon"></span><div class="wpmcinfierr" wpmcerr="email"></div></div><div class="wpmchimpa-subsc"><div class="wpmchimpa-subs-button"></div><div class="wpmchimpa-signal"><style type="text/css">#wpmchimpa-main .sp8 { margin: 0 auto;width: 50px;height: 30px;} #wpmchimpa-main .sp8 > div { background-color: #3d3d3d;margin-left: 3px;height: 100%;width: 6px;display: inline-block;-webkit-animation: wpmchimpa-mainsp8 1.2s infinite ease-in-out;animation: wpmchimpa-mainsp8 1.2s infinite ease-in-out;} #wpmchimpa-main .sp8 .sp82 { -webkit-animation-delay: -1.1s;animation-delay: -1.1s;} #wpmchimpa-main .sp8 .sp83 { -webkit-animation-delay: -1.0s;animation-delay: -1.0s;} #wpmchimpa-main .sp8 .sp84 { -webkit-animation-delay: -0.9s;animation-delay: -0.9s;} #wpmchimpa-main .sp8 .sp85 { -webkit-animation-delay: -0.8s;animation-delay: -0.8s;} @-webkit-keyframes wpmchimpa-mainsp8 { 0%, 40%, 100% { -webkit-transform: scaleY(0.4) } 20% { -webkit-transform: scaleY(1.0) } }@keyframes wpmchimpa-mainsp8 { 0%, 40%, 100% { transform: scaleY(0.4);-webkit-transform: scaleY(0.4);} 20% { transform: scaleY(1.0);-webkit-transform: scaleY(1.0);} }</style><div class="sp8"><div class="sp81"></div><div class="sp82"></div><div class="sp83"></div><div class="sp84"></div><div class="sp85"></div></div></div></div><div style="clear:both"></div></div><div class="wpmchimpa-field wpmchimpa-text wpmc_colsplit wpmc-ficon"><input type="text" name="merge_fields[FNAME]" wpmctype="text" wpmcfield="FNAME" wpmcreq="true" required/><span class="inputlabel">Имя</span><span class="inputicon"></span><div class="wpmcinfierr" wpmcerr="FNAME"></div></div><div class="wpmchimpa-field wpmchimpa-text wpmc_colsplit wpmc-ficon"><input type="text" name="merge_fields[LNAME]" wpmctype="text" wpmcfield="LNAME" wpmcreq="true" required/><span class="inputlabel">Фамилия</span><span class="inputicon"></span><div class="wpmcinfierr" wpmcerr="LNAME"></div></div><div class="wpmchimpa-field wpmchimpa-radio"><div class="wpmchimpa-itemh">Как вы хотите читать The Bell</div><div class="wpmchimpa-itemb"><label class="wpmchimpa-item"><input type="radio" name="group[f899931c09]" value="a22e2103b4" wpmctype="radio" wpmcfield="f899931c09" wpmcreq="true"><span>Дважды в день</span></label><label class="wpmchimpa-item"><input type="radio" name="group[f899931c09]" value="4ab55de31a" wpmctype="radio" wpmcfield="f899931c09" wpmcreq="true"><span>Утренняя рассылка</span></label><label class="wpmchimpa-item"><input type="radio" name="group[f899931c09]" value="e386c1935e" wpmctype="radio" wpmcfield="f899931c09" wpmcreq="true"><span>Вечерняя рассылка</span></label></div><div class="wpmcinfierr" wpmcerr="f899931c09"></div></div> <div style="clear:both"></div> <div class="wpmchimpa-tag">Без спама</div> </form> <div class="wpmchimpa-feedback" wpmcerr="gen"></div> </div> </div> <div class="wpmchimpa-close-button"></div> </div> </div> </div> </div><script data-cfasync="false" src="/cdn-cgi/scripts/af2821b0/cloudflare-static/email-decode.min.js"></script><script type='text/javascript' src='https://qipu.ru/wp-content/themes/authentic/js/vendors.min.js?ver=201710093'></script> <script type='text/javascript'> /* <![CDATA[ */ var translation = { "next":"Next","previous":"Previous"} ; /* ]]> */ </script> <script type='text/javascript' src='https://qipu.ru/wp-content/themes/authentic/js/scripts.js?ver=201710093'></script> <script type='text/javascript' src='/wp-includes/js/wp-embed.min.js?ver=9.9.9'></script> <script type='text/javascript' src='https://qipu.ru/wp-content/plugins/chimpmatepro/public/assets/js/public.js?ver=1.3.2'></script> </body> </html>