Практическое решение: CSS Tidy. Алгоритм разбора и сбора CSS Sprites. Уменьшение количества запросов
Прикладные тонкости
Практическое решение: CSS Tidy
Практическое решение: сборка изображений
Далее речь пойдет уже об инструменте Auto Sprites (http://sprites.in/), который был положен в основу разработки Web Optimizer (http://www.web-optimizer.ru/). После описанных выше умозаключений оставались чисто технические вопросы: как все это закодировать. И как отладить полученное решение.
Для начала нам нужно разобрать все дерево CSS-правил, потом отобрать из них относящиеся к фоновым изображениям и линейным размерам объектов, уже потом произвести над ними требуемые действия. Идеально для этой задачи подходит CSS Tidy (http://csstidy.sourceforge.net/), который был замечательно испробован, протестирован и улучшен после интеграции на для сотен реальных сайтов. CSS Tidy представляет собой набор PHP-библиотек, которые могут включены в произвольный проект для каких-либо действий над заданным набором стилевых правил.
Ниже приводится простой алгоритм выделения из массива CSS-правил нужных нам свойств фона на языке PHP:
/* $this – объект класса css_sprites */
/* сначала создадим новый объект CSS Tidy, используя заданный $css_code*/
$this->css = new csstidy();
$this->css->parse($css_code);
/* определим CSS-свойство для элементов без фона, */
/* чтобы не переопределить его в ходе преобразований */
$this->none = 'none!important';
/* далее мы переберем весь массив на предмет сначала @media-конструкций*/
foreach ($this->css->css as $import => $token) {
/* создам для каждого заданного @media свой массив правил */
$this->media[$import] = array();
/* а затем и самих CSS-селекторов */
foreach ($token as $tags => $rule) {
/* получим для каждого набора селекторов массив CSS-свойств и их значений */
foreach ($rule as $key => $value) {
/* выделим из всех свойств только относящиесяк фону */
if (preg_match("/background/", $key)) {
/* переопределим «выключенный» фон, чтобы не затронуть */
/* в ходе преобразований */
if ($key == 'background' && $value == 'none') {
$this->css->css[$import][$tags]['background'] = $this->none;
}
/* теперь для каждого отдельного CSS-селектора */
foreach (split(",", $tags) as $tag) {
/* создаем отдельный объект */
if (!empty($this->media[$import][$tag])) {
$this->media[$import][$tag] = array();
}
if ($key == 'background') {
/* получаем массив фоновых свойств из CSS-свойства background */
$background = $this->css->optimise->dissolve_short_bg($value);
foreach ($background as $bg => $property) {
/* пропускаем свойства, заданные по умолчанию */
if (
/* в частности, для background-position */
!($bg == 'background-position' &&
($property == '0 0 !important' ||
$property == 'top left !important' ||
$property == '0 0' ||
$property == 'top left')) &&
/* для background-origin */
!($bg == 'background-origin' &&
($property == 'padding !important' |
$property == 'padding')) &&
/* для background-color */
!($bg == 'background-color' &&
($property == 'transparent !important' ||
$property == 'transparent')) &&
/* для background-clip */
!($bg == 'background-clip' &&
($property == 'border !important' ||
$property == 'border')) &&
/* для background-attachement */
!($bg == 'background-attachment' &&
($property == 'scroll !important' ||
$property =='scroll')) &&
/* для background-size */
!($bg == 'background-size' &&
($property == 'auto !important' ||
$property == 'auto')) &&
/* и для background-repeat */
!($bg == 'background-repeat' &&
($property == 'repeat !important' ||
$property == 'repeat'))) {
/* Переопределяем background-image, если оно не задано */
if ($bg == 'background-image' &&
($property == 'none !important' ||
$property == 'none')) {
$property = $this->none;
}
/* и дополняем background-position, вместо left выставляем
left center, а вместо right – right center,
и ряд других исправлений */
if ($bg == 'background-position') {
$property =
$this->compute_background_position($property);
}
/* В конце выставляем полученные значения для массива правил,
определяющих исходные фоновые картинки */
$this->media[$import][$tag][$bg] = $property;
}
}
/* Если у нас задано детальное CSS-свойство, то просто его назначаем */
} else {
/* Дополняем background-position, вместо left выставляем left center,
а вместо right – right center,
также меняем местами bottom right и некоторые другие случаи */
if ($key == 'background-position') {
$value =
$this->compute_background_position($value);
}
/* и выставляем «исправленные» свойства для массива CSS-правил
пропуская свойства по умолчанию */
if ($key != 'background-position' || $value != '0 0') {
$this->media[$import][$tag][$key] = $value;
}
}
}
}
/* завершаем цикл перебора исходных CSS-правил */
}
}
}