Синтаксические плагины — это плагины расширения синтаксиса «ДокуВики». Чтобы понять, что необходимо для регистрации нового синтаксиса в «ДокуВики», вы должны прочитать, как работает парсер.
Для синтаксического плагина example требуется объявить имя класса как syntax_plugin_<example>
, который расширяет класс DokuWiki_Syntax_Plugin
, определённый в файле lib/plugins/syntax.php
. Класс необходимо сохранить в файле с названием lib/plugins/example/syntax.php
. Для более подробной информации можно обратиться к статье о структуре файлов плагина.
Класс должен содержать как минимум следующие функции:
getInfo()
— возвращает хэш с информацией о плагине: автор, электропочта, дата, название, описание, ссылки.getType()
— должен возвращать тип синтаксиса (см. ниже).getSort()
— возвращает число, по которому определяется, в каком порядке должны добавляться состояния, см. также parser, order of adding modes и getSort list.connectTo($mode)
— эта функция наследуется от класса Doku_Parser_Mode, определённого в inc/parser/parser.php
. Это место, где регистрируется регулярные выражения, необходимые для опознания вашего синтаксиса.handle($match, $state, $pos, Doku_Handler $handler)
— функция для подготовки совпавшего синтаксиса для использования рендером.render($mode, Doku_Renderer $renderer, $data)
— функция для отображения контента.
Следующие дополнительные методы могут быть переопределены при необходимости:
getPType()
— определяет, как данный синтаксис размещается относительно параграфов1). Возвращает:normal
— (значение по умолчанию, используется, если метод не переопределяется) Плагин может использоваться внутри параграфов,block
— Открытые параграфы должны быть закрыты до вывода плагина или stack
— Специальный случай. Плагин обёртывает другие параграфыgetAllowedTypes()
(значение по умолчанию: array()
) Должен вернуть массив типов состояний, которые могут быть включены в собственную разметку плагина.accepts($mode)
— Эта функция говорит парсеру, допускает ли плагин синтаксическое состояние $mode в своей разметке. Поведение по умолчанию заключается в проверке наличия $mode в массиве состояний, хранящемся в унаследованном свойстве allowedModes
.
При необходимости могут быть определены дополнительные функции. Рекомендуется добавлять впереди символ подчерка к названиям самостоятельно определённых функций для того, чтобы избежать возможных пересечений имён при дальнейшем развитии спецификации плагинов.
Унаследованные свойства
allowedModes
— начальное значение: пустой массив, унаследованный от класса Doku_Parser_Mode
, определённого в inc/parser/parser.php
. Содержит список других синтаксических состояний, появление которых допустимо внутри собственного синтаксического состояния плагина (т. е. состояния, которые относятся к любым другим элементам разметки «ДокуВики», которые могут быть включены внутрь собственной разметки плагина). Обычно он автоматически заполняется функцией accepts()
, используя результаты getAllowedTypes()
.«ДокуВики» использует различные синтаксические типы для того, чтобы определить, какие синтаксические конструкции могут быть вставлены внутрь другой. Например, вы можете вставить текстовое форматирование внутрь таблицы.
Для того, чтобы интегрировать свой плагин в эту систему, нужно указать, какой тип он имеет и какие типы могут вставляться в него. В настоящий момент доступны следующие типы:
Тип | Используется в… | Описание |
---|---|---|
container | listblock, table, quote, hr | Контейнеры — это сложные состояния, которые могут содержать много других состояний, но они не должны использоваться в таблицах и списках (состояние hr нарушает общий принцип), поэтому они отнесены к этому типу. |
baseonly | header | Некоторые состояния допустимы только внутри базового состояния. |
formatting | strong, emphasis, underline, monospace, subscript, superscript, deleted, footnote | Состояния для изменения стиля текста (сноски (footnote) также можно рассматривать как стиль). |
substition2) | acronym, smiley, wordblock, entity, camelcaselink, internallink, media, externallink, linebreak, emaillink, windowssharelink, filelink, notoc, nocache, multiplyentity, quotes, rss | Состояния, в которых токен просто меняется на что-то; не могут содержать в себе другие состояния. |
protected | preformatted, code, file, php, html | Состояния, имеющие начальный и конечный токены, но внутри которых не допустимы никакие другие состояния. |
disabled | unformatted | Внутри этого состояния вики-разметка не отрабатывается, переносы строки и пробелы не сохраняются. |
paragraphs | eol | Используется для отметки границ параграфов. |
Для описания того, что каждый из типов значит и какие другие классы форматирования зарегистрированы в них, читайте комментарии в файле inc/parser/parser.php
.
Цель данного руководства — разобрать концепции, касающиеся синтаксических плагинов «ДокуВики» и пройти шаги, связанные с написанием своего собственного плагина.
Для тех, кто с особым нетерпением жаждет начать: возьмите копию плагина syntax plugin skeleton. Это своего рода костяк — плагин, который выводит «Hello World!», когда встречает токен «<TEST>
» в статье вики. Начните наращивать на него «мясо».
состояния — modes
handle
handle()
вызывается, когда парсер решит, что столкнулся в содержимом статьи вики с куском, относящимся к синтаксису вашего состояния.$state
говорит, какой тип шаблона из приписанных к вашему состоянию сработал. Если это просто обычный текст, то параметр state
будет установлен в DOKU_LEXER_UNMATCHED
.render()
, потому что выдача метода handle кэшируется. This also means that you shouldn't do any stuff here that mustn't be cached.render
render()
выполняет инструкции отображения, которые применимы к синтаксическому состоянию плагина (и которые были созданы методом handle()
плагина.$renderer->doc .= 'content';
handle()
.
Нет никакой гарантии, что метод
render()
будет вызван в то же время, что и метод handle()
. Инструкции, произведённые хэндлером, кэшируются и могут использоваться рендером в более позднее время. Единственный надёжный способ передать данные от handle()
к render()
— это использовать возвращаемый методом handle()
массив, который передаётся методу render()
в качестве параметра $data
.
Состояния (более точно синтаксические состояния) — это основа, на которой базируется парсер «ДокуВики». Каждый отдельный элемент разметки «ДокуВики» имеет своё синтаксическое состояние. Например, существует состояние strong
для работы со strong, состояние superscript
для работы с superscript, состояние table
для работы таблицами и многие другие.
Когда парсер сталкивается с разметкой, он попадает в соответствующее этой разметке синтаксическое состояние. Свойства и методы конкретного синтаксического состояния управляют тем, как ведёт себя парсер, пока он в этом состоянии, включая:
Ваш плагин добавит своё синтаксическое состояние к парсеру — это автоматически производится «ДокуВики», когда впервые загружает плагин, назначаемое имя — plugin_
+ имя директории плагина (которое является также именем класса плагина без префикса «syntax_
»). Затем, когда парсер сталкивается с разметкой, используемой вашим плагином, он (парсер) войдёт в это синтаксическое состояние. Пока он находится в этом состоянии, ваш плагин управляет тем, что может делать парсер.
Для упрощения синтаксические состояния, которые ведут себя одинаковым образом, были сгруппированы в несколько типов состояний — полный список может быть найден в разделе «Синтаксические плагины».
Каждый тип состояний соотносится с ключом в массиве $PARSER_MODES
. Элемент этого массива, соответствующий каждому типу состояний, сам является массивом, который содержит все синтаксические состояния, относящиеся к этому типу. Например, в «чистой» «ДокуВики» без установленных плагинов элемент массива $PARSER_MODES['formatting']
содержит: strong, emphasis, underline, superscript, subscript, monospace, deleted и footnote.
Когда плагин загружается в парсер, то через getType()
он запрашивается о типе состояния, к которому относится. Затем синтаксические состояния, относящиеся к плагину, добавляются в соответствующий массив $PARSER_MODES
.
Указанный вашим плагином тип состояний определяет, где в статье «ДокуВики» парсер будет опознавать разметку вашего плагин. Другие синтаксические состояния «ДокуВики» (также, как и плагины) не будут знать о вашем плагине, но они знают о различных типах состояний.
Если они допускают конкретный тип состояний, он допускают все состояния этого типа, включая любые плагины, которые заявили этот тип состояний.
Выберите тип состояний для своего плагина, сравнивая поведение своего плагина с поведением стандартных состояний «ДокуВики». Выберите тип, к которому относятся наиболее похожие состояния.
Есть другие состояния, которые могут возникнуть внутри разметки вашего собственного состояния.
Каждое синтаксическое состояние имеет собственный массив допустимых состояний, который говорит парсеру, какие именно другие синтаксические состояния будут опознаваться во время обработки состояния. То есть, если вы хотите, чтобы ваш плагин мог оказаться внутри разметки «**strong**», тогда состояние strong
должен включить состояние вашего плагина в свой массив «allowedModes». И если вы хотите позволить разметке strong
включаться внутрь разметки вашего плагина, то ваш плагин должен содержать 'strong'
в своём массиве «allowModes».
Ваш плагин собирает в массив «allowedModes» другие синтаксические состояния посредством типа состояний, объявляемого методом
getType()
.
Ваш плагин сообщает парсеру, какие другие синтаксические состояния он допускает, декларируя (объявляя) их через метод
getAllowedTypes()
.
PType определяет, как парсеру работать с HTML-элементами <p>, когда он имеет дело с вашим синтаксическим состоянием.
Обычно в тот момент, когда парсер сталкивается с некоторой разметкой, имеется открытый HTML-тэг параграфа. Парсеру необходимо знать, должен ли он закрыть этот тэг перед входом в ваше синтаксическое состояние и затем открыть другой параграф на выходе (PType='block'
или PType='stack'
) или парсер должен оставить параграф в покое (PType='normal'
).
PType также определяет будет ли параграф создаваться внутри вашего синтаксического состояния. И если будет - то как.
Если PType='normal'
, то параграф не будет создаваться вовсе.
Если PType='block'
, то парсер закроет параграф перед входом в ваше синтаксическое состояние, и откроет новый параграф после выходы из вашего синтаксического состояния. Внутри вашего синтаксического состояния создание параграфов должно быть запрещено (см. "Допустимые состояния"), иначе открытый параграф закроется как обычно (как только встретятся два подряд или более newlines), и откроется новый. В результате, например, открывающий TAG <DIV> может оказаться внутри одного параграфа, а закрывающий </DIV> внутри другого. Или ещё что-то.
Если PType='stack'
, то парсер:
Для тех, кто знает CSS, возвращение PType='block'
означает, что html, произведённый вашим плагином, будет похож на display:block
, а возвращение PType='normal'
означает html, похожий на display:inline
.
Предполагается, что вы хорошо знакомы со стандартным шаблоном syntax plugin ENTRY ⇒ UNMATCHED ⇒ EXIT. В зависимости от значения PType <p>
и </p>
будут расставляться автоматически рендером в разных точках снаружи и внутри текста плагина. Поэтому вашему плагину не нужно заботиться об этих тэгах.
wikisyntax | PType=normal | PType=block | PType=stack |
---|---|---|---|
foo <plugin>text</plugin> bar | <p>foo ENTRY("<plugin>") UNMATCHED("text") EXIT("</plugin>") </p> <p>bar</p> | <p>foo</p> ENTRY("<plugin>") UNMATCHED("text") EXIT("</plugin>") <p>bar</p> | <p>foo</p> ENTRY("<plugin>") <p> UNMATCHED("text") </p> EXIT("</plugin>") <p>bar</p> |
Этот номер используется лексером3) для управления порядком, в котором он проверяет шаблоны синтаксических состояний на «сырых» (исходных) данных вики. Это важно только в том случае, если один и тот же участок данных попадает в шаблоны, относящиеся к двум или более состояниям. После проверки будет выбран шаблон, относящийся к состоянию с наименьшим порядковым номером.
Вы можете использовать это свойство для написания плагина, который заменяет или расширяет «родной» хендлер «ДокуВики» для той же синтаксической конструкции. Примером является плагин «Code».
Подробности о существующих порядковых номерах доступны для обоих parser (sort list).
Парсер использует PHP-функции «preg»4). Детальное объяснения регулярных выражений и их синтаксиса выходит за пределы этого руководства. Существует много хороших источников в интернете.
Полный синтаксис «preg» не доступен для использования в конструировании шаблонов синтаксических плагинов. Ниже приведён список известных различий:
(?i)
, (?-i)
.
Парсер предоставляет плагину четыре функции для регистрации необходимых шаблонов. Каждая функция относится к шаблонам с разными смыслами.
addSpecialPattern()
— это шаблоны, которые используются, когда один шаблон — это всё, что нужно. В терминах парсера эти шаблоны представляют и вход в синтаксическое состояние плагина, и выход из этого синтаксического состояния, всё в одно сравнение. Обычно они используются в плагинах substition
.addEntryPattern()
— шаблон, указывающий на начало данных, которые должны быть обработаны плагином. Обычно эти шаблоны должны включать в себя заглядывание вперёд для проверки существования выходного шаблона. Любой плагин, который регистрирует входной шаблон, также должен зарегистрировать выходной шаблон.addExitPattern()
— шаблон, указывающий на конец данных, которые должны быть обработаны плагином. Этот совпадение с этим шаблоном может произойти, только если было найдено совпадение с входным шаблоном5).addPattern()
— представляют специальный синтаксис, применимый к плагину, который может встретиться между входным и выходным шаблонами. Обычно это нужно только для достаточно сложных структур, например, таблиц и списков.
Один плагин может добавить несколько шаблонов в парсер, включая более чем один шаблон одного типа.
Советы
+?
или *?
вместо +
или *
.
Это часть вашего плагина, которая должна совершать всю работу. До того как «ДокуВики» выведет статью вики, он создаёт список инструкций для рендера. Метод handle()
плагина создаёт инструкции отображения для собственного синтаксического состояния. В некий более поздний момент они будут интерпретированы методом render()
плагина. Список инструкций кэшируется и может быть использован много раз, разумно максимально увеличить объём работы, совершаемой один раз этой функцией и максимально уменьшить объём работы, совершаемый много раз функцией render()
.
Параметр $match
— текст, который совпадает с шаблоном, или, в случае DOKU_LEXER_UNMATCHED
, непрерывный кусок обычного текста, который не совпал с каким-либо шаблоном.
Параметр $state
— тип шаблона, из-за которого запустился вызов handle().
DOKU_LEXER_ENTER
— шаблон установлен функцией addEntryPattern();DOKU_LEXER_MATCHED
— шаблон установлен функцией addPattern();DOKU_LEXER_EXIT
— шаблон установлен функцией addExitPattern();DOKU_LEXER_SPECIAL
— шаблон установлен функцией addSpecialPattern();DOKU_LEXER_UNMATCHED
— обычный текст, встреченный внутри синтаксического состояния плагина, который не совпал ни с одним шаблоном.
Параметр $pos
— позиция первого символа найденного текста.
Параметр &$handler
— ссылка на объект Doku_Handler.
Часть плагина, которая производит вывод окончательной веб-страницы или какой-либо другой поддерживаемый формат. Именно здесь плагин добавляет собственный вывод к уже созданным другими частями рендерера путём склейки со свойством doc
рендера. Т. е.:
$renderer->doc .= "некий вывод плагина...";
В любых сырых данных вики, которые передаются
render()
, все спецсимволы должны быть преобразованы в элементы HTML. Вы можете использовать PHP-функции htmlspecialchars()
, htmlentities()
или собственный метод xmlEntities()
рендера. Т. е.:
$renderer->doc .= $renderer->_xmlEntities($text);
Параметр $mode
— имя формата состояния финального вывода произведённого рендером. В настоящее время «ДокуВики» поддерживает только один формат вывода — XHTML
6).
Новые состояния могут быть представлены в плагинах рендера. Плагины должны производить вывод только для тех форматов, которые они поддерживают, это значит, что эта функция должна быть структурирована…
if ($mode == 'xhtml') { // supported mode // code to generate XHTML output from instruction $data }
Параметр $data
— массив, содержащий инструкции, предварительно подготовленные собственным методом handle()
плагина. Эта функция должна интерпретировать инструкции и выдавать соответствующий вывод.
Сырые данные вики, которые достигли вашего плагина, больше никогда не должны обрабатываться. Никакой дальнейшей обработки не производится над выводом после того, как он покидает плагин. Как минимум, плагин должен убедиться, что в выводе все спецсимволы HTML заменены на HTML-последовательности. Также к извлечённым и используемым внутри данным вики нужно относится с вниманием. См. также статью «Безопасность».
Смотрите статьи «Локализация» и «Структура файлов плагина».
Смотрите статью «Конфигурация».
Смотрите статью «Структура файлов плагина».
Для того, чтобы облегчить жизнь пользователям вики, которые установили ваш плагин, следует добавить кнопку в панель инструментов редактора.
См. статьи:
Ну хорошо, вы решили расширить синтаксис «ДокуВики» своим собственным плагином. Вам придётся разработать, каким будет ваша синтаксическая конструкция и как она будет отображаться в браузере пользователя. Теперь вам необходимо написать сам плагин.
lib/plugins/
. Эта директория должна называться также, как ваш плагин.syntax.php
. В качестве отправной точки, можете использовать скелет плагина. Скопируйте его в свою директорию.syntax_plugin_<название вашего плагина>
7);getInfo()
, чтобы он выдавал информацию о вашем плагине;getType()
, чтобы он выдавал тип состояний, к которому относится ваш плагин;getAllowedTypes()
для сообщения всех типов состояний, которые ваш плагин может включать внутрь своей собственной синтаксической конструкции. Если ваш плагин не желает позволять какому-либо состоянию включаться в себя, он может быть выкинут;getPType()
, чтобы он выдавал PType, который относится к вашему плагин. Если это 'normal'
, вы можете просто убрать этот метод;getSort()
, чтобы он выдавал уникальный номер, проверьте его в списке плагинов;connectTo()
, чтобы зарегистрировать шаблон для опознавания вашего синтакиса;postConnect()
, если ваш синтаксис имеет второй шаблон, для того, чтобы указать, когда парсер должен покинуть ваше синтаксическое состояние.handle()
и render()
:
Когда синтаксическая конструкция этого плагина [NOW]
встречается в статье вики, текущая дата и время отображается в формате RFC2822.
'substition'
. Мы подставляем временую метку вместо токена [NOW]
, аналогично смайлам и акронимами. Они также относятся к типу состояний 'substition'
.[NOW]
. Т. о. нам не нужен метод getAllowedTypes()
.normal
— это значение по умолчанию, поэтому нам не нужно определять метод getPType()
.[NOW]
. Единственная вещь, с которой нужно быть осторожным, это то, что символы «[» и «]» имеют специальное значение в регулярных выражениях, поэтому нам нужно «выключить» (escape) их, создав шаблон '\[NOW\]'
.handler()
не должен ничего делать. Нам не нужно заботиться о специальных состояниях или дополнительных параметрах в нашем синтаксисе. Мы просто вернём пустой массив в качестве инструкций для рендера.render()
— добавить штамп времени к текущей статье вики — $renderer->doc .= date('r');
И вот наш плагин завершён!
<?php /** * Plugin Now: Inserts a timestamp. * * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Christopher Smith <chris@jalakai.co.uk> */ // must be run within DokuWiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); /** * All DokuWiki plugins to extend the parser/rendering mechanism * need to inherit from this class */ class syntax_plugin_now extends DokuWiki_Syntax_Plugin { function getInfo(){ return array( 'author' => 'me', 'email' => 'me@someplace.com', 'date' => '2005-07-28', 'name' => 'Now Plugin', 'desc' => 'Include the current date and time', 'url' => 'http://www.dokuwiki.org/plugin:tutorial', ); } function getType() { return 'substition'; } function getSort() { return 32; } function connectTo($mode) { $this->Lexer->addSpecialPattern('\[NOW\]',$mode,'plugin_now'); } function handle($match, $state, $pos, &$handler) { return array($match, $state, $pos); } function render($mode, &$renderer, $data) { if($mode == 'xhtml'){ $renderer->doc .= date('r'); return true; } return false; } } ?>
Замечание: из-за способа, которым «ДокуВики» кэширует страницы, этот плагин будет отображать дату/время для момента, когда был создан кэш страницы. Вам нужно добавить на страницу макрос ~~NOCACHE~~
,чтобы отображалось правильное время каждый раз, когда запрашивается страница.
Когда встретится синтаксическая конструкция плагина <color somecolour/somebackgroundcolour>
встречается в статье вики, цвет текста сменяется на «somecolour», а цвет фона — на «somebackgroundcolour»; и оба остаются такими, пока не встретится </color>
.
strong
, его тип — 'formatting', поэтому мы тоже должны использовать этот тип.substition
, formatting
и disabled
.normal
— это значение по умолчанию, поэтому нам снова не нужен метод getPType()
.'<color.*>(?=.*?</color>)'
. Выходной шаблон аналогично </color>
.handle()
должен иметь дело с тремя состояниями: совпадающими с входным и выходным шаблонами и «несовпадающим» для промежуточного текста. DOKU_LEXER_ENTER
требует некоторой обработки для извлечения значений цветов текста и фона, они войдут в инструкцию для нашего рендера.DOKU_LEXER_UNMATCHED
не требует какой-либо обработки, но нам придётся передать «несовпадающий» текст (в параметре $match
) методу render()
, поэтому он войдёт в инструкцию для нашего рендера.DOKU_LEXER_EXIT
не требует какой-либо обработки и не имеет никаких особых данных, мы просто должны сделать выходную инструкцию для render()
.render()
необходимо иметь дело с теми же тремя сотояниями, что и handle()
.DOKU_LEXER_ENTER
— отрыть тэг span
с указанием стиля, использующего значения цветов текста и фона.DOKU_LEXER_UNMATCHED
— добавить «несовпавший» текст к выходному документу.DOKU_LEXER_EXIT
— закрыть тэг span
.Опять же, всё достаточно очевидно. И вот, что мы имеем:
<?php /** * Plugin Color: Sets new colors for text and background. * * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Christopher Smith <chris@jalakai.co.uk> */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); /** * All DokuWiki plugins to extend the parser/rendering mechanism * need to inherit from this class */ class syntax_plugin_color extends DokuWiki_Syntax_Plugin { /** * return some info */ function getInfo(){ return array( 'author' => 'Christopher Smith', 'email' => 'chris@jalakai.co.uk', 'date' => '2008-02-06', 'name' => 'Color Plugin', 'desc' => 'Changes text colour and background', 'url' => 'http://www.dokuwiki.org/plugin:tutorial', ); } function getType(){ return 'formatting'; } function getAllowedTypes() { return array('formatting', 'substition', 'disabled'); } function getSort(){ return 158; } function connectTo($mode) { $this->Lexer->addEntryPattern('<color.*?>(?=.*?</color>)',$mode,'plugin_color'); } function postConnect() { $this->Lexer->addExitPattern('</color>','plugin_color'); } /** * Handle the match */ function handle($match, $state, $pos, &$handler){ switch ($state) { case DOKU_LEXER_ENTER : list($color, $background) = preg_split("/\//u", substr($match, 6, -1), 2); if ($color = $this->_isValid($color)) $color = "color:$color;"; if ($background = $this->_isValid($background)) $background = "background-color:$background;"; return array($state, array($color, $background)); case DOKU_LEXER_UNMATCHED : return array($state, $match); case DOKU_LEXER_EXIT : return array($state, ''); } return array(); } /** * Create output */ function render($mode, &$renderer, $data) { if($mode == 'xhtml'){ list($state,$match) = $data; switch ($state) { case DOKU_LEXER_ENTER : list($color, $background) = $match; $renderer->doc .= "<span style='$color $background'>"; break; case DOKU_LEXER_UNMATCHED : $renderer->doc .= $renderer->_xmlEntities($match); break; case DOKU_LEXER_EXIT : $renderer->doc .= "</span>"; break; } return true; } return false; } // validate color value $c // this is cut price validation - only to ensure the basic format is correct and there is nothing harmful // three basic formats "colorname", "#fff[fff]", "rgb(255[%],255[%],255[%])" function _isValid($c) { $c = trim($c); $pattern = "/^\s*( ([a-zA-z]+)| #colorname - not verified (\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))| #colorvalue (rgb\(([0-9]{1,3}%?,){2}[0-9]{1,3}%?\)) #rgb triplet )\s*$/x"; if (preg_match($pattern, $c)) return trim($c); return ""; } } ?>
Замечание: никаких проверок на корректность названий цветов или значения RGB не производилось.
metadata
, которое ничего не выводит, только собирает метаданные для страницы. Используйте его для вставки значений в массив метаданных.