Единообразие. Довольно просто познать природу вещей, если можно привести их свойства к общему знаменателю. Наш мир многогранен, даже сложен, но, в то же время, он довольно унифицирован. Многое в природе построено по одному и тому же принципу, что позволяет переносить свойства одних объектов на другие. Или даже использовать одни предметы в качестве других. Природа на наших глазах создаёт книгу жизни, и, как хороший автор, использует там метафоры, эпитеты и, разумеется, олицетворения.
В 105 выпуске уже был обозначен факт, что человеку свойственно всё упорядочивать и систематизировать. Сегодня пришло время упомянуть ещё одно важное человеческое качество: стремление к унификации, созданию единого образа окружающего нас мира. Однако унификация не заканчивается на живой природе, она переносится и на творения человека. Что бы мы не создали, мы не хотим создавать вновь что-то, пусть даже слабо похожее на уже сотворённое, — гораздо мудрым выбором будет для нас использование или приспособление уже созданного.
Сегодняшний разговор в очередной раз пойдёт о CSS, а точнее об одном из его нераскрытых возможностей, которое способно вывести CSS на серверную, тёмную для него сторону силы.
Лирическое преступление
Отходя от шока после прочтения столь необычного внетематического введения, перейдём непосредственно к сути теоретических изысканий. Сегодня мы будем применять CSS на сервере, а не на клиенте. Что же это значит? CSS будет выступать для нас не в роли Каскадных Таблиц Стилей, а в роли… Чего-то иного. Давайте разбираться вместе, чего же.
Для разборок нам потребуется самый обычный XHTML-документ, над которым мы будем экспериментировать все следующие часы.
Перед нами самый обычный документ, который мы создали для наших экспериментов. Мы видим, что он чудесным образом был уже стилизован, так как мы разглядели подключение CSS-файла. Такой документ можно прямо сейчас открыть в браузере и увидеть его во всей красе. Просто чудеса.
Теперь давайте представим, что нам нужно этот документ связать с динамическими данными, которые могут приходить к нам в любой форме. Какую форму лучше выбрать? Какой формат шаблонов лучше всего использовать? Однозначно, выбор шаблонизатора — выбор читателя. Однако справедливости ради стоит заметить, что не каждая выбранная форма привязки будет удобной всем сторонам. Зачастую выбранная привязка удовлетворяет лишь одной из сторон процесса разработки, но не той, которая занимается вёрсткой документов. Данная запись пытается склонить весы на сторону верстальщиков и FE-разработчиков: мы представляем сегодня интересы именно этого слоя населения Земли.
Ранее в записи уже упоминался серверный CSS. Пришло время окончательно раскрыть это понятие. Прямо сейчас мы теоретически создадим и оформим новый формат на основе CSS. Наш формат позаимствует у своего предшественника структуру и душевную составляющую, а остальное мы добавим ему самостоятельно. Серверный CSS давайте лучше будем называть Каскадными Таблицами Трансформации — CTS. Далее вы поймёте, почему мы их так назвали.
CTS и будет нашим шаблонизатором. Его главная задача — связывать динамические данные со статической структурой XHTML-документа. Посмотрим, как CTS будут справляться с ней.
Осуществление связывания
CSS нам одолжил свою структуру, поэтому синтаксис CTS-файлов будет полностью совпадать с синтаксисом CSS-файлов. Но почему же мы назвали CTS именно CTS? Всё очень логично: он будет заниматься трансформацией исходной структуры документа, её преобразованием и даже искажением. Исходный XHTML-документ после применения CTS не будет похож сам на себя до этой операции. Уже слышны нарастающие крики: «Документ уже не тот!» Подождите, пока ещё тот.
Давайте вспомним, как мы осуществляем стилизацию документа посредством CSS. Для этого нам доступны так называемые селекторы, с помощью которых мы можем попасть в любой уголок нашего документа. Это наш GPS-навигатор по документу. CSS-селекторы можно создавать на основе трёх контрольных структур: имя элемента в документе, уникальный идентификатор элемента и CSS-класс документа. Подробнее с селекторами вы можете познакомиться в соответствующей рекомендации W3C, а мы посмотрим самые простые примеры различных селекторов:
После указания селектора, мы указываем необходимый набор свойств в фигурных скобках , с помощью которого управляем визуальным представлением элемента в браузере. Каждое свойство — это пара имя-значение. Например так:
Селектор и набор свойств вместе составляют CSS-правило, которых в документе можно записать сколько душе угодно (но лучше не злоупотреблять).
Теперь представим, что вместо браузера у нас другая программа — шаблонизатор CTS, а вместо визуального представления — структура документа. Таким образом, мы приходим к определению CTS: он предназначен для управления структурой документа на стороне сервера с помощью заданного набора свойств.
В CSS мы имеем предопределённый набор свойств, с помощью которых мы управляем визуальной составляющей документа. В CTS мы должны создать точно такой же набор свойств, который должен реализовывать по меньшей мере следующий набор операций:
- Управление содержимым элементов: добавление текста к концу элемента, установка текста в начало элемента или на указанное в текст местоположение (ключевая точка);
- Манипуляции с атрибутами: добавление новых атрибутов, возможность изменения и удаления существующих;
- Управление элементами: сокрытие (удаление из дерева) элементов при определённых условиях, добавление новых элементов в дерево при определённых условиях или без них;
- Работа с CSS-классами: специальные свойства для изменения содержимого атрибута class;
- Поддержка операций цикла и дополнительных селекторов для управления цикла, например: :last-loop-element — это последний элемент, который будет создан в цикле, и с помощью этого селектора можно будет управлять его параметрами.
Это базовые группы свойств, которые следует реализовать в первую очередь.
В CTS мы добавим единственный новый синтаксический элемент: подстановочные переменные и переменные, созданные внутри CTS-объявлений. Подстановочные переменные должны начинаться со знака %, а внутренние подстановочные переменные — со знака %%. Например, на серверной стороне нам доступна переменная с именем page.title, тогда, чтобы подставить её в CTS-файле, мы используем следующую конструкцию: %page.title. Внутренние переменные используются, в основном, в циклах, для того, чтобы не было перекрещивающихся определений переменных, так как их имена должны быть уникальными в пределах документа.
Сейчас важно понять одну большую разницу между CSS и CTS, которая поможет нам в дальнейшем создавать правильные имена для свойств: если CSS оперирует существительными, то в CTS используются глаголы. CTS — это язык действий, где каждое свойство определённого правила производит операцию над элементом, который указан в селекторе (или над группой элементов).
Ключевая особенность CTS заключается в использовании существующего дерева элементов и связанных с ними атрибутов. Селекторы помогают нам по уже определённым классам и идентификаторам, а также по предопределённым в XHTML именам элементов, добираться до нужных и применять к ним необходимые действия.
Давайте посмотрим на пример правила, с помощью которого можно установить заголовок страницы из динамической переменной:
О предопределённых свойствах, в данном случае об append, мы поговорим чуть позже. Следует только знать, что оно добавляет к содержимому элемента по селектору новое (если у элемента не было содержимого до этой операции, то весь элемент будет содержать лишь то, что мы добавим через CTS). Главное здесь — не забыть установить %page.title, чтобы эта переменная успешно была добавлена к содержимому заголовка страницы.
Функции подстановки значений
В CSS мы можем использовать следующую конструкцию для установки фона элемента:
В данном случае мы видим ключевое слово url и круглые скобки после него, в которых заключено непосредственное значение (аргумент). Такую конструкцию мы будем называть функцией CTS. Результат выполнения функции подставляется вместо неё и передаётся свойству.
В CTS определено две ключевых функции. Первая — это selector, с помощью которой можно находить элементы в документе для подстановки в качестве значения данного свойства. Вторая — это expression, который является логическим выражением.
Функция selector обладает следующим синтаксисом:
Функция expression обладает следующим синтаксисом:
Аргументом данной функции является логическое выражение, которое возвращает true или false. Выражение состоит из двух частей, каждая из которых определяется либо собственным значением, либо переменной, либо значением элемента или атрибута, полученного по селектору. Если выражение в скобках истинно, то указанное действие по свойству будет выполнено, иначе этого не произойдёт. Данную функцию можно применять абсолютно к любому свойству, просто приписывая её в конец как ещё один аргумент.
При реализации CTS-шаблонизатора, можно встраивать дополнительные функции, которые будут доступны для вызова точно таким же образом. Можно реализовать абсолютно любой набор таких функций.
Предопределённые операции
В дальнейшем нам понадобится следующая памятка. Если вы запутаетесь при чтении записи, где селекторы, где свойства и значения, то смотрите на неё:
Ранее мы разбивали набор предопределённых свойств (операций) по группам. Пришло время разобрать какие именно свойства мы должны определить.
Управление содержимым элемента
- append — присоединение содержимого переменной или строки в конец содержимого элемента;
- prepend — присоединение содержимого переменной или строки в начало содержимого элемента;
- replace — замена содержимого элемента указанным содержимым;
- clear — удаление содержимого из элемента;
- store — помещение содержимого элемента во внутреннюю переменную, например:
store: %%new.variable. В данном коде мы помещаем содержимое элемента во внутреннюю переменную%new.variable. В дальнейшем мы можем использовать эту переменную по всему CTS-документу, например, для вставки в другие места; - substitute — замена указанной строки другой строкой. Позволяет подставлять необходимое содержимое в нужное место в содержимом элемента. Например, если у нас есть строка Hello, My name is ###. I'm glad to see you there!, то мы можем заменить ### на нужную нам строку следующим образом:
substitute: «» %page.nameдля замены на содержимое переменной илиsubstitute: "###" "Din"для замены на статичную строку;
Рассмотрим пример применения каждого из этих атрибутов (мы будем совершать все операции на одном элементе, чтобы сохранить минимальный объём кода). Для примера мы представим, что у нас есть такой XHTML-элемент:
Теперь произведём примерную его модификацию:
Манипуляции с атрибутами
- add-attribute — создание нового атрибута данного элемента. Необходимо передать в качестве свойств имя нового атрибута и его содержимое, в качестве которого может выступать как строка, так и переменная;
- remove-attribute — удаление атрибута по имени из элемента. Если атрибут не существует, то он, естественно, не будет удалён;
- append-attribute-content — добавление содержимого к концу значения существующего атрибута. Необходимо передать в качестве свойств имя атрибута и добавляемое значение, которое может быть как строкой, так и переменной;
- prepend-attribute-content — добавление содержимого в начало значения существующего атрибута. В качестве свойств — имя атрибута и значение (строка или переменная);
- replace-attribute-content — замена содержимого атрибута новым содержимым (строкой или переменной). В качестве свойств передаётся имя существующего атрибута и новое значение;
- clear-attribute-content — очищение содержимого атрибута. Необходимо передать имя атрибута, содержимое которого следует обнулить;
- store-attribute-content — сохранение содержимого атрибута в определённой внутренней переменной;
- substitute-attribute-content — замена определённой подстроки содержимого элемента указанной строкой или переменной.
Для примера возьмём следующий элемент:
Попробуем произвести некоторые из описанных выше операций:
Управление элементами
- hide — сокрытие (удаление) элемента. В качестве значения необходимо передать условное выражение;
- show — отображение элемента. В качестве значения — условное выражение;
- append-element — добавление дочернего элемента относительно текущего после всех остальных дочерних его элементов. Для проведения операции необходимо передать имя нового элемента или селектор элемента, который будет содеражть CSS-класс элемента или идентификатор элемента;
- prepend-element — добавление дочернего элемента относительно текущего перед всеми остальными дочерними его элементами. Для проведения операции необходимо передать имя нового элемента (или селектор);
- place-element — добавление дочернего элемента в позицию указанной строки. Для проведения операции необходимо передать строку для замены и имя нового элемента (либо селектор элемента). Новый элемент будет включать в себя заменённую строку;
- remove-element — удаление дочернего элемента относительно текущего. Необходимо передать имя элемента для удаления или селектор для нахождения нужного элемента;
- replace-element — замена дочернего элемента новым элементом. В качестве значения следует использовать селектор для нахождения элемента, которым следует заменить дочерний (который можно определить либо по имени элемента, либо таким же образом по селектору);
В качестве примера возьём элемент как в прошлом разделе:
Попробуем произвести некоторые манипуляции над ним:
Работа с CSS-классами
- add-class — добавление CSS-класса элементу;
- remove-class — удаление CSS-класса;
- replace-class — замена класса элемента, указанного первым значением, вторым значением;
- switch-class — циклическая смена одного CSS-класса другим. В качестве значения — два имени CSS-класса.
Дополнительных иллюстраций приводить здесь нет необходимости: названия свойств говорят сами за себя.
Циклы
Для циклов определены не только необходимые свойства, но и некоторые селекторы для удобства работы с первыми. Для начала разберём набор свойств:
- loop — добавление цикла. В качестве значений нужно передать переменную массива и внутреннюю (%%) переменную для доступа к элементам массива на каждой итерации цикла;
В качестве дополнительных селекторов выступают следующие:
- :loop-first — первый элемент цикла;
- :loop-last — последний элемент цикла;
- :loop-n(N) — N-ный элемент цикла.
Для примера, допустим, у нас есть следующая переменная:
И такой блок кода:
Преобразуем его с помощью цикла:
Подключение CTS-файлов
В зависимости от реализации CTS-шаблонизатора, подключение CTS-файлов к документам можно сделать различными способами. Мы же сделаем это так, как это сделано в CSS: inline-декларация CTS-стилей и подключение внешних файлов.
Подключение внешнего CTS-файла выглядит следующим образом:
А также можно использовать inline-декларацию (однако данный способ не рекомендуется из-за его семантической нестабильности):
После обработки таких документов CTS-шаблонизатором, им следует удалить все следы подключений, чтобы они не попали в публичный документ.
Теория реализации
Со стороны реализации CTS мы видим два элемента: основанный на CSS парсер и XML-парсер (или XHTML-парсер). Сначала будет обработан XHTML-документ, будет создано дерево его элементов. XML-парсер определяет, есть ли подключения внешних CTS-файлов или inline-декларации CTS. Если они присутствуют, к работе подключается CTS-парсер, основанный на CSS-парсере. В соответствии с внутренним алгоритмом, структура XML-дерева преобразуется с учётом указанного набора правил и выдаётся конечный результат.
Автор планирует в дальнейшем реализовать шаблонизатор Каскадных Таблиц Трансформации и применять его на практике. Помимо этого, планируется улучшение самого CTS и добавление в него новых свойств.
В заключение
Кажется, что такая структура очень подходит верстальщикам. При наличии хорошей документации, все свойства становятся понятными и ими довольно легко манипулировать. Использование такого шаблонизатора упрощает работу, однако со стороны производительности такое упрощение стоит работы двух разнородных парсеров. К слову, производительность системы можно увеличить, если добавить кеширование на разных этапах шаблонизации.
В эту прекрасную пятницу традиция «Забавы ради», которая продолжается уже несколько серий, обретает довольно интересные формы, не правда ли?